import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { EmailService, EmailOptions, PasswordResetEmailData, EmailVerificationData } from './email.service';

describe('EmailService', () => {
  let service: EmailService;
  let configService: ConfigService;
  let mockMailgunClient: any;

  const mockConfigService = {
    get: jest.fn((key: string, defaultValue?: any) => {
      const config: Record<string, any> = {
        MAILGUN_API_KEY: 'test-api-key',
        MAILGUN_DOMAIN: 'test.mailgun.org',
        MAILGUN_REGION: 'us',
        EMAIL_FROM: 'noreply@test.com',
        EMAIL_FROM_NAME: 'Test App',
        APP_URL: 'http://localhost:3000',
      };
      return config[key] ?? defaultValue;
    }),
  };

  beforeEach(async () => {
    // Reset mocks
    jest.clearAllMocks();

    // Mock Mailgun client
    mockMailgunClient = {
      messages: {
        create: jest.fn().mockResolvedValue({ id: 'test-message-id' }),
      },
    };

    // Mock Mailgun module
    jest.mock('mailgun.js', () => {
      return jest.fn().mockImplementation(() => ({
        client: jest.fn().mockReturnValue(mockMailgunClient),
      }));
    });

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        EmailService,
        {
          provide: ConfigService,
          useValue: mockConfigService,
        },
      ],
    }).compile();

    service = module.get<EmailService>(EmailService);
    configService = module.get<ConfigService>(ConfigService);

    // Inject mock mailgun client
    (service as any).mailgunClient = mockMailgunClient;
    (service as any).mailgunDomain = 'test.mailgun.org';
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('sendEmail', () => {
    it('should send email successfully with Mailgun', async () => {
      const emailOptions: EmailOptions = {
        to: 'user@example.com',
        subject: 'Test Email',
        html: '<h1>Test</h1><p>This is a test email</p>',
        text: 'Test - This is a test email',
      };

      await service.sendEmail(emailOptions);

      expect(mockMailgunClient.messages.create).toHaveBeenCalledWith(
        'test.mailgun.org',
        expect.objectContaining({
          from: 'Test App <noreply@test.com>',
          to: 'user@example.com',
          subject: 'Test Email',
          html: '<h1>Test</h1><p>This is a test email</p>',
          text: 'Test - This is a test email',
        }),
      );
    });

    it('should strip HTML for text version if not provided', async () => {
      const emailOptions: EmailOptions = {
        to: 'user@example.com',
        subject: 'Test Email',
        html: '<h1>Hello</h1><p>World</p>',
      };

      await service.sendEmail(emailOptions);

      expect(mockMailgunClient.messages.create).toHaveBeenCalledWith(
        'test.mailgun.org',
        expect.objectContaining({
          text: expect.stringContaining('Hello'),
        }),
      );
    });

    it('should log warning if Mailgun is not configured', async () => {
      (service as any).mailgunClient = null;
      const loggerWarnSpy = jest.spyOn((service as any).logger, 'warn');

      const emailOptions: EmailOptions = {
        to: 'user@example.com',
        subject: 'Test Email',
        html: '<h1>Test</h1>',
      };

      await service.sendEmail(emailOptions);

      expect(loggerWarnSpy).toHaveBeenCalledWith(
        expect.stringContaining('EMAIL NOT SENT'),
      );
      expect(mockMailgunClient.messages.create).not.toHaveBeenCalled();
    });

    it('should throw error if Mailgun fails', async () => {
      mockMailgunClient.messages.create.mockRejectedValue(
        new Error('Mailgun API error'),
      );

      const emailOptions: EmailOptions = {
        to: 'user@example.com',
        subject: 'Test Email',
        html: '<h1>Test</h1>',
      };

      await expect(service.sendEmail(emailOptions)).rejects.toThrow(
        'Mailgun API error',
      );
    });
  });

  describe('sendPasswordResetEmail', () => {
    it('should send password reset email with correct template', async () => {
      const to = 'user@example.com';
      const data: PasswordResetEmailData = {
        userName: 'John Doe',
        resetLink: 'http://localhost:3000/reset-password?token=abc123',
        expiresIn: '1 hour',
      };

      await service.sendPasswordResetEmail(to, data);

      expect(mockMailgunClient.messages.create).toHaveBeenCalledWith(
        'test.mailgun.org',
        expect.objectContaining({
          to: 'user@example.com',
          subject: 'Reset Your Maternal App Password',
          html: expect.stringContaining('John Doe'),
        }),
      );

      const call = mockMailgunClient.messages.create.mock.calls[0][1];
      expect(call.html).toContain(data.resetLink);
      expect(call.html).toContain(data.expiresIn);
      expect(call.html).toContain('Reset Your Password');
    });

    it('should include security warning in password reset email', async () => {
      const data: PasswordResetEmailData = {
        userName: 'Jane',
        resetLink: 'http://test.com/reset',
        expiresIn: '30 minutes',
      };

      await service.sendPasswordResetEmail('jane@test.com', data);

      const call = mockMailgunClient.messages.create.mock.calls[0][1];
      expect(call.html).toContain("didn't request");
      expect(call.html).toContain('ignore this email');
    });
  });

  describe('sendEmailVerificationEmail', () => {
    it('should send email verification with correct template', async () => {
      const to = 'newuser@example.com';
      const data: EmailVerificationData = {
        userName: 'New User',
        verificationLink: 'http://localhost:3000/verify-email?token=xyz789',
      };

      await service.sendEmailVerificationEmail(to, data);

      expect(mockMailgunClient.messages.create).toHaveBeenCalledWith(
        'test.mailgun.org',
        expect.objectContaining({
          to: 'newuser@example.com',
          subject: 'Verify Your Maternal App Email',
          html: expect.stringContaining('New User'),
        }),
      );

      const call = mockMailgunClient.messages.create.mock.calls[0][1];
      expect(call.html).toContain(data.verificationLink);
      expect(call.html).toContain('Verify Your Email');
      expect(call.html).toContain('Why verify?');
    });

    it('should include benefits explanation in verification email', async () => {
      const data: EmailVerificationData = {
        userName: 'Test User',
        verificationLink: 'http://test.com/verify',
      };

      await service.sendEmailVerificationEmail('test@test.com', data);

      const call = mockMailgunClient.messages.create.mock.calls[0][1];
      expect(call.html).toContain('account security');
      expect(call.html).toContain('important updates');
    });
  });

  describe('sendWelcomeEmail', () => {
    it('should send welcome email with correct template', async () => {
      const to = 'parent@example.com';
      const userName = 'Happy Parent';

      await service.sendWelcomeEmail(to, userName);

      expect(mockMailgunClient.messages.create).toHaveBeenCalledWith(
        'test.mailgun.org',
        expect.objectContaining({
          to: 'parent@example.com',
          subject: 'Welcome to Maternal App! 🎉',
          html: expect.stringContaining('Happy Parent'),
        }),
      );

      const call = mockMailgunClient.messages.create.mock.calls[0][1];
      expect(call.html).toContain('Welcome to Maternal App!');
      expect(call.html).toContain('Track Activities');
      expect(call.html).toContain('AI Parenting Assistant');
      expect(call.html).toContain('Family Sync');
    });

    it('should include app URL in welcome email', async () => {
      await service.sendWelcomeEmail('user@test.com', 'User');

      const call = mockMailgunClient.messages.create.mock.calls[0][1];
      expect(call.html).toContain('http://localhost:3000');
      expect(call.html).toContain('Get Started');
    });

    it('should include support contact in welcome email', async () => {
      await service.sendWelcomeEmail('user@test.com', 'User');

      const call = mockMailgunClient.messages.create.mock.calls[0][1];
      expect(call.html).toContain('support@maternal-app.com');
    });
  });

  describe('stripHtml', () => {
    it('should remove HTML tags from content', () => {
      const html = '<h1>Hello</h1><p>World</p><strong>Bold</strong>';
      const result = (service as any).stripHtml(html);

      expect(result).not.toContain('<');
      expect(result).not.toContain('>');
      expect(result).toContain('Hello');
      expect(result).toContain('World');
      expect(result).toContain('Bold');
    });

    it('should remove style tags and content', () => {
      const html = '<style>body { color: red; }</style><p>Content</p>';
      const result = (service as any).stripHtml(html);

      expect(result).not.toContain('style');
      expect(result).not.toContain('color: red');
      expect(result).toContain('Content');
    });

    it('should normalize whitespace', () => {
      const html = '<p>Hello\n\n\n   World</p>';
      const result = (service as any).stripHtml(html);

      expect(result).toBe('Hello World');
    });
  });

  describe('template generation', () => {
    it('should generate password reset template with all required elements', () => {
      const data: PasswordResetEmailData = {
        userName: 'Test',
        resetLink: 'http://test.com/reset',
        expiresIn: '1 hour',
      };

      const template = (service as any).getPasswordResetEmailTemplate(data);

      expect(template).toContain('<!DOCTYPE html>');
      expect(template).toContain('Reset Your Password');
      expect(template).toContain(data.userName);
      expect(template).toContain(data.resetLink);
      expect(template).toContain(data.expiresIn);
      expect(template).toContain('gradient');
    });

    it('should generate email verification template with all required elements', () => {
      const data: EmailVerificationData = {
        userName: 'Test',
        verificationLink: 'http://test.com/verify',
      };

      const template = (service as any).getEmailVerificationTemplate(data);

      expect(template).toContain('<!DOCTYPE html>');
      expect(template).toContain('Verify Your Email');
      expect(template).toContain(data.userName);
      expect(template).toContain(data.verificationLink);
      expect(template).toContain('📧');
    });

    it('should generate welcome template with features', () => {
      const userName = 'Test User';
      const template = (service as any).getWelcomeEmailTemplate(userName);

      expect(template).toContain('<!DOCTYPE html>');
      expect(template).toContain('Welcome to Maternal App');
      expect(template).toContain(userName);
      expect(template).toContain('Track Activities');
      expect(template).toContain('AI Parenting Assistant');
      expect(template).toContain('Family Sync');
      expect(template).toContain('🎉');
    });
  });

  describe('configuration', () => {
    it('should initialize with US region by default', () => {
      expect(configService.get).toHaveBeenCalledWith('MAILGUN_REGION', 'us');
    });

    it('should use default values for missing config', () => {
      const mockConfig = {
        get: jest.fn((key: string, defaultValue?: any) => defaultValue),
      };

      const testModule = Test.createTestingModule({
        providers: [
          EmailService,
          { provide: ConfigService, useValue: mockConfig },
        ],
      });

      expect(mockConfig.get).toBeDefined();
    });

    it('should handle EU region configuration', () => {
      const euConfig = {
        ...mockConfigService,
        get: jest.fn((key: string, defaultValue?: any) => {
          if (key === 'MAILGUN_REGION') return 'eu';
          return mockConfigService.get(key, defaultValue);
        }),
      };

      // This would test EU URL configuration
      expect(euConfig.get('MAILGUN_REGION', 'us')).toBe('eu');
    });
  });
});
