import { PDFDocument, StandardFonts, rgb } from '@cantoo/pdf-lib';
import { readFile, writeFile, rename } from 'node:fs/promises';
import type { SignatureFieldData } from '@/lib/db/schema';

/**
 * Fills AcroForm text fields and draws signature rectangles on a PDF.
 * Uses atomic write: write to tmp path, verify %PDF header, rename to final path.
 *
 * Text fill strategy:
 *   1. Attempt AcroForm field filling by field name (works when PDF has AcroForm fields).
 *   2. For any entry that did not match an AcroForm field (or when there is no AcroForm at all),
 *      draw the key=value pairs as text directly on page 1 near the top margin.
 *      This ensures text fill data is ALWAYS visible in the output PDF.
 *
 * @param srcPath     - Absolute path to original PDF
 * @param destPath    - Absolute path to write prepared PDF (e.g. {docId}_prepared.pdf)
 * @param textFields  - Key/value map: { fieldNameOrLabel: value } (agent-provided)
 * @param sigFields   - Signature field array from SignatureFieldData[]
 */
export async function preparePdf(
  srcPath: string,
  destPath: string,
  textFields: Record<string, string>,
  sigFields: SignatureFieldData[],
): Promise<void> {
  const pdfBytes = await readFile(srcPath);
  const pdfDoc = await PDFDocument.load(pdfBytes);
  const helvetica = await pdfDoc.embedFont(StandardFonts.Helvetica);
  const pages = pdfDoc.getPages();

  // Track which text field entries were successfully written via AcroForm so that
  // the fallback text stamp only shows entries that were NOT already embedded.
  const acroFilledKeys = new Set<string>();
  let hasAcroForm = false;

  // Strategy A: fill existing AcroForm fields by name.
  // form.flatten() MUST happen before drawing rectangles — if reversed,
  // the AcroForm overlay obscures the drawn rectangles.
  try {
    const form = pdfDoc.getForm();
    hasAcroForm = true;
    for (const [fieldName, value] of Object.entries(textFields)) {
      try {
        form.getTextField(fieldName).setText(value);
        acroFilledKeys.add(fieldName);
      } catch {
        // Field name not found in AcroForm — will fall through to Strategy B
      }
    }
    form.flatten();
  } catch {
    // No AcroForm in this PDF — all entries fall through to Strategy B
    hasAcroForm = false;
  }

  // Strategy B: stamp any un-filled text field entries directly onto page 1.
  // This guarantees that text fill data is always reflected in the output PDF
  // even when the source PDF has no AcroForm fields (e.g. scanned/flat PDFs).
  const unstampedEntries = Object.entries(textFields).filter(
    ([key]) => !acroFilledKeys.has(key),
  );

  if (unstampedEntries.length > 0) {
    const firstPage = pages[0];
    if (firstPage) {
      const { height: pageHeight } = firstPage.getSize();
      // Start near the top of the page (points from bottom, since PDF Y=0 is bottom)
      const startY = pageHeight - 60;  // 60pt (~0.83 inch) from top
      const lineHeight = 14;
      const labelColor = rgb(0.2, 0.2, 0.2);
      const valueColor = rgb(0.05, 0.05, 0.55);

      unstampedEntries.forEach(([key, value], index) => {
        const y = startY - index * lineHeight;
        if (y < 10) return; // Don't draw below bottom margin

        firstPage.drawText(`${key}: ${value}`, {
          x: 10,
          y,
          size: 10,
          font: helvetica,
          color: hasAcroForm ? valueColor : labelColor,
          // Semi-transparent background note: @cantoo/pdf-lib drawText has no background
          // option; the text is drawn directly over existing content.
        });
      });
    }
  }

  // Draw signature field placeholders (blue rectangle + "Sign Here" label)
  for (const field of sigFields) {
    const page = pages[field.page - 1]; // page is 1-indexed
    if (!page) continue;
    page.drawRectangle({
      x: field.x,
      y: field.y,
      width: field.width,
      height: field.height,
      borderColor: rgb(0.15, 0.39, 0.92),
      borderWidth: 1.5,
      color: rgb(0.90, 0.94, 0.99),
    });
    page.drawText('Sign Here', {
      x: field.x + 4,
      y: field.y + 4,
      size: 8,
      font: helvetica,
      color: rgb(0.15, 0.39, 0.92),
    });
  }

  const modifiedBytes = await pdfDoc.save();

  // Atomic write: tmp → rename (prevents partial writes from corrupting the only copy)
  const tmpPath = `${destPath}.tmp`;
  await writeFile(tmpPath, modifiedBytes);

  // Verify output is a valid PDF (check magic bytes)
  const magic = modifiedBytes.slice(0, 4);
  const header = Buffer.from(magic).toString('ascii');
  if (!header.startsWith('%PDF')) {
    throw new Error('preparePdf: output is not a valid PDF (magic bytes check failed)');
  }

  await rename(tmpPath, destPath);
}
