import { beforeEach, describe, expect, it, vi } from "vitest";
import PizZip from "pizzip";

vi.mock("@/lib/prisma", () => ({
  prisma: {
    contrats: { findFirst: vi.fn() },
    ref_signataires: { findFirst: vi.fn(), findMany: vi.fn() },
    etablissement: { findFirst: vi.fn() },
  },
}));

vi.mock("@/services/employes.service", () => ({
  getEmployeByCos: vi.fn(),
}));

import { prisma } from "@/lib/prisma";
import { getEmployeByCos } from "@/services/employes.service";
import {
  buildAttestationImpotsData,
  buildAttestationImpotsPdfDraft,
  buildAttestationRetraitePdfDraft,
  listActiveSignataires,
  renderAttestationImpots,
} from "@/services/impressions.service";

const unrestrictedScope = {
  sectors: undefined,
  establishments: undefined,
  categories: undefined,
  contractTypes: undefined,
};

describe("Unit — services/impressions.service", () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  it("lists only active signataires", async () => {
    const expected = [
      { id: 1, prenomnom: "Béatrice LAFRANCE", fonction: "Directrice" },
    ];
    vi.mocked(prisma.ref_signataires.findMany).mockResolvedValue(expected as never);

    await expect(listActiveSignataires()).resolves.toEqual(expected);
    expect(prisma.ref_signataires.findMany).toHaveBeenCalledWith({
      where: { actif: true },
      orderBy: [{ prenomnom: "asc" }, { id: "asc" }],
      select: { id: true, prenomnom: true, fonction: true },
    });
  });

  it("builds the template data from employee, latest contract and signataire", async () => {
    vi.mocked(getEmployeByCos).mockResolvedValue({
      COS: 123,
      TIT: "Mme",
      PRE: "Alice",
      NSA: "MARTIN",
      ADR: "10 rue de Lille",
      COP: "59000",
      VIL: "Lille",
    } as never);
    vi.mocked(prisma.contrats.findFirst).mockResolvedValue({
      DAE: new Date("2026-05-11T00:00:00.000Z"),
      DSP: new Date("2026-12-31T00:00:00.000Z"),
      DSR: null,
      TCS: "CDD",
      POS: "AGENT",
      QUA: "Opératrice de tri",
    } as never);
    vi.mocked(prisma.ref_signataires.findFirst).mockResolvedValue({
      prenomnom: "Béatrice LAFRANCE",
      fonction: "Directrice générale",
    } as never);

    const result = await buildAttestationImpotsData(
      123,
      7,
      unrestrictedScope,
      new Date("2026-08-11T10:00:00.000Z"),
    );

    expect(getEmployeByCos).toHaveBeenCalledWith(123, unrestrictedScope);
    expect(result).toEqual({
      formuleSoussignataire: "soussigné(e)",
      signataireNom: "Béatrice LAFRANCE",
      signataireFonction: "Directrice générale",
      titreNomPrenom: "Madame Alice MARTIN",
      domicilie: "domiciliée",
      adresseLigne: "10 rue de Lille, 59000 Lille",
      typeContrat: "CDD",
      dateEmbauche: "11/05/2026",
      poste: "Opératrice de tri",
      dateDocument: "11/08/2026",
    });
  });

  it("rejects an employee outside the caller scope", async () => {
    vi.mocked(getEmployeByCos).mockResolvedValue(null);

    await expect(
      buildAttestationImpotsData(999, 7, unrestrictedScope),
    ).rejects.toMatchObject({
      code: "EMPLOYE_NOT_FOUND",
    });
  });

  it("rejects generation when no contract exists", async () => {
    vi.mocked(getEmployeByCos).mockResolvedValue({ COS: 123 } as never);
    vi.mocked(prisma.contrats.findFirst).mockResolvedValue(null);

    await expect(
      buildAttestationImpotsData(123, 7, unrestrictedScope),
    ).rejects.toMatchObject({
      code: "CONTRACT_NOT_FOUND",
    });
  });

  it("uses the latest contract even when the employee is no longer active", async () => {
    vi.mocked(getEmployeByCos).mockResolvedValue({
      COS: 123,
      TIT: "M",
      PRE: "Jean",
      NSA: "DUPONT",
      ADR: "1 rue Nationale",
      COP: "59000",
      VIL: "Lille",
    } as never);
    vi.mocked(prisma.contrats.findFirst).mockResolvedValue({
      DAE: new Date("2026-01-01T00:00:00.000Z"),
      DSP: new Date("2026-08-10T00:00:00.000Z"),
      DSR: new Date("2026-08-10T00:00:00.000Z"),
      TCS: "CDD",
      POS: "AGENT",
      QUA: null,
    } as never);
    vi.mocked(prisma.ref_signataires.findFirst).mockResolvedValue({
      prenomnom: "Béatrice LAFRANCE",
      fonction: "Directrice",
    } as never);

    await expect(
      buildAttestationImpotsData(
        123,
        7,
        unrestrictedScope,
        new Date("2026-08-11T10:00:00.000Z"),
      ),
    ).resolves.toMatchObject({
      titreNomPrenom: "Monsieur Jean DUPONT",
      typeContrat: "CDD",
      dateEmbauche: "01/01/2026",
    });

    expect(prisma.contrats.findFirst).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { Id_Salarie: 123 },
        orderBy: [{ DAE: "desc" }, { id: "desc" }],
      }),
    );
  });

  it("returns the missing fields instead of producing an incomplete document", async () => {
    vi.mocked(getEmployeByCos).mockResolvedValue({
      COS: 123,
      TIT: "M",
      PRE: "Jean",
      NSA: "DUPONT",
      ADR: null,
      COP: null,
      VIL: null,
    } as never);
    vi.mocked(prisma.contrats.findFirst).mockResolvedValue({
      DAE: new Date("2026-05-11T00:00:00.000Z"),
      DSP: null,
      DSR: null,
      TCS: "CDI",
      POS: null,
      QUA: null,
    } as never);
    vi.mocked(prisma.ref_signataires.findFirst).mockResolvedValue({
      prenomnom: "Béatrice LAFRANCE",
      fonction: "Directrice",
    } as never);

    await expect(
      buildAttestationImpotsData(123, 7, unrestrictedScope),
    ).rejects.toMatchObject({
      code: "MISSING_DOCUMENT_DATA",
      missingFields: ["adresseLigne", "poste"],
    });
  });

  it("renders a valid DOCX with every template tag replaced", async () => {
    const buffer = await renderAttestationImpots({
      formuleSoussignataire: "soussigné(e)",
      signataireNom: "Béatrice LAFRANCE",
      signataireFonction: "Directrice générale",
      titreNomPrenom: "Madame Alice MARTIN",
      domicilie: "domiciliée",
      adresseLigne: "10 rue de Lille, 59000 Lille",
      typeContrat: "CDD",
      dateEmbauche: "11/05/2026",
      poste: "Opératrice de tri",
      dateDocument: "11/08/2026",
    });

    expect(buffer.subarray(0, 2).toString()).toBe("PK");
    const xml = new PizZip(buffer).file("word/document.xml")?.asText() ?? "";
    expect(xml).toContain("Béatrice LAFRANCE");
    expect(xml).toContain("Alice MARTIN");
    expect(xml).not.toMatch(/\{[a-zA-Z]+\}/);
  });

  it("builds an editable PDF draft and detects a former employee", async () => {
    vi.mocked(getEmployeByCos).mockResolvedValue({
      COS: 123,
      TIT: "Mme",
      PRE: "Alice",
      NSA: "MARTIN",
      ADR: "10 rue de Lille",
      COP: "59000",
      VIL: "Lille",
    } as never);
    vi.mocked(prisma.contrats.findFirst).mockResolvedValue({
      DAE: new Date("2025-05-11T00:00:00.000Z"),
      DSP: new Date("2026-07-31T00:00:00.000Z"),
      DSR: new Date("2026-07-31T00:00:00.000Z"),
      TCS: "CDD",
      POS: "AGENT",
      QUA: "Opératrice de tri",
      ETB: "LESQUIN",
    } as never);
    vi.mocked(prisma.ref_signataires.findFirst).mockResolvedValue({
      prenomnom: "Béatrice LAFRANCE",
      fonction: "Directrice générale",
    } as never);
    vi.mocked(prisma.etablissement.findFirst).mockResolvedValue({
      entreprise: "S.A.S. ENVIE 2E NORD",
    } as never);

    await expect(
      buildAttestationImpotsPdfDraft(
        123,
        7,
        unrestrictedScope,
        new Date("2026-08-11T10:00:00.000Z"),
      ),
    ).resolves.toEqual({
      formuleSoussignataire: "soussigné(e)",
      signataireNom: "Béatrice LAFRANCE",
      signataireFonction: "Directrice générale",
      titreNomPrenom: "Madame Alice MARTIN",
      domicilie: "domiciliée",
      adresseLigne: "10 rue de Lille, 59000 Lille",
      entreprise: "S.A.S. ENVIE 2E NORD",
      typeContrat: "CDD",
      dateEmbauche: "2025-05-11",
      dateFinContrat: "2026-07-31",
      poste: "Opératrice de tri",
      dateDocument: "2026-08-11",
      lieuDocument: "Lesquin",
      estToujoursDansEffectifs: false,
    });
  });

  it("prefills retirement on the day after the planned departure", async () => {
    vi.mocked(getEmployeByCos).mockResolvedValue({
      COS: 123,
      TIT: "M",
      PRE: "Jean",
      NSA: "DUPONT",
      ADR: "1 rue Nationale",
      COP: "59000",
      VIL: "Lille",
    } as never);
    vi.mocked(prisma.contrats.findFirst).mockResolvedValue({
      DAE: new Date("2024-05-11T00:00:00.000Z"),
      DSP: new Date("2026-09-30T00:00:00.000Z"),
      DSR: null,
      TCS: "CDD",
      POS: "AGENT",
      QUA: "Agent polyvalent",
      ETB: "LESQUIN",
    } as never);
    vi.mocked(prisma.ref_signataires.findFirst).mockResolvedValue({
      prenomnom: "Béatrice LAFRANCE",
      fonction: "Directrice générale",
    } as never);
    vi.mocked(prisma.etablissement.findFirst).mockResolvedValue({
      entreprise: "S.A.S. ENVIE 2E NORD",
    } as never);

    await expect(
      buildAttestationRetraitePdfDraft(
        123,
        7,
        unrestrictedScope,
        new Date("2026-08-11T10:00:00.000Z"),
      ),
    ).resolves.toMatchObject({
      dateSortiePrevue: "2026-09-30",
      dateRetraite: "2026-10-01",
    });
  });
});
