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

const { prismaMock, readStoredProposalDocumentMock, storeContractDocumentFileMock } = vi.hoisted(() => ({
  prismaMock: {
    proposal: {
      findFirst: vi.fn(),
    },
    contractDocument: {
      findMany: vi.fn(),
      create: vi.fn(),
    },
  },
  readStoredProposalDocumentMock: vi.fn(),
  storeContractDocumentFileMock: vi.fn(),
}));

vi.mock("@/lib/prisma", () => ({
  prisma: prismaMock,
}));

vi.mock("@/lib/proposals/storage", async () => {
  const actual = await vi.importActual<typeof import("@/lib/proposals/storage")>("@/lib/proposals/storage");
  return {
    ...actual,
    readStoredProposalDocument: readStoredProposalDocumentMock,
  };
});

vi.mock("@/lib/contracts/storage", async () => {
  const actual = await vi.importActual<typeof import("@/lib/contracts/storage")>("@/lib/contracts/storage");
  return {
    ...actual,
    storeContractDocumentFile: storeContractDocumentFileMock,
  };
});

import {
  ensureProposalPdfLinkedToContract,
  getLatestProposalPdfSourceForUser,
  isPdfProposalDocument,
} from "@/lib/contracts/proposal-continuity";

describe("proposal continuity", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("detects PDF proposal documents by mime type or file extension", () => {
    expect(isPdfProposalDocument({ fileName: "source.pdf", mimeType: "application/octet-stream" })).toBe(true);
    expect(isPdfProposalDocument({ fileName: "source.bin", mimeType: "application/pdf" })).toBe(true);
    expect(isPdfProposalDocument({ fileName: "source.docx", mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" })).toBe(
      false,
    );
  });

  it("returns proposal_not_found when proposal does not belong to user", async () => {
    prismaMock.proposal.findFirst.mockResolvedValue(null);

    const result = await getLatestProposalPdfSourceForUser("user-1", "proposal-1");

    expect(result).toEqual({ status: "proposal_not_found" });
    expect(readStoredProposalDocumentMock).not.toHaveBeenCalled();
  });

  it("returns pdf_not_found when proposal has no PDF document", async () => {
    prismaMock.proposal.findFirst.mockResolvedValue({
      id: "proposal-1",
      title: "Propuesta sin PDF",
      documents: [{ id: "doc-1", fileName: "evidencia.docx", filePath: "storage/proposals/u/p/doc-1.docx", mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", sizeBytes: 20, checksumSha256: null, createdAt: new Date() }],
    });

    const result = await getLatestProposalPdfSourceForUser("user-1", "proposal-1");

    expect(result).toEqual({
      status: "pdf_not_found",
      proposal: { id: "proposal-1", title: "Propuesta sin PDF" },
    });
    expect(readStoredProposalDocumentMock).not.toHaveBeenCalled();
  });

  it("returns PDF source when proposal has a PDF document", async () => {
    const fileBuffer = Buffer.from("pdf");
    prismaMock.proposal.findFirst.mockResolvedValue({
      id: "proposal-1",
      title: "Propuesta con PDF",
      documents: [
        {
          id: "doc-2",
          fileName: "contrato.pdf",
          filePath: "storage/proposals/u/p/doc-2.pdf",
          mimeType: "application/pdf",
          sizeBytes: 120,
          checksumSha256: "sha-source",
          createdAt: new Date(),
        },
      ],
    });
    readStoredProposalDocumentMock.mockResolvedValue(fileBuffer);

    const result = await getLatestProposalPdfSourceForUser("user-1", "proposal-1");

    expect(result).toEqual({
      status: "ok",
      source: {
        proposal: { id: "proposal-1", title: "Propuesta con PDF" },
        document: {
          id: "doc-2",
          fileName: "contrato.pdf",
          filePath: "storage/proposals/u/p/doc-2.pdf",
          mimeType: "application/pdf",
          sizeBytes: 120,
          checksumSha256: "sha-source",
          createdAt: expect.any(Date),
        },
        fileBuffer,
      },
    });
    expect(readStoredProposalDocumentMock).toHaveBeenCalledTimes(1);
    expect(readStoredProposalDocumentMock.mock.calls[0]?.[0]).toBe("storage/proposals/u/p/doc-2.pdf");
  });

  it("avoids creating duplicate contract document when checksum matches", async () => {
    prismaMock.contractDocument.findMany.mockResolvedValue([
      {
        id: "existing-doc",
        fileName: "contrato.pdf",
        sizeBytes: 120,
        checksumSha256: "sha-source",
      },
    ]);

    const result = await ensureProposalPdfLinkedToContract({
      userId: "user-1",
      contractId: "contract-1",
      source: {
        proposal: { id: "proposal-1", title: "Propuesta" },
        document: {
          id: "proposal-doc",
          fileName: "contrato.pdf",
          filePath: "storage/proposals/u/p/doc.pdf",
          mimeType: "application/pdf",
          sizeBytes: 120,
          checksumSha256: "sha-source",
          createdAt: new Date(),
        },
        fileBuffer: Buffer.from("pdf"),
      },
    });

    expect(result).toEqual({ created: false, documentId: "existing-doc" });
    expect(storeContractDocumentFileMock).not.toHaveBeenCalled();
    expect(prismaMock.contractDocument.create).not.toHaveBeenCalled();
  });

  it("stores and links proposal PDF when no duplicate exists", async () => {
    prismaMock.contractDocument.findMany.mockResolvedValue([]);
    storeContractDocumentFileMock.mockResolvedValue({
      fileName: "contract-copy.pdf",
      filePath: "storage/contracts/user-1/contract-1/copy.pdf",
      mimeType: "application/pdf",
      sizeBytes: 120,
      checksumSha256: "sha-copy",
    });
    prismaMock.contractDocument.create.mockResolvedValue({
      id: "new-doc",
    });

    const result = await ensureProposalPdfLinkedToContract({
      userId: "user-1",
      contractId: "contract-1",
      source: {
        proposal: { id: "proposal-1", title: "Propuesta" },
        document: {
          id: "proposal-doc",
          fileName: "contrato.pdf",
          filePath: "storage/proposals/u/p/doc.pdf",
          mimeType: "application/pdf",
          sizeBytes: 120,
          checksumSha256: "sha-source",
          createdAt: new Date(),
        },
        fileBuffer: Buffer.from("pdf"),
      },
    });

    expect(result).toEqual({ created: true, documentId: "new-doc" });
    expect(storeContractDocumentFileMock).toHaveBeenCalledWith("user-1", "contract-1", "contrato.pdf", "application/pdf", expect.any(Buffer));
    expect(prismaMock.contractDocument.create).toHaveBeenCalledWith({
      data: {
        contractId: "contract-1",
        fileName: "contract-copy.pdf",
        filePath: "storage/contracts/user-1/contract-1/copy.pdf",
        mimeType: "application/pdf",
        sizeBytes: 120,
        checksumSha256: "sha-copy",
        kind: "SIGNED_CONTRACT",
      },
      select: {
        id: true,
      },
    });
  });
});
