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

export type PdfLayoutFontName = 'TimesRoman' | 'TimesRomanBold' | 'Helvetica' | 'HelveticaBold';

export type PdfTextLayoutEdit = {
  id: string;
  text: string;
  page: number;
  x: number;
  y: number;
  fontSize: number;
  lineHeight: number;
  boxWidth: number;
  letterSpacing: number;
  textAlign: 'left' | 'center' | 'right';
  fontName: PdfLayoutFontName;
  colorHex: string;
};

const DEFAULT_TEXT_COLOR = '#111827';

function parseHexColor(colorHex?: string): { r: number; g: number; b: number } {
  const normalized = (colorHex || DEFAULT_TEXT_COLOR).trim();
  const short = /^#([0-9a-fA-F]{3})$/;
  const long = /^#([0-9a-fA-F]{6})$/;

  if (short.test(normalized)) {
    const m = normalized.match(short)?.[1] ?? '111';
    return {
      r: parseInt(m[0] + m[0], 16) / 255,
      g: parseInt(m[1] + m[1], 16) / 255,
      b: parseInt(m[2] + m[2], 16) / 255,
    };
  }
  if (long.test(normalized)) {
    const m = normalized.match(long)?.[1] ?? '111827';
    return {
      r: parseInt(m.slice(0, 2), 16) / 255,
      g: parseInt(m.slice(2, 4), 16) / 255,
      b: parseInt(m.slice(4, 6), 16) / 255,
    };
  }
  return { r: 17 / 255, g: 24 / 255, b: 39 / 255 };
}

function sanitizeEdits(edits: PdfTextLayoutEdit[], pageCount: number): PdfTextLayoutEdit[] {
  return edits
    .map((edit) => ({
      ...edit,
      text: String(edit.text || ''),
      page: Number.isFinite(edit.page) ? Math.max(1, Math.min(pageCount, Math.round(edit.page))) : 1,
      x: Number.isFinite(edit.x) ? edit.x : 48,
      y: Number.isFinite(edit.y) ? edit.y : 780,
      fontSize: Number.isFinite(edit.fontSize) ? Math.max(1, edit.fontSize) : 12,
      lineHeight: Number.isFinite(edit.lineHeight) ? Math.max(1, edit.lineHeight) : 14,
      boxWidth: Number.isFinite(edit.boxWidth) ? Math.max(1, edit.boxWidth) : 240,
      letterSpacing: Number.isFinite(edit.letterSpacing) ? edit.letterSpacing : 0,
      textAlign: edit.textAlign === 'center' || edit.textAlign === 'right' ? edit.textAlign : 'left',
      colorHex: edit.colorHex || DEFAULT_TEXT_COLOR,
    }))
    .filter((edit) => edit.text.trim().length > 0);
}

function measureLineWidth(line: string, fontSize: number, letterSpacing: number, font: Awaited<ReturnType<PDFDocument['embedFont']>>): number {
  if (!line) return 0;
  const chars = Array.from(line);
  const baseWidth = chars.reduce((sum, char) => sum + font.widthOfTextAtSize(char, fontSize), 0);
  return baseWidth + Math.max(0, chars.length - 1) * letterSpacing;
}

function drawSpacedLine(params: {
  page: ReturnType<PDFDocument['getPages']>[number];
  line: string;
  x: number;
  y: number;
  fontSize: number;
  letterSpacing: number;
  font: Awaited<ReturnType<PDFDocument['embedFont']>>;
  color: { r: number; g: number; b: number };
}) {
  const { page, line, x, y, fontSize, letterSpacing, font, color } = params;
  if (!line) return;
  if (letterSpacing === 0) {
    page.drawText(line, { x, y, size: fontSize, font, color: rgb(color.r, color.g, color.b) });
    return;
  }

  let cursorX = x;
  for (const char of Array.from(line)) {
    page.drawText(char, { x: cursorX, y, size: fontSize, font, color: rgb(color.r, color.g, color.b) });
    cursorX += font.widthOfTextAtSize(char, fontSize) + letterSpacing;
  }
}

export async function applyPdfTextLayoutEdits(pdfBlob: Blob, edits: PdfTextLayoutEdit[]): Promise<Blob> {
  const inputBytes = new Uint8Array(await pdfBlob.arrayBuffer());
  const pdfDoc = await PDFDocument.load(inputBytes, { updateMetadata: false });
  const pages = pdfDoc.getPages();
  const safeEdits = sanitizeEdits(edits, pages.length);

  const fontMap = {
    TimesRoman: await pdfDoc.embedFont(StandardFonts.TimesRoman),
    TimesRomanBold: await pdfDoc.embedFont(StandardFonts.TimesRomanBold),
    Helvetica: await pdfDoc.embedFont(StandardFonts.Helvetica),
    HelveticaBold: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
  } as const;

  for (const edit of safeEdits) {
    const page = pages[edit.page - 1];
    if (!page) continue;
    const color = parseHexColor(edit.colorHex);
    const lines = edit.text.split('\n');
    const font = fontMap[edit.fontName];
    lines.forEach((line, index) => {
      const lineWidth = measureLineWidth(line, edit.fontSize, edit.letterSpacing, font);
      const availableWidth = Math.max(1, edit.boxWidth);
      let startX = edit.x;
      if (edit.textAlign === 'center') {
        startX = edit.x + (availableWidth - lineWidth) / 2;
      } else if (edit.textAlign === 'right') {
        startX = edit.x + (availableWidth - lineWidth);
      }
      drawSpacedLine({
        page,
        line,
        x: startX,
        y: edit.y - index * edit.lineHeight,
        fontSize: edit.fontSize,
        letterSpacing: edit.letterSpacing,
        font,
        color,
      });
    });
  }

  const out = await pdfDoc.save({ useObjectStreams: true });
  return new Blob([out], { type: 'application/pdf' });
}
