import * as nodemailer from 'nodemailer';
import { EmailService } from '../services/email.service';

vi.mock('nodemailer');

describe('EmailService', () => {
  let service: EmailService;
  let mockTransporter: { sendMail: ReturnType<typeof vi.fn>; verify: ReturnType<typeof vi.fn> };
  let mockLogger: { log: ReturnType<typeof vi.fn>; error: ReturnType<typeof vi.fn>; warn: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockTransporter = {
      sendMail: vi.fn(),
      verify: vi.fn(),
    };

    vi.mocked(nodemailer.createTransport).mockReturnValue(mockTransporter as any);

    mockLogger = { log: vi.fn(), error: vi.fn(), warn: vi.fn() };

    service = new EmailService(mockLogger as any);
    service.onModuleInit();
  });

  it('send sends email and returns messageId', async () => {
    mockTransporter.sendMail.mockResolvedValue({ messageId: 'test-message-id' });

    const result = await service.send({
      to: 'test@example.com',
      subject: 'Test Subject',
      html: '<p>Test body</p>',
    });

    expect(mockTransporter.sendMail).toHaveBeenCalledWith(
      expect.objectContaining({
        to: 'test@example.com',
        subject: 'Test Subject',
        html: '<p>Test body</p>',
      }),
    );
    expect(result).toEqual({ messageId: 'test-message-id' });
  });

  it('send throws error when sendMail fails', async () => {
    const smtpError = new Error('SMTP connection refused');
    mockTransporter.sendMail.mockRejectedValue(smtpError);

    await expect(
      service.send({ to: 'test@example.com', subject: 'Test', html: '<p>Test</p>' }),
    ).rejects.toThrow('SMTP connection refused');

    expect(mockLogger.error).toHaveBeenCalled();
  });

  it('verify returns true when transporter verifies successfully', async () => {
    mockTransporter.verify.mockResolvedValue(true);

    const result = await service.verify();

    expect(result).toBe(true);
    expect(mockTransporter.verify).toHaveBeenCalled();
  });

  it('verify returns false when transporter verify fails', async () => {
    mockTransporter.verify.mockRejectedValue(new Error('Connection failed'));

    const result = await service.verify();

    expect(result).toBe(false);
  });

  it('onModuleInit creates transporter with env config', () => {
    process.env['SMTP_HOST'] = 'smtp.example.com';
    process.env['SMTP_PORT'] = '587';
    process.env['SMTP_USER'] = 'user@example.com';
    process.env['SMTP_PASS'] = 'secret';

    const freshService = new EmailService(mockLogger as any);
    freshService.onModuleInit();

    expect(nodemailer.createTransport).toHaveBeenCalledWith(
      expect.objectContaining({
        host: 'smtp.example.com',
        port: 587,
        auth: { user: 'user@example.com', pass: 'secret' },
      }),
    );

    delete process.env['SMTP_HOST'];
    delete process.env['SMTP_PORT'];
    delete process.env['SMTP_USER'];
    delete process.env['SMTP_PASS'];
  });
});
