import { describe, it, expect, vi } from "vitest";
import { parsePageRange, runPdfSplitter } from "./run";
import { PDFDocument } from "pdf-lib";
import type { ToolResult } from "../../core/io/output-types";

describe("PDF Splitter Utilities", () => {
  describe("parsePageRange", () => {
    it("should parse single pages correctly", () => {
      expect(parsePageRange("1", 5)).toEqual([0]);
      expect(parsePageRange("3", 5)).toEqual([2]);
    });

    it("should parse simple ranges correctly", () => {
      expect(parsePageRange("1-3", 5)).toEqual([0, 1, 2]);
    });

    it("should parse reverse ranges correctly", () => {
      expect(parsePageRange("3-1", 5)).toEqual([2, 1, 0]);
    });

    it("should parse comma-separated lists and ranges", () => {
      expect(parsePageRange("1-3, 5", 5)).toEqual([0, 1, 2, 4]);
      expect(parsePageRange("1, 3, 5", 10)).toEqual([0, 2, 4]);
    });

    it("should filter out pages out of bounds", () => {
      expect(parsePageRange("1-10", 3)).toEqual([0, 1, 2]);
      expect(parsePageRange("0, 5", 3)).toEqual([]); // 0 is invalid (1-indexed page bounds)
    });
  });

  describe("runPdfSplitter", () => {
    const createMockPdf = async (pagesCount: number): Promise<Uint8Array> => {
      const pdfDoc = await PDFDocument.create();
      for (let i = 0; i < pagesCount; i++) {
        pdfDoc.addPage([200, 200]);
      }
      return pdfDoc.save();
    };

    it("should extract a range of pages into a single PDF", async () => {
      const pdfBytes = await createMockPdf(5);
      const mockFile = new File([pdfBytes as unknown as BlobPart], "source.pdf", { type: "application/pdf" });

      const mockContext = {
        reportProgress: vi.fn(),
        signal: new AbortController().signal,
        logger: console,
      };

      const result = await runPdfSplitter(
        { files: [mockFile] },
        { splitMode: "extract", pageRange: "2-4" },
        mockContext
      ) as Extract<ToolResult, { type: "files" }>;

      expect(result.type).toBe("files");
      expect(result.files).toHaveLength(1);
      expect(result.files[0].name).toBe("source_extracted.pdf");
      expect(result.files[0].blob).toBeInstanceOf(Blob);

      // Verify the generated PDF has exactly 3 pages
      const resBytes = await result.files[0].blob.arrayBuffer();
      const resDoc = await PDFDocument.load(new Uint8Array(resBytes));
      expect(resDoc.getPageCount()).toBe(3);
    });

    it("should burst a PDF into separate page files", async () => {
      const pdfBytes = await createMockPdf(3);
      const mockFile = new File([pdfBytes as unknown as BlobPart], "source.pdf", { type: "application/pdf" });

      const mockContext = {
        reportProgress: vi.fn(),
        signal: new AbortController().signal,
        logger: console,
      };

      const result = await runPdfSplitter(
        { files: [mockFile] },
        { splitMode: "burst", pageRange: "" },
        mockContext
      ) as Extract<ToolResult, { type: "files" }>;

      expect(result.type).toBe("files");
      expect(result.files).toHaveLength(3);
      expect(result.files[0].name).toBe("source_page_1.pdf");
      expect(result.files[1].name).toBe("source_page_2.pdf");
      expect(result.files[2].name).toBe("source_page_3.pdf");

      // Verify each generated PDF has exactly 1 page
      for (const fileObj of result.files) {
        const resBytes = await fileObj.blob.arrayBuffer();
        const resDoc = await PDFDocument.load(new Uint8Array(resBytes));
        expect(resDoc.getPageCount()).toBe(1);
      }
    });

    it("should throw error if invalid range is supplied", async () => {
      const pdfBytes = await createMockPdf(3);
      const mockFile = new File([pdfBytes as unknown as BlobPart], "source.pdf", { type: "application/pdf" });

      const mockContext = {
        reportProgress: vi.fn(),
        signal: new AbortController().signal,
        logger: console,
      };

      await expect(
        runPdfSplitter(
          { files: [mockFile] },
          { splitMode: "extract", pageRange: "10-12" }, // Out of bounds
          mockContext
        )
      ).rejects.toThrow("No valid pages selected.");
    });
  });
});
