"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  X, ArrowLeft, UploadCloud, Loader2, Download, FileDown, RotateCcw, Copy, FileText, Archive, CheckCircle2,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Checkbox } from "@/components/ui/checkbox";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useApp } from "@/components/app-context";
import { catById, toolById } from "@/data/tools";
import { getToolDef, isAiTool } from "@/lib/tool-registry";
import type { Field } from "@/lib/tool-registry";
import { downloadBlob, fmtBytes, type OutFile, type RunnerOutput } from "@/lib/files";
import { PdfEditor } from "@/components/tools/pdf-editor";
import { SignPad, PdfViewer, ColorPickerTool } from "@/components/tools/special-tools";
import { PDFDocument } from "pdf-lib";

type Phase = "idle" | "working" | "done";

export function ToolWorkbench() {
  const { tool, closeTool, openTool, lang, T, presetFile } = useApp();
  const meta = tool ? toolById[tool] : null;
  const def = tool ? getToolDef(tool) : undefined;

  const [files, setFiles] = useState<File[]>([]);
  const [text, setText] = useState("");
  const [options, setOptions] = useState<Record<string, unknown>>({});
  const [phase, setPhase] = useState<Phase>("idle");
  const [progress, setProgress] = useState("");
  const [result, setResult] = useState<RunnerOutput | null>(null);
  const [error, setError] = useState("");
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const [inputPreview, setInputPreview] = useState<string | null>(null);
  const [previewPage, setPreviewPage] = useState(1);
  const inputRef = useRef<HTMLInputElement>(null);
  const bodyRef = useRef<HTMLDivElement>(null);

  // init on open
  useEffect(() => {
    setFiles(presetFile ? [presetFile] : []);
    setText("");
    setResult(null);
    setError("");
    setPhase("idle");
    setProgress("");
    setPreviewPage(1);
    setPreviewUrl(null);
    if (def) {
      const init: Record<string, unknown> = {};
      def.fields.forEach((f) => { init[f.key] = f.def ?? (f.type === "checkbox" ? false : ""); });
      setOptions(init);
    }
    bodyRef.current?.scrollTo({ top: 0 });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [tool, presetFile]);

  const isCustom = def?.custom;
  const needsInput = (def?.needsText || !isCustom) && !result;
  const canRun = isCustom ? false : def?.needsText ? text.trim().length > 0 || files.length > 0 : files.length > 0;

  const addFiles = useCallback((fl: FileList | null) => {
    if (!fl?.length) return;
    setError("");
    setFiles((prev) => (def?.multiple ? [...prev, ...Array.from(fl)] : [fl[0]]));
    setResult(null);
    setPhase("idle");
  }, [def?.multiple]);

  const run = async () => {
    if (!tool || !def) return;
    setError("");
    setPhase("working");
    setResult(null);
    setProgress("");
    try {
      if (isAiTool(tool)) {
        if (!text.trim()) throw new Error(T("aiEmpty"));
        const res = await fetch("/api/ai", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ tool, text, target: options.target, lang }),
        });
        if (!res.ok) throw new Error(T("aiFailed"));
        const data = (await res.json()) as { text: string };
        setResult({ files: [{ name: `${tool}.txt`, blob: new Blob([data.text], { type: "text/plain" }) }], text: data.text });
      } else {
        const out = await def.run({ files, text, options, lang, onProgress: (m) => setProgress(m) });
        if (!out.files.length && !out.report?.length && !out.text) throw new Error(T("errGeneric"));
        setResult(out);
      }
      setPhase("done");
    } catch (e) {
      const msg = e instanceof Error ? e.message : "";
      setError(
        msg === "no-pdf" ? (lang === "bn" ? "কোনো পিডিএফ পাওয়া যায়নি।" : "No PDF file found.") :
        msg === "empty" ? (lang === "bn" ? "আগে কিছু লেখা দিন।" : "Please enter some text first.") :
        msg === "need-two" ? (lang === "bn" ? "দুটি ফাইল বা দুই অংশ লাগবে।" : "Two files (or two texts) are needed.") :
        msg === "no-image" ? (lang === "bn" ? "দ্বিতীয় ফাইল হিসেবে একটি ছবি দিন।" : "Add an image as the second file.") :
        msg === "empty-zip" ? (lang === "bn" ? "জিপ ফাঁকা।" : "The ZIP is empty.") :
        msg === "bad-json" ? (lang === "bn" ? "JSON ভালো না।" : "Invalid JSON.") :
        msg === "bad-color" ? (lang === "bn" ? "রঙের ফরম্যাট ঠিক না।" : "Invalid colour.") :
        msg === "bad-date" ? (lang === "bn" ? "তারিখ বোঝা যায়নি।" : "Could not parse the date.") :
        msg === "no-pairs" ? (lang === "bn" ? "key: value লাইন পাওয়া যায়নি।" : "No key: value lines found.") :
        msg === "all-blank" ? (lang === "bn" ? "সব পেজই ফাঁকা মনে হচ্ছে।" : "Every page looks blank.") :
        T("errGeneric"),
      );
      setPhase("idle");
    }
  };

  const downloadAll = async () => {
    if (!result?.files.length) return;
    if (result.files.length === 1) return downloadBlob(result.files[0].blob, result.files[0].name);
    const JSZip = (await import("jszip")).default;
    const zip = new JSZip();
    result.files.forEach((f) => zip.file(f.name, f.blob));
    downloadBlob(await zip.generateAsync({ type: "blob" }), "filedeck-results.zip");
  };

  // preview for single image result
  useEffect(() => {
    if (result?.files.length === 1 && result.files[0].blob.type.startsWith("image/")) {
      const url = URL.createObjectURL(result.files[0].blob);
      setPreviewUrl(url);
      return () => URL.revokeObjectURL(url);
    }
    setPreviewUrl(null);
  }, [result]);

  // input preview (image objectURL or pdf page-1 dataURL)
  useEffect(() => {
    let revoke: string | null = null;
    let cancelled = false;
    (async () => {
      if (!files[0] || phase !== "idle") { setInputPreview(null); return; }
      if (def?.preview === "image" && files[0].type.startsWith("image/")) {
        revoke = URL.createObjectURL(files[0]);
        if (!cancelled) setInputPreview(revoke);
      } else if (def?.preview === "pdf" && (files[0].type === "application/pdf" || files[0].name.toLowerCase().endsWith(".pdf"))) {
        try {
          const { renderPdfPage } = await import("@/lib/files");
          const buf = await files[0].arrayBuffer();
          const { canvas } = await renderPdfPage(buf, 1, 420);
          if (!cancelled) setInputPreview(canvas.toDataURL("image/png"));
        } catch { if (!cancelled) setInputPreview(null); }
      } else {
        if (!cancelled) setInputPreview(null);
      }
    })();
    return () => {
      cancelled = true;
      if (revoke) URL.revokeObjectURL(revoke);
    };
  }, [def?.preview, files, phase]);

  if (!tool || !meta || !def) return null;

  const cat = catById[meta.cat];
  const Name = meta.name[lang];

  return (
    <div className="fixed inset-0 z-50 flex flex-col bg-white dark:bg-[#0c1512]">
      {/* header */}
      <div className="flex h-[60px] shrink-0 items-center gap-3 border-b border-zinc-200 px-4 dark:border-white/10 sm:px-6">
        <button onClick={closeTool} className="flex h-9 items-center gap-1.5 rounded-lg px-2.5 text-[13.5px] font-bold text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-white/10" aria-label={T("back")}>
          <ArrowLeft className="h-4 w-4" />
          <span className="hidden sm:inline">{T("back")}</span>
        </button>
        <span className={`flex h-9 w-9 items-center justify-center rounded-xl ${cat.chip}`}>
          <FileText className="h-4.5 w-4.5" />
        </span>
        <div className="min-w-0">
          <div className="truncate text-[15.5px] font-extrabold text-zinc-900 dark:text-white">{Name}</div>
          <div className="text-[11.5px] text-zinc-400">{cat.name[lang]}</div>
        </div>
        <button onClick={closeTool} className="ml-auto flex h-9 w-9 items-center justify-center rounded-full text-zinc-500 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-white/10" aria-label={T("close")}>
          <X className="h-5 w-5" />
        </button>
      </div>

      {isCustom === "editor" ? (
        files[0] ? <PdfEditor file={files[0]} /> : (
          <DropArea accept={def.accept} onFiles={addFiles} label={T("dropHereOne")} onBrowse={() => inputRef.current?.click()} />
        )
      ) : isCustom === "sign" ? (
        files[0] ? <SignPad file={files[0]} /> : (
          <DropArea accept={def.accept} onFiles={addFiles} label={T("dropHereOne")} onBrowse={() => inputRef.current?.click()} />
        )
      ) : isCustom === "view" ? (
        files[0] ? <PdfViewer file={files[0]} /> : (
          <DropArea accept={def.accept} onFiles={addFiles} label={T("dropHereOne")} onBrowse={() => inputRef.current?.click()} />
        )
      ) : isCustom === "picker" ? (
        files[0] ? <ColorPickerTool file={files[0]} /> : (
          <DropArea accept={def.accept} onFiles={addFiles} label={T("dropHereOne")} onBrowse={() => inputRef.current?.click()} />
        )
      ) : (
        <div ref={bodyRef} className="min-h-0 flex-1 overflow-y-auto">
          <div className="mx-auto grid max-w-5xl gap-6 p-4 sm:p-6 lg:grid-cols-[1fr_360px]">
            {/* left: input + result */}
            <div className="space-y-4">
              {def.needsText && (
                <div className="space-y-2">
                  {isAiTool(tool) && (
                    <div className="rounded-xl border border-amber-200 bg-amber-50 p-3 text-[12.5px] font-medium leading-5 text-amber-800 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200">
                      ☝️ {T("aiNotice")}
                    </div>
                  )}
                  <label className="text-[13px] font-bold text-zinc-700 dark:text-zinc-200">{T("aiInput")}</label>
                  <textarea
                    value={text}
                    onChange={(e) => setText(e.target.value)}
                    placeholder={def.textPlaceholder?.[lang]}
                    rows={def.needsText ? 9 : 4}
                    className="w-full resize-y rounded-xl border border-zinc-200 bg-white p-3.5 text-[14px] leading-6 text-zinc-800 outline-none transition-colors placeholder:text-zinc-400 focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20 dark:border-white/15 dark:bg-white/5 dark:text-zinc-100"
                  />
                </div>
              )}

              {!def.needsText || def.accept ? (
                <DropArea accept={def.accept} multiple={def.multiple} onFiles={addFiles} onBrowse={() => inputRef.current?.click()} label={def.multiple ? T("dropHere") : T("dropHereOne")} compact={files.length > 0} />
              ) : null}

              {inputPreview && phase === "idle" && (
                <div className="flex justify-center rounded-xl border border-zinc-100 bg-zinc-50/60 p-3 dark:border-white/10 dark:bg-white/[0.03]">
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img src={inputPreview} alt="input preview" className="max-h-64 rounded-lg shadow-sm" />
                </div>
              )}

              {files.length > 0 && (
                <ul className="space-y-1.5">
                  {files.map((f, i) => (
                    <li key={i} className="flex items-center gap-3 rounded-xl border border-zinc-100 bg-white px-3.5 py-2.5 dark:border-white/10 dark:bg-white/5">
                      <span className="flex h-8 w-8 items-center justify-center rounded-lg bg-emerald-50 dark:bg-emerald-500/10">
                        <FileText className="h-4 w-4 text-emerald-700 dark:text-emerald-400" />
                      </span>
                      <span className="min-w-0 flex-1">
                        <span className="block truncate text-[13.5px] font-semibold text-zinc-800 dark:text-zinc-100">{f.name}</span>
                        <span className="text-[11.5px] text-zinc-400">{fmtBytes(f.size)}</span>
                      </span>
                      <button
                        onClick={() => { setFiles((arr) => arr.filter((_, j) => j !== i)); }}
                        className="rounded-full p-1.5 text-zinc-400 hover:bg-zinc-100 dark:hover:bg-white/10"
                        aria-label="remove"
                      >
                        <X className="h-3.5 w-3.5" />
                      </button>
                    </li>
                  ))}
                </ul>
              )}

              {error && (
                <div className="rounded-xl bg-rose-50 p-3.5 text-[13.5px] font-semibold text-rose-700 dark:bg-rose-500/10 dark:text-rose-300">{error}</div>
              )}

              {phase === "working" && (
                <div className="flex items-center gap-3 rounded-xl bg-emerald-50 p-4 text-[14px] font-semibold text-emerald-800 dark:bg-emerald-500/10 dark:text-emerald-300">
                  <Loader2 className="h-5 w-5 animate-spin" />
                  {T("running")} {progress && <span className="font-mono text-[12px] text-emerald-600">{progress}</span>}
                </div>
              )}

              {phase === "done" && result && (
                <div className="space-y-3 rounded-2xl border border-emerald-200 bg-emerald-50/50 p-4 dark:border-emerald-500/25 dark:bg-emerald-500/5">
                  <div className="flex items-center gap-2 text-[15px] font-extrabold text-emerald-800 dark:text-emerald-300">
                    <CheckCircle2 className="h-5 w-5" /> {T("done")}
                  </div>

                  {result.text !== undefined && (
                    <div className="relative">
                      <pre className="max-h-72 overflow-auto whitespace-pre-wrap rounded-xl border border-zinc-200 bg-white p-3.5 font-sans text-[13.5px] leading-6 text-zinc-800 dark:border-white/10 dark:bg-white/5 dark:text-zinc-100">{result.text}</pre>
                      <button
                        onClick={() => { navigator.clipboard.writeText(result.text ?? ""); }}
                        className="absolute right-2 top-2 flex items-center gap-1.5 rounded-lg bg-white px-2.5 py-1.5 text-[11.5px] font-bold text-zinc-600 shadow-sm ring-1 ring-zinc-200 hover:bg-zinc-50 dark:bg-[#12201a] dark:text-zinc-200 dark:ring-white/15"
                      >
                        <Copy className="h-3.5 w-3.5" /> {T("copyResult")}
                      </button>
                    </div>
                  )}

                  {result.report && (
                    <div className="rounded-xl border border-zinc-200 bg-white p-3.5 dark:border-white/10 dark:bg-white/5">
                      <pre className="whitespace-pre-wrap font-sans text-[13px] leading-6 text-zinc-700 dark:text-zinc-200">{result.report.join("\n")}</pre>
                    </div>
                  )}

                  {previewUrl && (
                    <div className="flex justify-center rounded-xl bg-white p-3 dark:bg-white/5">
                      {/* eslint-disable-next-line @next/next/no-img-element */}
                      <img src={previewUrl} alt="result" className="max-h-80 rounded-lg shadow" />
                    </div>
                  )}

                  {result.files.length > 0 && (
                    <div className="space-y-2">
                      <ul className="space-y-1.5">
                        {result.files.slice(0, 30).map((f: OutFile, i: number) => (
                          <li key={i} className="flex items-center gap-3 rounded-xl bg-white px-3.5 py-2.5 dark:bg-white/5">
                            <span className="flex h-8 w-8 items-center justify-center rounded-lg bg-emerald-100 dark:bg-emerald-500/15">
                              {f.name.endsWith(".zip") ? <Archive className="h-4 w-4 text-emerald-700 dark:text-emerald-400" /> : <FileDown className="h-4 w-4 text-emerald-700 dark:text-emerald-400" />}
                            </span>
                            <span className="min-w-0 flex-1 truncate text-[13.5px] font-semibold text-zinc-800 dark:text-zinc-100">{f.name}</span>
                            <span className="text-[11.5px] text-zinc-400">{fmtBytes(f.blob.size)}</span>
                            <Button size="sm" onClick={() => downloadBlob(f.blob, f.name)} className="h-8 gap-1.5 rounded-lg bg-[#0b7a43] px-3 text-[12.5px] font-bold hover:bg-[#096c3b]">
                              <Download className="h-3.5 w-3.5" /> {T("download")}
                            </Button>
                          </li>
                        ))}
                      </ul>
                      {result.files.length > 1 && (
                        <Button onClick={downloadAll} className="h-10 w-full gap-2 rounded-xl bg-[#0b7a43] text-[14px] font-bold hover:bg-[#096c3b]">
                          <Download className="h-4 w-4" /> {T("downloadAll")}
                        </Button>
                      )}
                    </div>
                  )}

                  <button onClick={() => { setPhase("idle"); setResult(null); }} className="flex items-center gap-1.5 text-[13px] font-bold text-zinc-500 hover:text-emerald-700 dark:text-zinc-400">
                    <RotateCcw className="h-3.5 w-3.5" /> {T("startOver")}
                  </button>
                </div>
              )}
            </div>

            {/* right: options */}
            <aside className="space-y-4 lg:sticky lg:top-4 lg:self-start">
              {def.fields.length > 0 && (
                <div className="rounded-2xl border border-zinc-200 p-4 dark:border-white/10">
                  <div className="mb-3 text-[13px] font-extrabold uppercase tracking-wide text-zinc-400">{T("options")}</div>
                  <div className="grid grid-cols-2 gap-3">
                    {def.fields.map((f) => (
                      <FieldControl
                        key={f.key}
                        field={f}
                        lang={lang}
                        value={options[f.key]}
                        onChange={(v) => setOptions((o) => ({ ...o, [f.key]: v }))}
                        full={!f.half}
                      />
                    ))}
                  </div>
                </div>
              )}

              {!isCustom && (
                <Button
                  onClick={run}
                  disabled={phase !== "idle" || !canRun}
                  className="h-12 w-full gap-2 rounded-xl bg-[#0b7a43] text-[15.5px] font-bold shadow-md shadow-emerald-700/20 hover:bg-[#096c3b]"
                >
                  {phase === "working" ? <Loader2 className="h-5 w-5 animate-spin" /> : <UploadCloud className="h-5 w-5" />}
                  {phase === "working" ? T("running") : T("run")}
                </Button>
              )}

              <p className="px-1 text-[12px] leading-5 text-zinc-400">
                🛡 {T("freeNote")}
              </p>
            </aside>
          </div>
        </div>
      )}

      <input
        ref={inputRef}
        type="file"
        accept={def.accept}
        multiple={def.multiple}
        className="hidden"
        onChange={(e) => { addFiles(e.target.files); e.currentTarget.value = ""; }}
      />
    </div>
  );
}

function DropArea({
  accept, multiple, onFiles, onBrowse, label, compact,
}: {
  accept?: string;
  multiple?: boolean;
  onFiles: (f: FileList | null) => void;
  onBrowse: () => void;
  label: string;
  compact?: boolean;
}) {
  const [over, setOver] = useState(false);
  if (compact)
    return (
      <button
        onClick={onBrowse}
        onDragOver={(e) => { e.preventDefault(); setOver(true); }}
        onDragLeave={() => setOver(false)}
        onDrop={(e) => { e.preventDefault(); setOver(false); onFiles(e.dataTransfer.files); }}
        className={`flex w-full items-center justify-center gap-2 rounded-xl border-2 border-dashed px-4 py-3 text-[13px] font-semibold transition-colors ${over ? "border-emerald-500 bg-emerald-50/60 text-emerald-700 dark:bg-emerald-500/10" : "border-zinc-200 text-zinc-500 hover:border-emerald-300 hover:text-emerald-700 dark:border-white/15 dark:text-zinc-400"}`}
      >
        <UploadCloud className="h-4 w-4" /> {label}
      </button>
    );
  return (
    <div
      onDragOver={(e) => { e.preventDefault(); setOver(true); }}
      onDragLeave={() => setOver(false)}
      onDrop={(e) => { e.preventDefault(); setOver(false); onFiles(e.dataTransfer.files); }}
      className={`flex flex-col items-center justify-center rounded-2xl border-2 border-dashed px-6 py-14 text-center transition-colors ${over ? "border-emerald-500 bg-emerald-50/60 dark:bg-emerald-500/5" : "border-zinc-200 bg-zinc-50/50 dark:border-white/15 dark:bg-white/[0.02]"}`}
    >
      <span className="flex h-14 w-14 items-center justify-center rounded-2xl bg-emerald-50 dark:bg-emerald-500/10">
        <UploadCloud className="h-7 w-7 text-[#0b8a47] dark:text-emerald-400" />
      </span>
      <button onClick={onBrowse} className="mt-4 rounded-xl bg-[#0b7a43] px-6 py-2.5 text-[14.5px] font-bold text-white shadow-sm transition-colors hover:bg-[#096c3b]">
        {label}
      </button>
    </div>
  );
}

function FieldControl({ field, lang, value, onChange, full }: { field: Field; lang: "en" | "bn"; value: unknown; onChange: (v: unknown) => void; full?: boolean }) {
  const label = field.label[lang];
  const cls = "h-10 rounded-lg border-zinc-200 text-[13.5px] dark:border-white/15 dark:bg-white/5";
  return (
    <div className={full ? "col-span-2 space-y-1.5" : "space-y-1.5"}>
      {field.type !== "checkbox" && <label className="block text-[12px] font-bold text-zinc-500 dark:text-zinc-400">{label}</label>}
      {field.type === "number" && (
        <Input type="number" min={field.min} max={field.max} step={field.step} value={(value as number | string) ?? ""} onChange={(e) => onChange(e.target.value === "" ? undefined : Number(e.target.value))} className={cls} />
      )}
      {field.type === "text" && (
        <Input value={(value as string) ?? ""} placeholder={field.placeholder} onChange={(e) => onChange(e.target.value)} className={cls} />
      )}
      {field.type === "color" && (
        <div className="flex items-center gap-2">
          <input type="color" value={(value as string) || "#000000"} onChange={(e) => onChange(e.target.value)} className="h-10 w-12 cursor-pointer rounded-lg border border-zinc-200 bg-transparent p-1 dark:border-white/15" aria-label={label} />
          <Input value={(value as string) ?? ""} onChange={(e) => onChange(e.target.value)} className={cls} />
        </div>
      )}
      {field.type === "select" && (
        <Select value={String(value ?? "")} onValueChange={(v) => onChange(v)}>
          <SelectTrigger className={cls}><SelectValue /></SelectTrigger>
          <SelectContent>
            {field.options?.map((opt) => (
              <SelectItem key={opt.value} value={opt.value}>{opt.label[lang]}</SelectItem>
            ))}
          </SelectContent>
        </Select>
      )}
      {field.type === "checkbox" && (
        <label className="flex h-10 cursor-pointer items-center gap-2.5 text-[13.5px] font-semibold text-zinc-700 dark:text-zinc-200">
          <Checkbox checked={!!value} onCheckedChange={(v) => onChange(!!v)} />
          {label}
        </label>
      )}
    </div>
  );
}
