/**
 * Deposit Reminder Service Tests
 *
 * Tests for automated deposit reminder functionality:
 * - Query logic for finding bookings due for reminders
 * - Email template generation
 * - Status updates
 */

import {
  createDepositReminderEmail,
  type DepositReminderEmailParams,
} from "../../lib/services/email/templates/deposit-reminder-templates";

// Mock dependencies
jest.mock("../../lib/db", () => ({
  prisma: {
    bookings: {
      findMany: jest.fn(),
      findUnique: jest.fn(),
      update: jest.fn(),
    },
  },
}));

jest.mock("../../lib/supabase", () => ({
  supabaseAdmin: null,
}));

jest.mock("../../lib/email", () => ({
  sendEmailImmediate: jest.fn().mockResolvedValue(true),
}));

jest.mock("../../lib/queue", () => ({
  enqueueJob: jest.fn().mockResolvedValue("job-id"),
}));

jest.mock("../../lib/logger", () => ({
  logger: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    debug: jest.fn(),
  },
}));

describe("Deposit Reminder Email Templates", () => {
  const baseParams: DepositReminderEmailParams = {
    customerName: "John Doe",
    appointmentDate: "Tuesday, February 6, 2026",
    appointmentTime: "10:00 AM",
    service: "Hair Cut",
    depositAmount: 50,
    currency: "QAR",
    paymentLink: "https://mawidi.com/en/bookings/123/pay",
    language: "en",
  };

  describe("createDepositReminderEmail - English", () => {
    it("should create email with correct subject", () => {
      const email = createDepositReminderEmail(baseParams);

      expect(email.subject).toContain("Deposit Due");
      expect(email.subject).toContain("Tomorrow");
      expect(email.subject).toContain("Hair Cut");
    });

    it("should include customer name in greeting", () => {
      const email = createDepositReminderEmail(baseParams);

      expect(email.html).toContain("Dear John Doe");
      expect(email.text).toContain("Dear John Doe");
    });

    it("should include appointment details", () => {
      const email = createDepositReminderEmail(baseParams);

      expect(email.html).toContain("Tuesday, February 6, 2026");
      expect(email.html).toContain("10:00 AM");
      expect(email.html).toContain("Hair Cut");
    });

    it("should include formatted deposit amount", () => {
      const email = createDepositReminderEmail(baseParams);

      // QAR 50.00 or similar currency formatting
      expect(email.html).toContain("50");
      expect(email.html).toContain("QAR");
    });

    it("should include payment link button", () => {
      const email = createDepositReminderEmail(baseParams);

      expect(email.html).toContain(
        'href="https://mawidi.com/en/bookings/123/pay"',
      );
      expect(email.html).toContain("Pay Now");
    });

    it("should include warning message", () => {
      const email = createDepositReminderEmail(baseParams);

      expect(email.html).toContain("Important");
      expect(email.text).toContain("Important");
    });

    it("should include plain text version", () => {
      const email = createDepositReminderEmail(baseParams);

      expect(email.text).toContain("Deposit Reminder");
      expect(email.text).toContain("John Doe");
      expect(email.text).toContain(
        "Pay Now: https://mawidi.com/en/bookings/123/pay",
      );
    });
  });

  describe("createDepositReminderEmail - Arabic", () => {
    const arabicParams: DepositReminderEmailParams = {
      ...baseParams,
      customerName: "أحمد محمد",
      service: "قص شعر",
      language: "ar",
    };

    it("should create email with Arabic subject", () => {
      const email = createDepositReminderEmail(arabicParams);

      expect(email.subject).toContain("تذكير");
      expect(email.subject).toContain("غداً");
    });

    it("should include Arabic greeting", () => {
      const email = createDepositReminderEmail(arabicParams);

      expect(email.html).toContain("عزيزي");
      expect(email.html).toContain("أحمد محمد");
    });

    it("should have RTL direction in HTML", () => {
      const email = createDepositReminderEmail(arabicParams);

      expect(email.html).toContain('dir="rtl"');
      expect(email.html).toContain('lang="ar"');
    });

    it("should include Arabic CTA button text", () => {
      const email = createDepositReminderEmail(arabicParams);

      expect(email.html).toContain("ادفع الآن");
    });

    it("should include Arabic warning message", () => {
      const email = createDepositReminderEmail(arabicParams);

      expect(email.html).toContain("مهم");
    });
  });

  describe("Currency formatting", () => {
    it("should format GBP currency", () => {
      const params: DepositReminderEmailParams = {
        ...baseParams,
        depositAmount: 25.5,
        currency: "GBP",
      };
      const email = createDepositReminderEmail(params);

      expect(email.html).toContain("£");
      expect(email.html).toContain("25");
    });

    it("should format USD currency", () => {
      const params: DepositReminderEmailParams = {
        ...baseParams,
        depositAmount: 100,
        currency: "USD",
      };
      const email = createDepositReminderEmail(params);

      expect(email.html).toContain("$");
      expect(email.html).toContain("100");
    });

    it("should format SAR currency", () => {
      const params: DepositReminderEmailParams = {
        ...baseParams,
        depositAmount: 75,
        currency: "SAR",
      };
      const email = createDepositReminderEmail(params);

      // SAR formatting varies by locale
      expect(email.html).toContain("75");
    });
  });
});

describe("Deposit Reminder Service", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("getDueDepositReminders", () => {
    it("should query bookings within 24-25 hour window", async () => {
      const { prisma } = await import("../../lib/db");
      const mockFindMany = prisma.bookings.findMany as ReturnType<
        typeof jest.fn
      >;

      mockFindMany.mockResolvedValue([]);

      const { getDueDepositReminders } =
        await import("../../lib/services/deposit-reminder.service");

      await getDueDepositReminders();

      expect(mockFindMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            status: { in: ["pending", "confirmed"] },
            depositPaid: false,
            depositReminderSentAt: null,
          }),
        }),
      );
    });
  });

  describe("sendDepositReminderForBooking", () => {
    it("should return error if booking not found", async () => {
      const { prisma } = await import("../../lib/db");
      const mockFindUnique = prisma.bookings.findUnique as ReturnType<
        typeof jest.fn
      >;

      mockFindUnique.mockResolvedValue(null);

      const { sendDepositReminderForBooking } =
        await import("../../lib/services/deposit-reminder.service");

      const result = await sendDepositReminderForBooking("non-existent-id");

      expect(result.emailSent).toBe(false);
      expect(result.error).toBe("Booking not found");
    });

    it("should skip if deposit already paid", async () => {
      const { prisma } = await import("../../lib/db");
      const mockFindUnique = prisma.bookings.findUnique as ReturnType<
        typeof jest.fn
      >;

      mockFindUnique.mockResolvedValue({
        id: "booking-123",
        customerEmail: "test@example.com",
        depositPaid: true,
        depositAmount: 50,
        status: "confirmed",
      });

      const { sendDepositReminderForBooking } =
        await import("../../lib/services/deposit-reminder.service");

      const result = await sendDepositReminderForBooking("booking-123");

      expect(result.emailSent).toBe(false);
      expect(result.error).toBe("Deposit already paid");
    });

    it("should skip if no customer email", async () => {
      const { prisma } = await import("../../lib/db");
      const mockFindUnique = prisma.bookings.findUnique as ReturnType<
        typeof jest.fn
      >;

      mockFindUnique.mockResolvedValue({
        id: "booking-123",
        customerEmail: null,
        depositPaid: false,
        depositAmount: 50,
        status: "confirmed",
      });

      const { sendDepositReminderForBooking } =
        await import("../../lib/services/deposit-reminder.service");

      const result = await sendDepositReminderForBooking("booking-123");

      expect(result.emailSent).toBe(false);
      expect(result.error).toBe("No customer email available");
    });

    it("should skip if no deposit amount configured", async () => {
      const { prisma } = await import("../../lib/db");
      const mockFindUnique = prisma.bookings.findUnique as ReturnType<
        typeof jest.fn
      >;

      mockFindUnique.mockResolvedValue({
        id: "booking-123",
        customerEmail: "test@example.com",
        depositPaid: false,
        depositAmount: 0,
        status: "confirmed",
      });

      const { sendDepositReminderForBooking } =
        await import("../../lib/services/deposit-reminder.service");

      const result = await sendDepositReminderForBooking("booking-123");

      expect(result.emailSent).toBe(false);
      expect(result.error).toBe("No deposit amount configured");
    });
  });

  describe("processDepositReminders", () => {
    it("should enqueue jobs for each due booking", async () => {
      const { prisma } = await import("../../lib/db");
      const { enqueueJob } = await import("../../lib/queue");
      const mockFindMany = prisma.bookings.findMany as ReturnType<
        typeof jest.fn
      >;
      const mockEnqueueJob = enqueueJob as ReturnType<typeof jest.fn>;

      mockFindMany.mockResolvedValue([
        {
          id: "booking-1",
          customerEmail: "test1@example.com",
          depositAmount: 50,
          status: "pending",
        },
        {
          id: "booking-2",
          customerEmail: "test2@example.com",
          depositAmount: 100,
          status: "confirmed",
        },
      ]);

      const { processDepositReminders } =
        await import("../../lib/services/deposit-reminder.service");

      const result = await processDepositReminders();

      expect(result.processed).toBe(2);
      expect(result.enqueued).toBe(2);
      expect(mockEnqueueJob).toHaveBeenCalledTimes(2);
      expect(mockEnqueueJob).toHaveBeenCalledWith("deposit_reminder", {
        bookingId: "booking-1",
      });
      expect(mockEnqueueJob).toHaveBeenCalledWith("deposit_reminder", {
        bookingId: "booking-2",
      });
    });

    it("should skip cancelled bookings", async () => {
      const { prisma } = await import("../../lib/db");
      const { enqueueJob } = await import("../../lib/queue");
      const mockFindMany = prisma.bookings.findMany as ReturnType<
        typeof jest.fn
      >;
      const mockEnqueueJob = enqueueJob as ReturnType<typeof jest.fn>;

      mockFindMany.mockResolvedValue([
        {
          id: "booking-1",
          customerEmail: "test1@example.com",
          depositAmount: 50,
          status: "cancelled",
        },
      ]);

      const { processDepositReminders } =
        await import("../../lib/services/deposit-reminder.service");

      const result = await processDepositReminders();

      expect(result.processed).toBe(1);
      expect(result.skipped).toBe(1);
      expect(result.enqueued).toBe(0);
      expect(mockEnqueueJob).not.toHaveBeenCalled();
    });

    it("should skip bookings without email", async () => {
      const { prisma } = await import("../../lib/db");
      const { enqueueJob } = await import("../../lib/queue");
      const mockFindMany = prisma.bookings.findMany as ReturnType<
        typeof jest.fn
      >;
      const mockEnqueueJob = enqueueJob as ReturnType<typeof jest.fn>;

      mockFindMany.mockResolvedValue([
        {
          id: "booking-1",
          customerEmail: null,
          depositAmount: 50,
          status: "pending",
        },
      ]);

      const { processDepositReminders } =
        await import("../../lib/services/deposit-reminder.service");

      const result = await processDepositReminders();

      expect(result.skipped).toBe(1);
      expect(mockEnqueueJob).not.toHaveBeenCalled();
    });
  });
});
