import { Test } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { BadRequestException, NotFoundException } from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
import { ActivityTicketMailService } from './activity-ticket-mail.service';
import { BookingActivityItemEntity } from '../../../entities/booking-activity-item.entity';
import { ActivityTicketItemEntity } from '../../../entities/activity-ticket-item.entity';
import { MailerService } from '../../mailer/mailer.service';

describe('ActivityTicketMailService', () => {
  let service: ActivityTicketMailService;
  const managerQuery = jest.fn();
  const activityRepo = { findOne: jest.fn(), manager: { query: managerQuery } };
  const ticketRepo = { findOne: jest.fn() };
  const mailer = { send: jest.fn() };
  const cls = { get: jest.fn((k: string) => ({ tenant_id: 't1', user_id: 'u1' })[k]) };

  const item = {
    id: 'i1',
    date: '2026-10-20',
    ticket_file_url: null,
    booking: { booking_number: 'BK-1', query: { guest_name: '<b>Ann</b>', guest_mobile: '+971' } },
    activity: { name: 'Burj Khalifa' },
    ticket: { name: 'Adult' },
  };

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        ActivityTicketMailService,
        { provide: getRepositoryToken(BookingActivityItemEntity), useValue: activityRepo },
        { provide: getRepositoryToken(ActivityTicketItemEntity), useValue: ticketRepo },
        { provide: MailerService, useValue: mailer },
        { provide: ClsService, useValue: cls },
      ],
    }).compile();
    service = module.get(ActivityTicketMailService);
    managerQuery.mockImplementation(async (sql: string) =>
      sql.includes('FROM users')
        ? [{ email: 'ops@example.com', name: 'Ops' }]
        : [{ company_name: 'Mellow Tours' }],
    );
    mailer.send.mockResolvedValue({ messageId: 'm1' });
  });

  afterEach(() => jest.clearAllMocks());

  it('emails the logged-in user an absolute ticket link, escaping guest text', async () => {
    activityRepo.findOne.mockResolvedValue(item);
    ticketRepo.findOne.mockResolvedValue({
      ticket_ref_no: 'TK-9',
      document_url: '/api/uploads/acme/t.pdf',
    });
    const res = await service.emailTicket('i1', 'https://dmc.workzy.co/');
    expect(res).toEqual({ sent_to: 'ops@example.com' });
    const mail = mailer.send.mock.calls[0][0];
    expect(mail.to).toEqual({ email: 'ops@example.com', name: 'Ops' });
    expect(mail.fromName).toBe('Mellow Tours');
    expect(mail.html).toContain('https://dmc.workzy.co/api/uploads/acme/t.pdf');
    expect(mail.html).toContain('&lt;b&gt;Ann&lt;/b&gt;');
    expect(mail.html).not.toContain('<b>Ann</b>');
  });

  it('scopes the activity lookup to the caller tenant (tenant isolation)', async () => {
    activityRepo.findOne.mockResolvedValue(null);
    await expect(service.emailTicket('foreign', 'https://x')).rejects.toThrow(NotFoundException);
    expect(activityRepo.findOne).toHaveBeenCalledWith(
      expect.objectContaining({ where: expect.objectContaining({ tenant_id: 't1' }) }),
    );
    expect(mailer.send).not.toHaveBeenCalled();
  });

  it('refuses when the activity has no ticket file', async () => {
    activityRepo.findOne.mockResolvedValue(item);
    ticketRepo.findOne.mockResolvedValue(null);
    await expect(service.emailTicket('i1', 'https://x')).rejects.toThrow(BadRequestException);
    expect(mailer.send).not.toHaveBeenCalled();
  });
});
