"use client";
import { useEffect, useRef, useState, useTransition } from "react";
import { createPortal } from "react-dom";
import { useRouter } from "next/navigation";
import { X, Upload, Trash2, Loader2, Move, ZoomIn, ZoomOut, RotateCcw } from "lucide-react";
import { setActressPortraitTransform, clearActressPortrait } from "@/app/actions/actressPortrait";
import type { ActressAllPortraits, PortraitSlotKey } from "@/lib/db/queries";
import { portraitUrl } from "@/lib/assetUrls";

interface Props {
  actressId: number;
  actressName: string;
  initial: ActressAllPortraits;
  initialSlot?: PortraitSlotKey;
  onClose: () => void;
}

const PHI = 1.618;
const FRAME_H = 360;
const PORTRAIT_H = FRAME_H;
const PORTRAIT_W = Math.round(FRAME_H / PHI);
const HORIZ_H = FRAME_H;
const HORIZ_W = Math.round(FRAME_H * PHI);

const SLOT_LABELS: Record<PortraitSlotKey, string> = { "1": "P1", "2": "P2", "3": "P3", "4": "P4", "h": "L" };
const SLOT_KEYS: PortraitSlotKey[] = ["1", "2", "3", "4", "h"];

function slotKey(s: PortraitSlotKey): keyof ActressAllPortraits {
  return s === "h" ? "ph" : (`p${s}` as "p1" | "p2" | "p3" | "p4");
}

export function ActressPortraitEditor({ actressId, actressName, initial, initialSlot = "1", onClose }: Props) {
  const router = useRouter();
  const fileRef = useRef<HTMLInputElement>(null);
  const [slot, setSlot] = useState<PortraitSlotKey>(initialSlot);
  const [slots, setSlots] = useState<ActressAllPortraits>(initial);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [, start] = useTransition();
  const dragRef = useRef<{ x: number; y: number; ox: number; oy: number } | null>(null);

  const cur = slots[slotKey(slot)];
  const isHorizontal = slot === "h";
  const W = isHorizontal ? HORIZ_W : PORTRAIT_W;
  const H = isHorizontal ? HORIZ_H : PORTRAIT_H;

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  useEffect(() => {
    const ACCEPTED = new Set(["image/jpeg", "image/png", "image/webp"]);
    const onPaste = (e: ClipboardEvent) => {
      if (busy) return;
      const items = e.clipboardData?.items;
      if (!items) return;
      let imageItem: DataTransferItem | null = null;
      let unsupported: string | null = null;
      for (const it of items) {
        if (it.kind !== "file") continue;
        if (ACCEPTED.has(it.type)) { imageItem = it; break; }
        if (it.type.startsWith("image/")) unsupported = it.type;
      }
      if (imageItem) {
        e.preventDefault();
        const file = imageItem.getAsFile();
        if (file) uploadFile(file);
      } else if (unsupported) {
        e.preventDefault();
        setError("Unsupported image format — paste JPEG, PNG, or WebP");
      }
    };
    window.addEventListener("paste", onPaste);
    return () => window.removeEventListener("paste", onPaste);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [busy, slot]);

  function patchSlot(patch: Partial<ActressAllPortraits[keyof ActressAllPortraits]>) {
    setSlots((s) => ({ ...s, [slotKey(slot)]: { ...s[slotKey(slot)], ...patch } }));
  }

  async function uploadFile(file: File) {
    setBusy(true);
    setError(null);
    try {
      const fd = new FormData();
      fd.append("file", file);
      const res = await fetch(`/api/actress-portrait/${actressId}?slot=${slot}`, { method: "POST", body: fd });
      if (!res.ok) {
        const j = await res.json().catch(() => ({}));
        throw new Error(j.error ?? `upload failed (${res.status})`);
      }
      const j = await res.json();
      patchSlot({ path: j.portraitPath, zoom: 1, offsetX: 0, offsetY: 0 });
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setBusy(false);
    }
  }

  function onPointerDown(e: React.PointerEvent) {
    if (!cur.path) return;
    (e.target as Element).setPointerCapture(e.pointerId);
    dragRef.current = { x: e.clientX, y: e.clientY, ox: cur.offsetX, oy: cur.offsetY };
  }
  function onPointerMove(e: React.PointerEvent) {
    if (!dragRef.current) return;
    const dx = e.clientX - dragRef.current.x;
    const dy = e.clientY - dragRef.current.y;
    patchSlot({ offsetX: dragRef.current.ox + dx, offsetY: dragRef.current.oy + dy });
  }
  function onPointerUp() { dragRef.current = null; }

  const previewRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const el = previewRef.current;
    if (!el) return;
    const handler = (e: WheelEvent) => {
      if (!cur.path) return;
      e.preventDefault();
      const factor = e.deltaY < 0 ? 1.08 : 1 / 1.08;
      patchSlot({ zoom: Math.max(0.5, Math.min(5, cur.zoom * factor)) });
    };
    el.addEventListener("wheel", handler, { passive: false });
    return () => el.removeEventListener("wheel", handler);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [cur.path, cur.zoom, slot]);

  async function save() {
    setBusy(true);
    try {
      await setActressPortraitTransform(actressId, slot, { zoom: cur.zoom, offsetX: cur.offsetX, offsetY: cur.offsetY });
      router.refresh();
      onClose();
    } finally {
      setBusy(false);
    }
  }

  function reset() { patchSlot({ zoom: 1, offsetX: 0, offsetY: 0 }); }

  async function removePortrait() {
    if (!confirm(`Remove ${SLOT_LABELS[slot]}?`)) return;
    setBusy(true);
    try {
      await clearActressPortrait(actressId, slot);
      patchSlot({ path: null, zoom: 1, offsetX: 0, offsetY: 0 });
      router.refresh();
    } finally {
      setBusy(false);
    }
  }

  if (typeof document === "undefined") return null;
  return createPortal(
    <div
      className="fixed inset-x-0 bottom-0 top-16 z-50 flex items-center justify-center px-4 py-4 fade-in"
      onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}
    >
      <div
        className="bg-[var(--color-bg-0)] rounded-2xl border border-[var(--color-glass-border)] shadow-2xl p-4"
        style={{ width: `min(${isHorizontal ? 800 : 480}px, calc(100vw - 32px))` }}
      >
        <div className="flex items-center justify-between mb-3">
          <div className="min-w-0">
            <div className="text-[10px] uppercase tracking-wider font-mono text-[var(--color-fg-muted)]">Portraits</div>
            <div className="text-base font-medium truncate">{actressName}</div>
          </div>
          <button onClick={onClose} className="text-[var(--color-fg-muted)] hover:text-[var(--color-fg)] shrink-0">
            <X className="w-5 h-5" />
          </button>
        </div>

        <div className="flex items-center gap-1 mb-3">
          {SLOT_KEYS.map((k) => {
            const active = k === slot;
            const has = !!slots[slotKey(k)].path;
            return (
              <button
                key={k}
                type="button"
                onClick={() => setSlot(k)}
                className={`flex items-center gap-1.5 text-xs font-mono font-semibold px-3 py-1.5 rounded-lg border transition-colors ${
                  active ? "bg-[var(--color-cyan)] text-black border-transparent" : "glass glass-hover"
                }`}
              >
                {SLOT_LABELS[k]}
                <span className={`w-1.5 h-1.5 rounded-full ${has ? "bg-emerald-400" : "bg-white/20"}`} />
              </button>
            );
          })}
        </div>

        <div className="flex gap-4 items-stretch">
          {cur.path ? (
            <div
              ref={previewRef}
              className="relative shrink-0 rounded-xl overflow-hidden bg-black/30 select-none border-2 border-dashed"
              style={{
                width: W,
                height: H,
                cursor: "grab",
                containerType: "inline-size",
                borderColor: "color-mix(in oklch, var(--color-violet) 60%, transparent)",
              }}
              onPointerDown={onPointerDown}
              onPointerMove={onPointerMove}
              onPointerUp={onPointerUp}
              onPointerCancel={onPointerUp}
            >
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src={cur.path ? portraitUrl({ path: cur.path, slot }) : ""}
                alt=""
                draggable={false}
                className="absolute top-1/2 left-1/2 max-w-none origin-center pointer-events-none border-2 border-dashed box-border"
                style={{
                  transform: `translate(-50%, -50%) translate(${cur.offsetX / W * 100}cqw, ${cur.offsetY / W * 100}cqw) scale(${cur.zoom})`,
                  width: "100cqw",
                  height: "auto",
                  borderColor: "color-mix(in oklch, var(--color-cyan) 70%, transparent)",
                }}
              />
              <div className="absolute bottom-1.5 left-1/2 -translate-x-1/2 text-[9px] uppercase tracking-wider font-mono text-white flex items-center gap-1 pointer-events-none whitespace-nowrap px-1.5 py-0.5 rounded-md bg-black/70 backdrop-blur-sm">
                <Move className="w-3 h-3" /> drag · scroll
              </div>
            </div>
          ) : (
            <button
              type="button"
              onClick={() => fileRef.current?.click()}
              className="shrink-0 rounded-xl border border-dashed border-[var(--color-glass-border-strong)] text-center text-sm text-[var(--color-fg-muted)] hover:border-[var(--color-cyan)] hover:text-[var(--color-fg-dim)] transition-colors flex flex-col items-center justify-center gap-2"
              style={{ width: W, height: H }}
            >
              <Upload className="w-6 h-6" />
              Click Or Paste Image
            </button>
          )}

          <div className="flex-1 min-w-0 flex flex-col justify-between">
            <div className="space-y-2.5">
              <div className="flex items-center gap-2">
                <ZoomOut className="w-4 h-4 text-[var(--color-fg-muted)] shrink-0" />
                <input
                  type="range"
                  min={0.5}
                  max={5}
                  step={0.01}
                  value={cur.zoom}
                  onChange={(e) => patchSlot({ zoom: Number(e.target.value) })}
                  className="flex-1 accent-[var(--color-cyan)] min-w-0"
                  disabled={!cur.path}
                />
                <ZoomIn className="w-4 h-4 text-[var(--color-fg-muted)] shrink-0" />
              </div>
              <div className="text-[10px] font-mono text-[var(--color-fg-dim)] tabular-nums text-right -mt-1">
                {cur.zoom.toFixed(2)}x
              </div>

              <div className="flex items-center gap-1.5">
                <button
                  type="button"
                  onClick={() => fileRef.current?.click()}
                  disabled={busy}
                  className="flex-1 flex items-center justify-center gap-1.5 text-xs px-2 py-1.5 rounded-lg glass glass-hover disabled:opacity-50"
                >
                  {busy ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Upload className="w-3.5 h-3.5" />}
                  {cur.path ? "Replace" : "Upload"}
                </button>
                <button
                  type="button"
                  onClick={reset}
                  disabled={!cur.path || busy}
                  title="Reset zoom & position"
                  className="flex items-center justify-center gap-1.5 text-xs px-2 py-1.5 rounded-lg glass glass-hover disabled:opacity-40"
                >
                  <RotateCcw className="w-3.5 h-3.5" />
                </button>
                {cur.path && (
                  <button
                    type="button"
                    onClick={removePortrait}
                    disabled={busy}
                    title="Remove"
                    className="flex items-center justify-center gap-1.5 text-xs px-2 py-1.5 rounded-lg border border-red-500/30 text-red-300 hover:bg-red-500/10 disabled:opacity-40"
                  >
                    <Trash2 className="w-3.5 h-3.5" />
                  </button>
                )}
                <input
                  ref={fileRef}
                  type="file"
                  accept="image/jpeg,image/png,image/webp"
                  hidden
                  onChange={(e) => { const f = e.target.files?.[0]; if (f) uploadFile(f); e.target.value = ""; }}
                />
              </div>

              {error && <div className="text-xs text-red-400">{error}</div>}
            </div>

            <div className="flex items-center gap-2 pt-3 border-t border-[var(--color-glass-border)]">
              <button
                type="button"
                onClick={onClose}
                className="flex-1 text-xs px-3 py-1.5 rounded-lg glass glass-hover"
              >
                Cancel
              </button>
              <button
                type="button"
                onClick={save}
                disabled={busy || !cur.path}
                className="flex-1 flex items-center justify-center gap-1.5 text-xs px-3 py-1.5 rounded-lg bg-[var(--color-cyan)] text-black font-medium disabled:opacity-50"
              >
                {busy ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : null} Save
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>,
    document.body,
  );
}
