import { PDFName, PDFPage, PDFString, PDFDocument } from "pdf-lib";
import { PdfData } from "pdfdataextract";
import { config } from "./config";
import { IGetMetaResponse } from "./types";
import { verifyPDF } from "./lib";
import { formatPdfTime } from "./lib/timeUtils";
import { AppError, GeneralError } from "./lib/errors";
import { isPDF } from "./lib/generalUtils";

type SealCoords = {
  [key: string]: { x: string; y: string };
};

class PDFparser {
  readonly document;
  readonly config;

  constructor(document: Buffer) {
    this.document = document;
    this.config = config;
  }

  getPDFMeta = async (): Promise<IGetMetaResponse> => {
    if (!(this.document instanceof Buffer)) {
      throw new AppError("Document is not Buffer");
    }

    if (!isPDF(this.document)) {
      throw new AppError("Only pdf file type is supported");
    }

    try {
      const signaturesMeta = await verifyPDF(this.document);
      const pdfMeta = await PdfData.extract(this.document, config);

      const result = {
        pages: pdfMeta.pages,
        title: pdfMeta.info.Title || "Unknown",
        author: pdfMeta.info.Author || "Unknown",
        creation_date: formatPdfTime(pdfMeta.info.CreationDate),
        mod_date: formatPdfTime(pdfMeta.info.ModDate),
      };

      if (signaturesMeta) {
        result["signatures"] = signaturesMeta.signatures;
        result["expired"] = signaturesMeta.expired;
      }

      return result;
    } catch (error) {
      throw new GeneralError(error);
    }
  };

  insertQrCode = async (
    imgBytes: ArrayBuffer,
    url: string,
    coords: SealCoords,
    scaleFactor: number
  ): Promise<ArrayBuffer> => {
    const pdfDoc = await PDFDocument.load(this.document);
    const img = await pdfDoc.embedPng(imgBytes);

    const scaled = img.scale(scaleFactor);

    const pages = pdfDoc.getPages();

    console.log(coords);

    for (let index = 0; index < pages.length; index++) {
      const page = pages[index];

      console.log(coords[index + 1]);

      const x =
        typeof coords[index + 1] !== "undefined"
          ? parseFloat(coords[index + 1]["x"])
          : null;

      const y =
        typeof typeof coords[index + 1] !== "undefined"
          ? parseFloat(coords[index + 1]["y"])
          : null;

      if (x && y) {
        page.drawImage(img, {
          x,
          y,
          width: scaled.width,
          height: scaled.height,
        });

        const link = this.createPageLinkAnnotation(page, url, {
          imgXPos: x,
          imgYPos: y,
          imgWidth: scaled.width,
          imagHeight: scaled.height,
        });

        page.node.set(PDFName.of("Annots"), pdfDoc.context.obj([link]));
      }
    }

    const pdfBytes = await pdfDoc.save();

    return pdfBytes;
  };

  private createPageLinkAnnotation = (
    page: PDFPage,
    uri: string,
    { imgXPos, imgYPos, imgWidth, imagHeight }
  ) =>
    page.doc.context.register(
      page.doc.context.obj({
        Type: "Annot",
        Subtype: "Link",
        Rect: [imgXPos, imgYPos, imgXPos + imgWidth, imgYPos + imagHeight],
        A: {
          Type: "Action",
          S: "URI",
          URI: PDFString.of(uri),
        },
      })
    );
}

export default PDFparser;