/**
 * Unit tests for lib/email.ts
 * Tests email sending, template generation, bilingual support
 */

// Mock nodemailer — factory must not reference outer `const` (hoisting TDZ)
const mockSendMail = jest.fn();
const mockVerify = jest.fn();

jest.mock("nodemailer", () => {
  // Re-import the outer variables via closure — safe because `var`-style
  // hoisting doesn't apply, but jest.mock factories run *after* const inits
  // when the const is declared ABOVE the jest.mock call in source order.
  // However, jest hoists the mock call above the const at transpile time.
  // Fix: build the transport lazily so the const is initialised by call-time.
  return {
    createTransport: jest.fn(() => ({
      sendMail: (...args: unknown[]) => mockSendMail(...args),
      verify: (...args: unknown[]) => mockVerify(...args),
    })),
  };
});

import {
  sendEmail,
  createOTPEmail,
  createWelcomeEmail,
  createPasswordResetEmail,
  sendOTPEmail,
  sendWelcomeEmail,
  sendPasswordResetEmail,
} from "@/lib/email";

describe("lib/email.ts", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    mockSendMail.mockResolvedValue({ messageId: "test-message-id" });
  });

  describe("sendEmail", () => {
    it("should send email successfully", async () => {
      const template = {
        subject: "Test Subject",
        html: "<p>Test HTML</p>",
        text: "Test Text",
      };

      const result = await sendEmail("test@example.com", template);

      expect(result).toBe(true);
      expect(mockSendMail).toHaveBeenCalledWith({
        from: expect.any(String),
        to: "test@example.com",
        subject: "Test Subject",
        html: "<p>Test HTML</p>",
        text: "Test Text",
      });
    });

    it("should return false on send failure", async () => {
      mockSendMail.mockRejectedValue(new Error("SMTP error"));

      const template = {
        subject: "Test",
        html: "<p>Test</p>",
        text: "Test",
      };

      const result = await sendEmail("test@example.com", template);

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

    it("should use FROM_EMAIL from environment", async () => {
      const template = {
        subject: "Test",
        html: "<p>Test</p>",
        text: "Test",
      };

      await sendEmail("test@example.com", template);

      expect(mockSendMail).toHaveBeenCalledWith(
        expect.objectContaining({
          from: expect.any(String),
        }),
      );
    });
  });

  describe("createOTPEmail", () => {
    it("should create English OTP email template", () => {
      const template = createOTPEmail("123456", "en");

      expect(template.subject).toBeDefined();
      expect(template.html).toContain("123456");
      expect(template.text).toContain("123456");
    });

    it("should create Arabic OTP email template", () => {
      const template = createOTPEmail("123456", "ar");

      expect(template.subject).toBeDefined();
      expect(template.html).toContain("123456");
      expect(template.text).toContain("123456");
    });

    it("should include OTP code in both HTML and text", () => {
      const template = createOTPEmail("654321", "en");

      expect(template.html).toContain("654321");
      expect(template.text).toContain("654321");
    });

    it("should include expiry warning in both languages", () => {
      const enTemplate = createOTPEmail("123456", "en");
      const arTemplate = createOTPEmail("123456", "ar");

      expect(enTemplate.html).toContain("10 minutes");
      expect(arTemplate.html).toContain("10 دقائق");
    });

    it("should include security warning", () => {
      const template = createOTPEmail("123456", "en");

      expect(template.html.toLowerCase()).toContain("ignore");
      expect(template.text.toLowerCase()).toContain("ignore");
    });

    it("should have responsive design", () => {
      const template = createOTPEmail("123456", "en");

      expect(template.html).toContain("viewport");
      expect(template.html).toContain("max-width");
    });

    it("should include copyright year dynamically", () => {
      const currentYear = new Date().getFullYear();
      const template = createOTPEmail("123456", "en");

      expect(template.html).toContain(currentYear.toString());
      expect(template.text).toContain(currentYear.toString());
    });
  });

  describe("createWelcomeEmail", () => {
    it("should create English welcome email", () => {
      const template = createWelcomeEmail("John Doe", "en");

      expect(template.subject).toContain("Welcome");
      expect(template.html).toContain("John Doe");
      expect(template.html).toContain('lang="en"');
    });

    it("should create Arabic welcome email", () => {
      const template = createWelcomeEmail("محمد", "ar");

      expect(template.subject).toBeDefined();
      expect(template.html).toContain("محمد");
    });

    it("should include user name in template", () => {
      const template = createWelcomeEmail("Test User", "en");

      expect(template.html).toContain("Test User");
      expect(template.text).toContain("Test User");
    });

    it("should have both HTML and text versions", () => {
      const template = createWelcomeEmail("Test", "en");

      expect(template.html).toBeTruthy();
      expect(template.text).toBeTruthy();
      expect(template.subject).toBeTruthy();
    });
  });

  describe("createPasswordResetEmail", () => {
    it("should create English password reset email", () => {
      const template = createPasswordResetEmail("123456", "en");

      expect(template.subject).toContain("Password") ||
        expect(template.subject).toContain("Reset");
      expect(template.html).toContain("123456");
      expect(template.html).toContain('lang="en"');
    });

    it("should create Arabic password reset email", () => {
      const template = createPasswordResetEmail("123456", "ar");

      expect(template.subject).toBeDefined();
      expect(template.html).toContain("123456");
    });

    it("should include reset code in both HTML and text", () => {
      const template = createPasswordResetEmail("999888", "en");

      expect(template.html).toContain("999888");
      expect(template.text).toContain("999888");
    });

    it("should include security warnings", () => {
      const template = createPasswordResetEmail("123456", "en");

      expect(template.html.toLowerCase()).toContain("ignore") ||
        expect(template.text.toLowerCase()).toContain("ignore");
    });
  });

  describe("sendOTPEmail", () => {
    it("should send OTP email in English", async () => {
      const result = await sendOTPEmail("test@example.com", "123456", "en");

      expect(result).toBe(true);
      expect(mockSendMail).toHaveBeenCalledWith(
        expect.objectContaining({
          to: "test@example.com",
          subject: expect.any(String),
          html: expect.stringContaining("123456"),
        }),
      );
    });

    it("should send OTP email in Arabic", async () => {
      const result = await sendOTPEmail("test@example.com", "123456", "ar");

      expect(result).toBe(true);
      expect(mockSendMail).toHaveBeenCalledWith(
        expect.objectContaining({
          to: "test@example.com",
          subject: expect.stringContaining("رمز"),
        }),
      );
    });

    it("should return false on send failure", async () => {
      mockSendMail.mockRejectedValue(new Error("SMTP error"));

      const result = await sendOTPEmail("test@example.com", "123456", "en");

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

  describe("sendWelcomeEmail", () => {
    it("should send welcome email in English", async () => {
      const result = await sendWelcomeEmail(
        "test@example.com",
        "John Doe",
        "en",
      );

      expect(result).toBe(true);
      expect(mockSendMail).toHaveBeenCalledWith(
        expect.objectContaining({
          to: "test@example.com",
          subject: expect.stringContaining("Welcome"),
          html: expect.stringContaining("John Doe"),
        }),
      );
    });

    it("should send welcome email in Arabic", async () => {
      const result = await sendWelcomeEmail("test@example.com", "محمد", "ar");

      expect(result).toBe(true);
      expect(mockSendMail).toHaveBeenCalledWith(
        expect.objectContaining({
          to: "test@example.com",
          html: expect.stringContaining("محمد"),
        }),
      );
    });
  });

  describe("sendPasswordResetEmail", () => {
    it("should send password reset email in English", async () => {
      const result = await sendPasswordResetEmail(
        "test@example.com",
        "123456",
        "en",
      );

      expect(result).toBe(true);
      expect(mockSendMail).toHaveBeenCalledWith(
        expect.objectContaining({
          to: "test@example.com",
          html: expect.stringContaining("123456"),
        }),
      );
    });

    it("should send password reset email in Arabic", async () => {
      const result = await sendPasswordResetEmail(
        "test@example.com",
        "123456",
        "ar",
      );

      expect(result).toBe(true);
      expect(mockSendMail).toHaveBeenCalled();
    });
  });

  describe("Template Quality", () => {
    it("should include valid HTML structure in OTP email", () => {
      const template = createOTPEmail("123456", "en");

      expect(template.html).toContain("<!DOCTYPE html>");
      expect(template.html).toContain("<html");
      expect(template.html).toContain("</html>");
    });

    it("should include brand styling", () => {
      const template = createOTPEmail("123456", "en");

      expect(template.html).toContain("Mawidi");
      expect(template.html).toContain("#10b981"); // Brand green color
    });

    it("should have mobile-friendly design", () => {
      const template = createOTPEmail("123456", "en");

      expect(template.html).toContain("viewport");
      expect(template.html).toContain("max-width");
    });

    it("should have RTL support for Arabic", () => {
      const template = createOTPEmail("123456", "ar");

      expect(template.html).toContain('dir="rtl"');
    });

    it("should have fallback text version", () => {
      const template = createOTPEmail("123456", "en");

      expect(template.text).toBeTruthy();
      expect(template.text.length).toBeGreaterThan(20);
    });
  });

  describe("Error Handling", () => {
    it("should handle transporter errors gracefully", async () => {
      mockSendMail.mockRejectedValue(new Error("Connection refused"));

      const result = await sendEmail("test@example.com", {
        subject: "Test",
        html: "<p>Test</p>",
        text: "Test",
      });

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

    it("should handle missing email address", async () => {
      const result = await sendEmail("", {
        subject: "Test",
        html: "<p>Test</p>",
        text: "Test",
      });

      // Should attempt to send (validation handled by nodemailer)
      expect(mockSendMail).toHaveBeenCalled();
    });
  });

  describe("Special Characters & Edge Cases", () => {
    it("should handle special characters in OTP", () => {
      const template = createOTPEmail("abc123", "en");

      expect(template.html).toContain("abc123");
    });

    it("should handle special characters in name", () => {
      const template = createWelcomeEmail("O'Brien-Smith", "en");

      expect(template.html).toContain("O'Brien-Smith");
    });

    it("should handle Arabic text in emails", () => {
      const template = createWelcomeEmail("محمد علي", "ar");

      expect(template.html).toContain("محمد علي");
      expect(template.text).toContain("محمد علي");
    });

    it("should handle very long names", () => {
      const longName = "A".repeat(100);
      const template = createWelcomeEmail(longName, "en");

      expect(template.html).toContain(longName);
    });
  });

  describe("SMTP Configuration", () => {
    it("should default to localhost for development", () => {
      // Configuration is loaded at module load
      // Just verify transporter was created
      expect(mockSendMail).toBeDefined();
    });
  });
});
