import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import type { PdfDocumentBundle } from "../services/renderers/types.js";

// ─── Mock playwright-core chromium ─────────────────────────────────────────────

const mockPdf = vi.fn().mockResolvedValue(Buffer.from("fake-pdf"));
const mockSetContent = vi.fn().mockResolvedValue(undefined);
const mockPage = {
  setContent: mockSetContent,
  pdf: mockPdf,
};
const mockBrowserClose = vi.fn().mockResolvedValue(undefined);
const mockBrowser = {
  newPage: vi.fn().mockResolvedValue(mockPage),
  close: mockBrowserClose,
};
const mockChromiumLaunch = vi.fn().mockResolvedValue(mockBrowser);

vi.mock("playwright-core", () => ({
  chromium: {
    launch: mockChromiumLaunch,
  },
}));

// ─── Mock puter-inference ──────────────────────────────────────────────────────

const mockPuterChatComplete = vi
  .fn()
  .mockResolvedValue("<!DOCTYPE html><html><body>test</body></html>");

vi.mock("../services/puter-inference.js", () => ({
  puterChatComplete: (...args: unknown[]) => mockPuterChatComplete(...args),
}));

// ─── Mock diagram-renderer (for resolveBrowserPath) ───────────────────────────

vi.mock("../services/renderers/diagram-renderer.js", () => ({
  resolveBrowserPath: () => "/fake/chromium",
}));

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

describe("renderPdfDocument", () => {
  let renderPdfDocument: (
    input: Record<string, unknown>,
  ) => Promise<import("../services/renderers/types.js").RenderResult>;

  beforeEach(async () => {
    vi.clearAllMocks();
    mockPuterChatComplete.mockResolvedValue(
      "<!DOCTYPE html><html><body>test</body></html>",
    );
    mockChromiumLaunch.mockResolvedValue(mockBrowser);
    mockBrowser.newPage.mockResolvedValue(mockPage);
    mockPdf.mockResolvedValue(Buffer.from("fake-pdf"));
    mockBrowserClose.mockResolvedValue(undefined);

    const mod = await import("../services/renderers/pdf-renderer.js");
    renderPdfDocument = mod.renderPdfDocument;
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it("returns RenderResult with pdf-document-bundle JSON for docType=report", async () => {
    const result = await renderPdfDocument({
      docType: "report",
      prompt: "Generate a quarterly sales report",
      title: "Q1 Sales Report",
    });

    expect(result.filename).toBe("document-report.json");
    expect(result.contentType).toBe("application/json");

    const bundle = JSON.parse(result.buffer.toString()) as PdfDocumentBundle;
    expect(bundle.type).toBe("pdf-document-bundle");
    expect(bundle.docType).toBe("report");
    expect(typeof bundle.pdfBase64).toBe("string");
    expect(bundle.pdfBase64.length).toBeGreaterThan(0);
  });

  it("returns bundle with docType='invoice' for docType=invoice", async () => {
    const result = await renderPdfDocument({
      docType: "invoice",
      prompt: "Invoice for web development services",
      title: "Invoice #001",
    });

    const bundle = JSON.parse(result.buffer.toString()) as PdfDocumentBundle;
    expect(bundle.type).toBe("pdf-document-bundle");
    expect(bundle.docType).toBe("invoice");
    expect(result.filename).toBe("document-invoice.json");
  });

  it("returns bundle with docType='api-docs' for docType=api-docs", async () => {
    const result = await renderPdfDocument({
      docType: "api-docs",
      prompt: "REST API documentation for user endpoints",
      title: "API Reference",
    });

    const bundle = JSON.parse(result.buffer.toString()) as PdfDocumentBundle;
    expect(bundle.type).toBe("pdf-document-bundle");
    expect(bundle.docType).toBe("api-docs");
    expect(result.filename).toBe("document-api-docs.json");
  });

  it("returns bundle with docType='one-pager' for docType=one-pager", async () => {
    const result = await renderPdfDocument({
      docType: "one-pager",
      prompt: "Product one-pager for investors",
      title: "Product Overview",
    });

    const bundle = JSON.parse(result.buffer.toString()) as PdfDocumentBundle;
    expect(bundle.type).toBe("pdf-document-bundle");
    expect(bundle.docType).toBe("one-pager");
    expect(result.filename).toBe("document-one-pager.json");
  });

  it("LLM system prompt varies by docType — report vs invoice vs api-docs vs one-pager", async () => {
    const capturedMessages: unknown[][] = [];
    mockPuterChatComplete.mockImplementation(
      (messages: unknown[]) => {
        capturedMessages.push(messages);
        return Promise.resolve(
          "<!DOCTYPE html><html><body>test</body></html>",
        );
      },
    );

    await renderPdfDocument({ docType: "report", prompt: "test" });
    await renderPdfDocument({ docType: "invoice", prompt: "test" });
    await renderPdfDocument({ docType: "api-docs", prompt: "test" });
    await renderPdfDocument({ docType: "one-pager", prompt: "test" });

    expect(capturedMessages).toHaveLength(4);
    const systemPrompts = capturedMessages.map(
      (msgs) => (msgs[0] as { role: string; content: string }).content,
    );

    // Each docType should produce a different system prompt
    const unique = new Set(systemPrompts);
    expect(unique.size).toBe(4);
  });

  it("closes browser even when rendering throws", async () => {
    mockPdf.mockRejectedValueOnce(new Error("PDF generation failed"));

    await expect(
      renderPdfDocument({ docType: "report", prompt: "test" }),
    ).rejects.toThrow();

    expect(mockBrowserClose).toHaveBeenCalled();
  });
});
