import { PDFParse } from "pdf-parse";
import { PdfEncryptedError, PdfUnreadableError } from "@/lib/pdf/errors";

export type PdfTextExtraction = {
  text: string;
  numPages: number;
};

function normalizeExtractedText(rawText: string) {
  const withoutPageMarkers = rawText
    .replace(/\u0000/g, " ")
    .replace(/\r\n?/g, "\n")
    .replace(/\n?\s*--\s*\d+\s*of\s*\d+\s*--\s*\n?/gi, "\n")
    .replace(/\n?\s*p[áa]gina\s+\d+\s+de\s+\d+\s*\n?/gi, "\n");

  return withoutPageMarkers
    .split("\n")
    .map((line) => line.replace(/\s+/g, " ").trim())
    .filter(Boolean)
    .join("\n")
    .trim();
}

function isEncryptedPdfError(error: unknown) {
  if (!(error instanceof Error)) {
    return false;
  }

  const name = error.name.toLowerCase();
  const message = error.message.toLowerCase();

  return name.includes("password") || message.includes("password") || message.includes("encrypted") || message.includes("encriptado");
}

export async function extractTextFromPdfBuffer(buf: Buffer): Promise<PdfTextExtraction> {
  if (!Buffer.isBuffer(buf) || buf.byteLength === 0) {
    throw new PdfUnreadableError("El buffer del PDF esta vacio.");
  }

  const parser = new PDFParse({ data: buf });

  try {
    const parsed = await parser.getText();
    const rawText = typeof parsed.text === "string" ? parsed.text : "";
    const text = normalizeExtractedText(rawText);
    const numPages = typeof parsed.total === "number" && Number.isFinite(parsed.total) ? parsed.total : 0;

    return {
      text,
      numPages,
    };
  } catch (error) {
    if (isEncryptedPdfError(error)) {
      throw new PdfEncryptedError(undefined, { cause: error });
    }

    throw new PdfUnreadableError("No fue posible extraer texto del PDF.", { cause: error });
  } finally {
    await parser.destroy().catch(() => undefined);
  }
}
