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

const {
  requirePermissionMock,
  employeeScopeFromAccessMock,
  generateAttestationImpotsMock,
} = vi.hoisted(() => ({
  requirePermissionMock: vi.fn(),
  employeeScopeFromAccessMock: vi.fn(),
  generateAttestationImpotsMock: vi.fn(),
}));

vi.mock("@/lib/authorization", () => ({
  requirePermission: requirePermissionMock,
  employeeScopeFromAccess: employeeScopeFromAccessMock,
}));

vi.mock("@/services/impressions.service", () => {
  class MockDocumentGenerationError extends Error {
    constructor(
      public readonly code: string,
      public readonly missingFields: string[] = [],
    ) {
      super(code);
      this.name = "DocumentGenerationError";
    }
  }

  return {
    DocumentGenerationError: MockDocumentGenerationError,
    generateAttestationImpots: generateAttestationImpotsMock,
  };
});

import { POST } from "@/app/api/impressions/attestation-impots/route";
import { DocumentGenerationError } from "@/services/impressions.service";

function request(body: string) {
  return new Request("http://localhost/api/impressions/attestation-impots", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body,
  });
}

describe("Unit — /api/impressions/attestation-impots", () => {
  beforeEach(() => {
    vi.resetAllMocks();
    requirePermissionMock.mockResolvedValue({
      ok: true,
      access: { allSectors: true },
    });
    employeeScopeFromAccessMock.mockReturnValue({ sectors: undefined });
  });

  it("requires impressions read permission", async () => {
    requirePermissionMock.mockResolvedValue({
      ok: false,
      response: Response.json({ message: "Forbidden" }, { status: 403 }),
    });

    const response = await POST(request(JSON.stringify({ cos: 123, signataireId: 7 })));

    expect(response.status).toBe(403);
    expect(generateAttestationImpotsMock).not.toHaveBeenCalled();
  });

  it("rejects an invalid body", async () => {
    const response = await POST(request(JSON.stringify({ cos: "abc" })));

    expect(response.status).toBe(400);
    expect(generateAttestationImpotsMock).not.toHaveBeenCalled();
  });

  it("downloads the generated DOCX", async () => {
    generateAttestationImpotsMock.mockResolvedValue(Buffer.from("docx"));

    const response = await POST(request(JSON.stringify({ cos: 123, signataireId: 7 })));

    expect(response.status).toBe(200);
    expect(response.headers.get("content-type")).toContain("wordprocessingml.document");
    expect(response.headers.get("content-disposition")).toBe(
      'attachment; filename="attestation-impots-123.docx"',
    );
    expect(generateAttestationImpotsMock).toHaveBeenCalledWith(
      123,
      7,
      { sectors: undefined },
    );
  });

  it("returns 409 when the employee has no contract", async () => {
    generateAttestationImpotsMock.mockRejectedValue(
      new DocumentGenerationError("CONTRACT_NOT_FOUND"),
    );

    const response = await POST(request(JSON.stringify({ cos: 123, signataireId: 7 })));

    expect(response.status).toBe(409);
  });

  it("returns missing fields with status 422", async () => {
    generateAttestationImpotsMock.mockRejectedValue(
      new DocumentGenerationError("MISSING_DOCUMENT_DATA", ["poste"]),
    );

    const response = await POST(request(JSON.stringify({ cos: 123, signataireId: 7 })));

    expect(response.status).toBe(422);
    await expect(response.json()).resolves.toMatchObject({
      missingFields: ["poste"],
    });
  });
});
