import { PDFDocument, StandardFonts, degrees, rgb } from "pdf-lib";
import type { ToolResult } from "../../core/io/output-types";
import type { ToolProgress } from "../../core/plugins/plugin-types";

export type WatermarkOrientation = "horizontal" | "vertical" | "diagonal";

export interface PdfWatermarkOptions {
  text: string;
  orientation: WatermarkOrientation;
  /** Opacity from 0 (invisible) to 100 (fully opaque). */
  opacity: number;
  /** Preset color key, see COLOR_PRESETS. */
  color: string;
}

export const MAX_WATERMARK_CHARS = 60;

export const COLOR_PRESETS: Record<string, { label: string; rgb: [number, number, number] }> = {
  "light-grey": { label: "Light Grey", rgb: [0.75, 0.75, 0.75] },
  grey: { label: "Grey", rgb: [0.5, 0.5, 0.5] },
  black: { label: "Black", rgb: [0, 0, 0] },
  red: { label: "Red", rgb: [0.86, 0.21, 0.21] },
  blue: { label: "Blue", rgb: [0.23, 0.4, 0.85] },
  green: { label: "Green", rgb: [0.2, 0.6, 0.33] },
};

/** Fraction of the page the watermark may occupy, leaving a safe margin. */
const FIT_MARGIN = 0.82;

/**
 * Compute the largest font size at which the rotated text still fits inside the
 * page, so the watermark is always shown in full without touching the edges.
 *
 * A line of text rotated by `angle` occupies an axis-aligned bounding box of:
 *   width  = |textW·cos| + |textH·sin|
 *   height = |textW·sin| + |textH·cos|
 * Both textW and textH scale linearly with font size, so we measure at a probe
 * size and scale down to satisfy both the width and height constraints.
 */
function fitFontSize(
  measureWidth: (size: number) => number,
  measureHeight: (size: number) => number,
  pageW: number,
  pageH: number,
  angleRad: number
): number {
  const probe = 100;
  const w = measureWidth(probe);
  const h = measureHeight(probe);
  if (w <= 0 || h <= 0) return 12;

  const cos = Math.abs(Math.cos(angleRad));
  const sin = Math.abs(Math.sin(angleRad));

  const bboxW = w * cos + h * sin;
  const bboxH = w * sin + h * cos;

  const scaleW = (pageW * FIT_MARGIN) / bboxW;
  const scaleH = (pageH * FIT_MARGIN) / bboxH;

  const size = probe * Math.min(scaleW, scaleH);
  return Math.max(6, Math.min(size, 300));
}

export async function applyWatermark(
  fileName: string,
  buffer: ArrayBuffer | Uint8Array,
  options: PdfWatermarkOptions,
  reportProgress?: (progress: ToolProgress) => void
): Promise<ToolResult> {
  const text = (options.text ?? "").trim().slice(0, MAX_WATERMARK_CHARS);
  if (!text) {
    throw new Error("Watermark text is required.");
  }

  const pdfDoc = await PDFDocument.load(
    buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer)
  );
  const font = await pdfDoc.embedFont(StandardFonts.HelveticaBold);

  const preset = COLOR_PRESETS[options.color] ?? COLOR_PRESETS["light-grey"];
  const [r, g, b] = preset.rgb;
  const opacity = Math.max(0, Math.min(1, options.opacity / 100));

  const pages = pdfDoc.getPages();
  const rotation =
    options.orientation === "vertical"
      ? 90
      : options.orientation === "diagonal"
        ? 45
        : 0;

  for (let i = 0; i < pages.length; i++) {
    const page = pages[i];
    const { width, height } = page.getSize();

    const angle = (rotation * Math.PI) / 180;

    const fontSize = fitFontSize(
      (s) => font.widthOfTextAtSize(text, s),
      (s) => font.heightAtSize(s),
      width,
      height,
      angle
    );
    const textWidth = font.widthOfTextAtSize(text, fontSize);
    const textHeight = font.heightAtSize(fontSize);

    // Place so the (rotated) text is centered on the page.
    const cx = width / 2;
    const cy = height / 2;
    const x = cx - (textWidth / 2) * Math.cos(angle) + (textHeight / 2) * Math.sin(angle);
    const y = cy - (textWidth / 2) * Math.sin(angle) - (textHeight / 2) * Math.cos(angle);

    page.drawText(text, {
      x,
      y,
      size: fontSize,
      font,
      color: rgb(r, g, b),
      opacity,
      rotate: degrees(rotation),
    });

    reportProgress?.({
      percentage: ((i + 1) / pages.length) * 100,
      message: `Watermarking page ${i + 1}/${pages.length}...`,
    });
  }

  const bytes = await pdfDoc.save();
  const blob = new Blob([bytes as unknown as BlobPart], {
    type: "application/pdf",
  });

  const outName = fileName.replace(/\.pdf$/i, "") + "_watermarked.pdf";

  return {
    type: "files",
    files: [
      {
        name: outName,
        mimeType: "application/pdf",
        blob,
        sizeAfter: blob.size,
      },
    ],
  };
}
