"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { Type, Square, Pencil, Trash2, Loader2, Save, ChevronLeft, ChevronRight } from "lucide-react";
import { loadPdfLib, renderPdfPage, canvasToBlob, downloadBlob } from "@/lib/files";
import type { LText } from "@/lib/i18n";
import { useApp } from "@/components/app-context";
import { t } from "@/lib/i18n";

type Tool = "text" | "rect" | "draw";

interface Item {
  id: number;
  type: Tool;
  page: number;
  /** normalized 0..1 relative to page canvas */
  x: number;
  y: number;
  w?: number;
  h?: number;
  text?: string;
  color: string;
  size: number;
  points?: { x: number; y: number }[];
  dataUrl?: string; // for stamp/sign images
}

export function PdfEditor({ file }: { file: File }) {
  const { T, lang } = useApp();
  const [pageCount, setPageCount] = useState(0);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [tool, setTool] = useState<Tool>("text");
  const [color, setColor] = useState("#dc2626");
  const [items, setItems] = useState<Item[]>([]);
  const [selected, setSelected] = useState<number | null>(null);
  const [editing, setEditing] = useState<number | null>(null);
  const canvasHostRef = useRef<HTMLDivElement>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const drawingRef = useRef<Item | null>(null);
  const dataRef = useRef<ArrayBuffer | null>(null);
  const [error, setError] = useState("");

  const TL = (x: keyof typeof t): string => (t[x] as LText)[lang];

  // load pdf
  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        setLoading(true);
        const buf = await file.arrayBuffer();
        dataRef.current = buf;
        const pdfjs = await import("pdfjs-dist");
        pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs";
        const doc = await pdfjs.getDocument({ data: buf.slice(0) }).promise;
        if (!cancelled) {
          setPageCount(doc.numPages);
          setLoading(false);
        }
      } catch {
        if (!cancelled) {
          setError("Could not open this PDF.");
          setLoading(false);
        }
      }
    })();
    return () => { cancelled = true; };
  }, [file]);

  // render page
  const renderPage = useCallback(async () => {
    if (!dataRef.current || !canvasHostRef.current) return;
    setLoading(true);
    try {
      const host = canvasHostRef.current;
      const wrap = host?.parentElement;
      const targetW = Math.min(820, Math.max(320, (wrap?.clientWidth ?? 700) - 32));
      const { canvas } = await renderPdfPage(dataRef.current, page, targetW);
      canvas.style.maxWidth = "100%";
      canvas.style.display = "block";
      canvas.className = "rounded-lg shadow-sm";
      host.replaceChildren(canvas);
      canvasRef.current = canvas;
    } catch {
      setError("Render failed");
    }
    setLoading(false);
  }, [page]);

  useEffect(() => {
    if (pageCount) renderPage();
  }, [renderPage, pageCount]);

  const toNorm = (e: React.MouseEvent | MouseEvent) => {
    const canvas = canvasRef.current;
    if (!canvas) return null;
    const r = canvas.getBoundingClientRect();
    return {
      x: Math.min(1, Math.max(0, ((e as MouseEvent).clientX - r.left) / r.width)),
      y: Math.min(1, Math.max(0, ((e as MouseEvent).clientY - r.top) / r.height)),
      rect: r,
    };
  };

  const onMouseDown = (e: React.MouseEvent) => {
    if (editing !== null) return;
    const pos = toNorm(e);
    if (!pos) return;
    const id = Date.now() + Math.floor(Math.random() * 1000);
    if (tool === "text") {
      setItems((arr) => [...arr, { id, type: "text", page, x: pos.x, y: pos.y, text: "", color, size: 16 }]);
      setSelected(id);
      setEditing(id);
      return;
    }
    if (tool === "rect") {
      drawingRef.current = { id, type: "rect", page, x: pos.x, y: pos.y, w: 0.01, h: 0.01, color, size: 2 };
    }
    if (tool === "draw") {
      drawingRef.current = { id, type: "draw", page, x: pos.x, y: pos.y, points: [{ x: pos.x, y: pos.y }], color, size: 3 };
    }
    setSelected(id);
  };

  const onMouseMove = (e: React.MouseEvent) => {
    const d = drawingRef.current;
    if (!d) return;
    const pos = toNorm(e);
    if (!pos) return;
    if (d.type === "rect") {
      d.w = Math.abs(pos.x - d.x);
      d.h = Math.abs(pos.y - d.y);
      d.x = Math.min(pos.x, d.x);
      d.y = Math.min(pos.y, d.y);
    } else if (d.points) {
      d.points.push({ x: pos.x, y: pos.y });
    }
    setItems((arr) => {
      const exists = arr.find((i) => i.id === d.id);
      if (exists) return arr.map((i) => (i.id === d.id ? { ...d } : i));
      return [...arr, { ...d }];
    });
  };

  const onMouseUp = () => {
    const d = drawingRef.current;
    drawingRef.current = null;
    if (d?.type === "draw" && (d.points?.length ?? 0) < 2) {
      setItems((arr) => arr.filter((i) => i.id !== d.id));
    }
  };

  const deleteSelected = () => {
    if (selected === null) return;
    setItems((arr) => arr.filter((i) => i.id !== selected));
    setSelected(null);
  };

  const savePdf = async () => {
    if (!dataRef.current) return;
    setSaving(true);
    try {
      const pdfLib = await loadPdfLib();
      const doc = await pdfLib.PDFDocument.load(dataRef.current.slice(0), { ignoreEncryption: true });
      const pages = doc.getPages();

      for (const item of items) {
        const p = pages[Math.min(pages.length, item.page) - 1];
        if (!p) continue;
        const { width, height } = p.getSize();
        if (item.type === "text" && item.text) {
          const fontSize = (item.size / (canvasRef.current?.width ?? 800)) * width;
          const font = await doc.embedFont("Helvetica");
          p.drawText(item.text, {
            x: item.x * width,
            y: height - item.y * height - fontSize,
            size: fontSize,
            font,
            color: hexToRgb(item.color),
          });
        } else if (item.type === "rect") {
          p.drawRectangle({
            x: item.x * width,
            y: height - (item.y + (item.h ?? 0)) * height,
            width: (item.w ?? 0) * width,
            height: (item.h ?? 0) * height,
            borderColor: hexToRgb(item.color),
            borderWidth: item.size,
          });
        } else if (item.type === "draw" && item.points && item.points.length > 1) {
          const cw = 1000;
          const baseCanvas = canvasRef.current;
          const ch = Math.round(((baseCanvas?.height ?? 1000) / (baseCanvas?.width ?? 800)) * cw);
          const oc = document.createElement("canvas");
          oc.width = cw;
          oc.height = ch;
          const ctx = oc.getContext("2d")!;
          ctx.strokeStyle = item.color;
          ctx.lineWidth = Math.max(2, item.size * 2);
          ctx.lineJoin = "round";
          ctx.lineCap = "round";
          ctx.beginPath();
          item.points.forEach((pt, i) => {
            const x = pt.x * cw;
            const y = pt.y * ch;
            if (i === 0) ctx.moveTo(x, y);
            else ctx.lineTo(x, y);
          });
          ctx.stroke();
          const blob = await canvasToBlob(oc, "image/png");
          const img = await doc.embedPng(await blob.arrayBuffer());
          const minX = Math.min(...item.points.map((p) => p.x));
          const maxX = Math.max(...item.points.map((p) => p.x));
          const minY = Math.min(...item.points.map((p) => p.y));
          const maxY = Math.max(...item.points.map((p) => p.y));
          p.drawImage(img, {
            x: minX * width,
            y: height - maxY * height,
            width: Math.max(1, (maxX - minX) * width),
            height: Math.max(1, (maxY - minY) * height),
          });
        } else if (item.type === "draw" && item.dataUrl) {
          const img = await doc.embedPng(item.dataUrl);
          const w = (item.w ?? 0.3) * width;
          const h = (w / img.width) * img.height;
          p.drawImage(img, { x: item.x * width, y: height - item.y * height - h, width: w, height: h });
        }
      }

      const bytes = await doc.save();
      downloadBlob(new Blob([bytes as BlobPart], { type: "application/pdf" }), file.name.replace(/\.pdf$/i, "") + "-edited.pdf");
    } catch (err) {
      console.error(err);
      setError("Save failed — try again.");
    }
    setSaving(false);
  };

  const pageItems = items.filter((i) => i.page === page);

  return (
    <div className="flex h-full min-h-0 flex-col lg:flex-row">
      {/* toolbar */}
      <div className="flex shrink-0 flex-row flex-wrap items-center gap-2 border-b border-zinc-200 bg-zinc-50/70 px-4 py-2.5 dark:border-white/10 dark:bg-white/5 lg:w-56 lg:flex-col lg:items-stretch lg:border-b-0 lg:border-r lg:py-4">
        <div className="hidden text-[11px] font-extrabold uppercase tracking-wider text-zinc-400 lg:block">Tools</div>
        {([
          ["text", Type, TL("edAddText")],
          ["rect", Square, TL("edAddShape")],
          ["draw", Pencil, TL("edDraw")],
        ] as const).map(([id, Icon, label]) => (
          <button
            key={id}
            onClick={() => { setTool(id); setEditing(null); }}
            className={`flex items-center gap-2 rounded-lg px-3 py-2 text-[13.5px] font-bold transition-colors ${tool === id ? "bg-[#0b7a43] text-white" : "bg-white text-zinc-700 ring-1 ring-zinc-200 hover:bg-zinc-50 dark:bg-white/5 dark:text-zinc-200 dark:ring-white/10"}`}
          >
            <Icon className="h-4 w-4" /> {label}
          </button>
        ))}
        <div className="flex items-center gap-2 px-1">
          <input type="color" value={color} onChange={(e) => setColor(e.target.value)} className="h-8 w-9 cursor-pointer rounded border border-zinc-200 bg-transparent dark:border-white/15" aria-label="colour" />
        </div>
        <button
          onClick={deleteSelected}
          disabled={selected === null}
          className="flex items-center gap-2 rounded-lg bg-white px-3 py-2 text-[13.5px] font-bold text-rose-600 ring-1 ring-zinc-200 transition-colors hover:bg-rose-50 disabled:opacity-40 dark:bg-white/5 dark:ring-white/10"
        >
          <Trash2 className="h-4 w-4" /> {TL("edDelete")}
        </button>
        <div className="lg:mt-auto" />
        <button
          onClick={savePdf}
          disabled={saving || loading}
          className="flex items-center justify-center gap-2 rounded-lg bg-[#0b7a43] px-3 py-2.5 text-[13.5px] font-bold text-white shadow-sm transition-colors hover:bg-[#096c3b] disabled:opacity-50"
        >
          {saving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
          {saving ? T("running") : TL("edSave")}
        </button>
      </div>

      {/* canvas area */}
      <div className="flex min-h-0 flex-1 flex-col bg-zinc-100 dark:bg-black/30">
        <div className="flex items-center justify-center gap-3 border-b border-zinc-200 bg-white/60 px-4 py-2 text-[13px] font-semibold text-zinc-600 dark:border-white/10 dark:text-zinc-300">
          <button onClick={() => setPage((p) => Math.max(1, p - 1))} disabled={page <= 1} className="rounded-md p-1 hover:bg-zinc-100 disabled:opacity-30 dark:hover:bg-white/10" aria-label="previous page">
            <ChevronLeft className="h-4 w-4" />
          </button>
          {T("page")} {page} {T("of")} {pageCount}
          <button onClick={() => setPage((p) => Math.min(pageCount, p + 1))} disabled={page >= pageCount} className="rounded-md p-1 hover:bg-zinc-100 disabled:opacity-30 dark:hover:bg-white/10" aria-label="next page">
            <ChevronRight className="h-4 w-4" />
          </button>
          <span className="hidden text-[12px] font-normal text-zinc-400 sm:inline">· {TL("edHint")}</span>
        </div>
        <div className="min-h-0 flex-1 overflow-auto p-4" onMouseDown={(e) => { if (e.target === e.currentTarget) setSelected(null); }}>
          {error ? (
            <div className="mx-auto mt-10 max-w-md rounded-xl bg-rose-50 p-5 text-center text-[14px] font-semibold text-rose-700">{error}</div>
          ) : (
            <div className="relative mx-auto w-fit select-none" onMouseDown={onMouseDown} onMouseMove={onMouseMove} onMouseUp={onMouseUp} onMouseLeave={onMouseUp}>
              <div ref={canvasHostRef} />
              {loading && (
                <div className="flex h-72 w-[560px] max-w-[80vw] items-center justify-center rounded-lg bg-white dark:bg-white/5">
                  <Loader2 className="h-7 w-7 animate-spin text-emerald-600" />
                </div>
              )}
              {!loading && pageItems.map((item) => {
                const canvas = canvasRef.current;
                if (!canvas) return null;
                const style: React.CSSProperties = {
                  left: `${item.x * 100}%`,
                  top: `${item.y * 100}%`,
                  position: "absolute",
                };
                if (item.type === "text") {
                  return (
                    <div
                      key={item.id}
                      style={{ ...style, color: item.color, fontSize: `${item.size}px`, minWidth: "24px", position: "absolute" }}
                      className={`cursor-move rounded border px-1 leading-snug ${selected === item.id ? "border-emerald-500 bg-emerald-50/40" : "border-transparent hover:border-emerald-300"}`}
                      contentEditable={editing === item.id}
                      suppressContentEditableWarning
                      onDoubleClick={() => { setEditing(item.id); setSelected(item.id); }}
                      onBlur={(e) => {
                        setEditing(null);
                        const txt = e.currentTarget.textContent ?? "";
                        setItems((arr) => arr.map((i) => (i.id === item.id ? { ...i, text: txt } : i)));
                      }}
                      draggable={!editing}
                      onDragEnd={(e) => {
                        const hostRect = canvasRef.current!.getBoundingClientRect();
                        setItems((arr) => arr.map((i) => i.id === item.id
                          ? { ...i, x: Math.min(0.95, Math.max(0, (e.clientX - hostRect.left) / hostRect.width)), y: Math.min(1, Math.max(0, (e.clientY - hostRect.top) / hostRect.height)) }
                          : i));
                      }}
                    >
                      {item.text || <span className="text-zinc-400">{TL("edTextPrompt")}</span>}
                    </div>
                  );
                }
                if (item.type === "rect") {
                  return (
                    <div
                      key={item.id}
                      style={{ ...style, position: "absolute", width: `${(item.w ?? 0) * 100}%`, height: `${(item.h ?? 0) * 100}%`, border: `${item.size}px solid ${item.color}` }}
                      className={`cursor-move rounded-sm ${selected === item.id ? "outline outline-2 outline-emerald-500" : ""}`}
                      onClick={() => setSelected(item.id)}
                    />
                  );
                }
                if (item.type === "draw" && item.points) {
                  const cw = canvas.width;
                  const ch = canvas.height;
                  const path = item.points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x * cw} ${p.y * ch}`).join(" ");
                  return (
                    <svg key={item.id} style={{ left: 0, top: 0, position: "absolute", pointerEvents: "none" }} width={cw} height={ch} className="absolute inset-0">
                      <path d={path} stroke={item.color} strokeWidth={item.size * 1.5} fill="none" strokeLinecap="round" strokeLinejoin="round" />
                    </svg>
                  );
                }
                return null;
              })}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

export function hexToRgb(hex: string): { type: "RGB"; red: number; green: number; blue: number } {
  const m = hex.replace("#", "");
  const v = m.length === 3 ? m.split("").map((c) => c + c).join("") : m;
  return {
    type: "RGB",
    red: parseInt(v.slice(0, 2), 16) / 255,
    green: parseInt(v.slice(2, 4), 16) / 255,
    blue: parseInt(v.slice(4, 6), 16) / 255,
  };
}
