/**
 * @jest-environment node
 */

/**
 * Tests for AppointmentTypeService formatters
 *
 * Covers:
 * - getFormattedCategoriesList (language-aware, emoji numbering)
 * - getFormattedServicesInCategory (language-aware, price/duration)
 * - getFormattedServicesList (bilingual, for AI prompts)
 * - getTypeNames (simple name array)
 * - findTypeByName (case-insensitive EN + AR)
 * - getTemplateCSV
 */

import { AppointmentTypeService } from "@/lib/services/appointment-type.service";

// ── Mocks ─────────────────────────────────────────────────
jest.mock("@/lib/db");

jest.mock("@/lib/services/qatari-dialect.service", () => ({
  qatariDialectService: {
    translateToQatari: jest.fn().mockResolvedValue({ translation: "مترجم" }),
  },
}));

jest.mock("@/lib/utils/organization-data", () => ({
  getEffectiveDataOwnerId: jest
    .fn()
    .mockResolvedValue({ organizationId: "org-1" }),
}));

import { prisma } from "@/lib/db";

// ── Test data ─────────────────────────────────────────────

const mockCategories = [
  {
    category: "General Medicine",
    categoryAr: "الطب العام",
    _count: { id: 12 },
  },
  { category: "Dental", categoryAr: "طب الأسنان", _count: { id: 8 } },
  { category: "Dermatology", categoryAr: "الأمراض الجلدية", _count: { id: 5 } },
];

const mockServicesInCategory = [
  {
    id: "1",
    nameEn: "General Checkup",
    nameAr: "فحص عام",
    duration: 30,
    price: 150,
    isActive: true,
    sortOrder: 0,
  },
  {
    id: "2",
    nameEn: "Blood Test",
    nameAr: "تحليل دم",
    duration: 15,
    price: 80,
    isActive: true,
    sortOrder: 1,
  },
  {
    id: "3",
    nameEn: "Vaccination",
    nameAr: "تطعيم",
    duration: 15,
    price: null,
    isActive: true,
    sortOrder: 2,
  },
];

// ── Tests ─────────────────────────────────────────────────

describe("AppointmentTypeService formatters", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  // ── getFormattedCategoriesList ──────────────────────────
  describe("getFormattedCategoriesList", () => {
    beforeEach(() => {
      (prisma.appointment_types.groupBy as jest.Mock).mockResolvedValue(
        mockCategories,
      );
    });

    it("returns English category names by default", async () => {
      const result =
        await AppointmentTypeService.getFormattedCategoriesList("org-1");

      expect(result).toContain("1️⃣ General Medicine");
      expect(result).toContain("2️⃣ Dental");
      expect(result).toContain("3️⃣ Dermatology");
      // Should NOT contain Arabic names in English mode
      expect(result).not.toContain("الطب العام");
    });

    it("returns Arabic category names when lang=ar", async () => {
      const result = await AppointmentTypeService.getFormattedCategoriesList(
        "org-1",
        "ar",
      );

      expect(result).toContain("1️⃣ الطب العام");
      expect(result).toContain("2️⃣ طب الأسنان");
      expect(result).toContain("3️⃣ الأمراض الجلدية");
      // Should NOT contain English names in Arabic mode
      expect(result).not.toContain("General Medicine");
    });

    it("returns empty string when no categories exist", async () => {
      (prisma.appointment_types.groupBy as jest.Mock).mockResolvedValue([]);

      const result = await AppointmentTypeService.getFormattedCategoriesList(
        "org-1",
        "en",
      );

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

    it("falls back to number prefix for 10+ categories", async () => {
      const manyCategories = Array.from({ length: 12 }, (_, i) => ({
        category: `Cat ${i + 1}`,
        categoryAr: `فئة ${i + 1}`,
        _count: { id: 3 },
      }));
      (prisma.appointment_types.groupBy as jest.Mock).mockResolvedValue(
        manyCategories,
      );

      const result =
        await AppointmentTypeService.getFormattedCategoriesList("org-1");

      // First 10 get emoji numbers, 11+ get plain numbers
      expect(result).toContain("🔟 Cat 10");
      expect(result).toContain("11. Cat 11");
      expect(result).toContain("12. Cat 12");
    });

    it("handles database errors gracefully", async () => {
      (prisma.appointment_types.groupBy as jest.Mock).mockRejectedValue(
        new Error("DB error"),
      );

      const result =
        await AppointmentTypeService.getFormattedCategoriesList("org-1");

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

  // ── getFormattedServicesInCategory ──────────────────────
  describe("getFormattedServicesInCategory", () => {
    beforeEach(() => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue(
        mockServicesInCategory,
      );
    });

    it("returns English service names with prices", async () => {
      const result =
        await AppointmentTypeService.getFormattedServicesInCategory(
          "org-1",
          "General Medicine",
          "en",
        );

      expect(result).toContain("1. General Checkup (30 min - 150 QAR)");
      expect(result).toContain("2. Blood Test (15 min - 80 QAR)");
      // No price → no price suffix
      expect(result).toContain("3. Vaccination (15 min)");
    });

    it("returns Arabic service names with Arabic duration label", async () => {
      const result =
        await AppointmentTypeService.getFormattedServicesInCategory(
          "org-1",
          "General Medicine",
          "ar",
        );

      expect(result).toContain("1. فحص عام (30 دقيقة - 150 QAR)");
      expect(result).toContain("2. تحليل دم (15 دقيقة - 80 QAR)");
      expect(result).toContain("3. تطعيم (15 دقيقة)");
      // Should NOT contain English names in Arabic mode
      expect(result).not.toContain("General Checkup");
    });

    it("returns empty string when no services in category", async () => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue([]);

      const result =
        await AppointmentTypeService.getFormattedServicesInCategory(
          "org-1",
          "Nonexistent",
        );

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

    it("omits price when null", async () => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue([
        {
          id: "10",
          nameEn: "Free Consultation",
          nameAr: "استشارة مجانية",
          duration: 20,
          price: null,
          isActive: true,
          sortOrder: 0,
        },
        {
          id: "11",
          nameEn: "Paid Checkup",
          nameAr: "فحص مدفوع",
          duration: 30,
          price: 200,
          isActive: true,
          sortOrder: 1,
        },
      ]);

      const result =
        await AppointmentTypeService.getFormattedServicesInCategory(
          "org-1",
          "General Medicine",
          "en",
        );

      // Free Consultation should NOT have QAR
      expect(result).toContain("1. Free Consultation (20 min)");
      expect(result).not.toMatch(/Free Consultation.*QAR/);

      // Paid Checkup should have QAR
      expect(result).toContain("2. Paid Checkup (30 min - 200 QAR)");
    });

    it("filters out inactive services via DB query", async () => {
      // The getTypesInCategory method passes isActive: true to the DB query.
      // When the DB returns only active services, inactive ones are excluded.
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue([
        {
          id: "1",
          nameEn: "Active Service",
          nameAr: "خدمة نشطة",
          duration: 30,
          price: 100,
          isActive: true,
          sortOrder: 0,
        },
        // Inactive services would NOT be returned by the DB query (where: { isActive: true })
      ]);

      const result =
        await AppointmentTypeService.getFormattedServicesInCategory(
          "org-1",
          "General Medicine",
          "en",
        );

      expect(result).toContain("Active Service");

      // Verify that the DB query includes isActive: true filter
      expect(prisma.appointment_types.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: expect.objectContaining({
            isActive: true,
          }),
        }),
      );
    });
  });

  // ── getFormattedServicesList (bilingual for AI) ────────
  describe("getFormattedServicesList", () => {
    it("returns bilingual format with price", async () => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue(
        mockServicesInCategory,
      );

      const result =
        await AppointmentTypeService.getFormattedServicesList("org-1");

      expect(result).toContain("- General Checkup / فحص عام (30 min, 150 QAR)");
      expect(result).toContain("- Blood Test / تحليل دم (15 min, 80 QAR)");
      // Null price → no price
      expect(result).toContain("- Vaccination / تطعيم (15 min)");
    });

    it("returns empty string when no active types", async () => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue([]);

      const result =
        await AppointmentTypeService.getFormattedServicesList("org-1");

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

  // ── getTypeNames ────────────────────────────────────────
  describe("getTypeNames", () => {
    it("returns English names array", async () => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue([
        { nameEn: "Checkup", nameAr: "فحص" },
        { nameEn: "Follow-up", nameAr: "متابعة" },
      ]);

      const names = await AppointmentTypeService.getTypeNames("org-1", "en");

      expect(names).toEqual(["Checkup", "Follow-up"]);
    });

    it("returns Arabic names array", async () => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue([
        { nameEn: "Checkup", nameAr: "فحص" },
        { nameEn: "Follow-up", nameAr: "متابعة" },
      ]);

      const names = await AppointmentTypeService.getTypeNames("org-1", "ar");

      expect(names).toEqual(["فحص", "متابعة"]);
    });
  });

  // ── findTypeByName ──────────────────────────────────────
  describe("findTypeByName", () => {
    const types = [
      { id: "1", nameEn: "General Checkup", nameAr: "فحص عام", isActive: true },
      {
        id: "2",
        nameEn: "Dental Cleaning",
        nameAr: "تنظيف أسنان",
        isActive: true,
      },
    ];

    beforeEach(() => {
      (prisma.appointment_types.findMany as jest.Mock).mockResolvedValue(types);
    });

    it("finds by English name (case-insensitive)", async () => {
      const result = await AppointmentTypeService.findTypeByName(
        "org-1",
        "general checkup",
      );

      expect(result?.id).toBe("1");
    });

    it("finds by Arabic name", async () => {
      const result = await AppointmentTypeService.findTypeByName(
        "org-1",
        "فحص عام",
      );

      expect(result?.id).toBe("1");
    });

    it("returns null when no match", async () => {
      const result = await AppointmentTypeService.findTypeByName(
        "org-1",
        "Unknown Service",
      );

      expect(result).toBeNull();
    });
  });

  // ── getTemplateCSV ──────────────────────────────────────
  describe("getTemplateCSV", () => {
    it("returns CSV with headers and example rows", () => {
      const csv = AppointmentTypeService.getTemplateCSV();

      expect(csv).toContain("nameEn,nameAr,duration,price");
      expect(csv).toContain("Consultation");
      expect(csv).toContain("استشارة");
    });
  });
});
