import { PDFDocument, rgb } from 'pdf-lib';

export class PdfService {
  /**
   * @param {ArrayBuffer} pdfBytes
   * @param {File} pdfFile
   */
  constructor(pdfBytes, pdfFile) {
    this.pdfBytes = pdfBytes;
    this.pdfOriginal = pdfBytes;
    this.pdfFile = pdfFile;
  }

  async loadPdfFromFile(file) {
    this.pdfFile = file;
    this.pdfBytes = await file.arrayBuffer();
    return true;
  }

  async loadPdfFromUrl(url) {
    const response = await fetch(url);
    const arrayBuffer = await response.arrayBuffer();
    this.pdfBytes = arrayBuffer;
    this.pdfFile = new File([arrayBuffer], 'document.pdf', { type: 'application/pdf' });
    return true;
  }

  getPositionForText(position, pageWidth, pageHeight, fontSize = 12, textLength = 0) {
    const margin = 2;
    const charWidth = fontSize * 0.6;
    const textWidth = textLength * charWidth;

    switch (position) {
      case 'top-left':
        return { x: margin, y: pageHeight - margin - fontSize };
      case 'top-center':
        return { x: (pageWidth - textWidth) / 2, y: pageHeight - margin - fontSize };
      case 'top-right':
        return { x: pageWidth - margin - textWidth, y: pageHeight - margin - fontSize };
      case 'middle-left':
        return { x: margin, y: (pageHeight + fontSize) / 2 };
      case 'middle-center':
        return { x: (pageWidth - textWidth) / 2, y: pageHeight / 2 };
      case 'middle-right':
        return { x: pageWidth - margin - textWidth, y: (pageHeight + fontSize) / 2 };
      case 'bottom-left':
        return { x: margin, y: margin + fontSize };
      case 'bottom-center':
        return { x: (pageWidth - textWidth) / 2, y: margin + fontSize };
      case 'bottom-right':
        return { x: pageWidth - margin - textWidth, y: margin + fontSize };
      default:
        return null;
    }
  }

  getPositionForImage(position, pageWidth, pageHeight, imageWidth = 100, imageHeight = 100) {
    const margin = 2;

    switch (position) {
      case 'top-left':
        return { x: margin, y: pageHeight - margin - imageHeight };
      case 'top-center':
        return { x: (pageWidth - imageWidth) / 2, y: pageHeight - margin - imageHeight };
      case 'top-right':
        return { x: pageWidth - margin - imageWidth, y: pageHeight - margin - imageHeight };
      case 'middle-left':
        return { x: margin, y: (pageHeight - imageHeight) / 2 };
      case 'middle-center':
        return { x: (pageWidth - imageWidth) / 2, y: (pageHeight - imageHeight) / 2 };
      case 'middle-right':
        return { x: pageWidth - margin - imageWidth, y: (pageHeight - imageHeight) / 2 };
      case 'bottom-left':
        return { x: margin, y: margin };
      case 'bottom-center':
        return { x: (pageWidth - imageWidth) / 2, y: margin };
      case 'bottom-right':
        return { x: pageWidth - margin - imageWidth, y: margin };
      default:
        return null;
    }
  }

  /**
   *
   * @param {PrintPdfConfigOverlay} configContent
   * @returns
   */
  async addConfigContent(configContent) {
    const pdfDoc = await PDFDocument.load(this.pdfBytes);
    const pages = pdfDoc.getPages();

    const configText = configContent.text;
    const configImage = configContent.image;
    if (configText && configText.active && configText.content) {
      const fontSize = configText.fontSize || 12;
      const fontStyle = await pdfDoc.embedFont(configText.isBold ? 'Helvetica-Bold' : 'Helvetica');
      const { position, content } = configText;

      pages.forEach((page) => {
        const { width, height } = page.getSize();

        let coordinates;

        if (position === 'custom') {
          coordinates = {
            x: configText.x,
            y: height - configText.y,
          };
        } else {
          coordinates = this.getPositionForText(position, width, height, fontSize, content.length);
        }

        if (coordinates) {
          page.drawText(content, {
            x: coordinates.x,
            y: coordinates.y,
            size: fontSize,
            font: fontStyle,
            color: rgb(0, 0, 0),
          });
        }
      });
    }

    if (configImage && configImage.active && configImage.data) {
      if (configImage.base64) {
        configImage.data = await this.base64ToFile(configImage.base64);
      }

      const imageBytes = await configImage.data.arrayBuffer();

      let imageEmbed;

      const imageType = configImage.data.type.toLowerCase();

      if (imageType === 'image/png') {
        imageEmbed = await pdfDoc.embedPng(imageBytes);
      } else if (imageType === 'image/jpeg' || imageType === 'image/jpg') {
        imageEmbed = await pdfDoc.embedJpg(imageBytes);
      } else {
        throw new Error('Unsupported image type');
      }

      pages.forEach((page) => {
        const { width: pageWidth, height: pageHeight } = page.getSize();
        let coordinates;

        if (configImage.position === 'custom') {
          coordinates = { x: configImage.x, y: pageHeight - configImage.y };
        } else {
          coordinates = this.getPositionForImage(
            configImage.position,
            pageWidth,
            pageHeight,
            configImage.width || 100,
            configImage.height || 100,
          );
        }

        if (coordinates) {
          page.drawImage(imageEmbed, {
            x: coordinates.x,
            y: coordinates.y,
            width: configImage.width || 100,
            height: configImage.height || 100,
          });
        }
      });
    }

    return pdfDoc.save();
  }

  /**
   *
   * @param {PrintSession} sessionPrint
   * @returns
   */
  async addFooterContent(sessionPrint) {
    const pdfDoc = await PDFDocument.load(this.pdfBytes);
    const pages = pdfDoc.getPages();
    const fontStyle = await pdfDoc.embedFont('Helvetica-Bold');
    const {
      fontSize, position, isPagging, isTimestamp, content,
    } = sessionPrint;

    pages.forEach((page, index) => {
      const { width, height } = page.getSize();

      const footerText = isPagging ? `${content} (${index + 1}/${pages.length})` : content;

      let coordinates;

      if (position === 'custom') {
        coordinates = {
          x: sessionPrint.x,
          y: height - sessionPrint.y,
        };
      } else {
        coordinates = this.getPositionForText(position, width, height, fontSize, footerText.length);
      }

      page.drawText(footerText, {
        x: coordinates.x,
        y: coordinates.y,
        size: fontSize,
        font: fontStyle,
        color: rgb(0, 0, 0),
      });

      if (isTimestamp) {
        const timestamp = new Date().toLocaleString();
        page.drawText(timestamp, {
          x: coordinates.x,
          y: coordinates.y - fontSize,
          size: 10,
          font: fontStyle,
          color: rgb(0, 0, 0),
        });
      }
    });

    return pdfDoc.save();
  }

  getPdfPreviewUrl(pdfInput) {
    const blob = new Blob([pdfInput || this.pdfBytes], { type: 'application/pdf' });
    return URL.createObjectURL(blob);
  }

  getPdfFile() {
    return this.pdfFile;
  }

  getPdfBytes() {
    return this.pdfBytes;
  }

  setPdfBytes(pdfBuffer) {
    this.pdfBytes = pdfBuffer;
    this.pdfOriginal = pdfBuffer;
    return this.pdfBytes;
  }

  resetOriginal() {
    this.pdfBytes = this.pdfOriginal;
  }

  base64ToFile(base64, filename) {
    const arr = base64.split(',');
    const mime = arr[0].match(/:(.*?);/)[1];
    const bstr = atob(arr[1]);
    let n = bstr.length;
    const u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, { type: mime });
  }
}
