"use client";
import { useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { Download, Upload, Loader2, AlertTriangle, Archive, PackageOpen } from "lucide-react";

export function BackupButtons() {
  const router = useRouter();
  const fileRef = useRef<HTMLInputElement>(null);
  const zipFileRef = useRef<HTMLInputElement>(null);
  const [importing, setImporting] = useState(false);
  const [importingZip, setImportingZip] = useState(false);
  const [exporting, setExporting] = useState(false);
  const [exportingAll, setExportingAll] = useState(false);
  const [message, setMessage] = useState<{ kind: "ok" | "err"; text: string } | null>(null);
  // Synchronous guard: setImporting() updates state asynchronously, so a
  // rapid second invocation could slip through before importing=true is
  // visible in the next render. The ref blocks that window.
  const importingRef = useRef(false);
  const importingZipRef = useRef(false);

  async function handleExport() {
    setExporting(true);
    setMessage(null);
    try {
      const res = await fetch("/api/backup/export");
      if (!res.ok) throw new Error(`Export failed (${res.status})`);
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      const stamp = new Date().toISOString().replace(/[:.]/g, "-");
      a.download = `pinkudex-backup-${stamp}.json`;
      document.body.appendChild(a);
      a.click();
      a.remove();
      URL.revokeObjectURL(url);
      setMessage({ kind: "ok", text: "Backup downloaded." });
    } catch (e) {
      setMessage({ kind: "err", text: (e as Error).message });
    } finally {
      setExporting(false);
    }
  }

  async function handleExportAll() {
    setExportingAll(true);
    setMessage(null);
    try {
      // Direct navigation so the browser owns the download (and shows its
      // own progress bar, ETA, pause/cancel) — fetch+blob would buffer the
      // whole zip in memory, which is fatal for multi-GB libraries.
      const a = document.createElement("a");
      a.href = "/api/backup/library-export";
      a.rel = "noopener";
      document.body.appendChild(a);
      a.click();
      a.remove();
      setMessage({ kind: "ok", text: "Library export started — see browser downloads." });
    } catch (e) {
      setMessage({ kind: "err", text: (e as Error).message });
    } finally {
      setExportingAll(false);
    }
  }

  async function handleImport(file: File) {
    if (importingRef.current) return;
    // Claim the lock BEFORE confirm() so a second invocation triggered
    // while the dialog is still open (e.g. rapid double-pick of the same
    // file) hits the early return instead of slipping through the race
    // window between confirm and setImporting.
    importingRef.current = true;
    if (!confirm(
      "Importing will REPLACE all existing actresses, covers, categories, tags, collections and settings with the contents of this backup.\n\nThis cannot be undone. Continue?",
    )) {
      importingRef.current = false;
      return;
    }

    setImporting(true);
    setMessage(null);
    try {
      const text = await file.text();
      const res = await fetch("/api/backup/import", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: text,
      });
      const j = await res.json();
      if (!res.ok) throw new Error(j.error ?? `Import failed (${res.status})`);
      const totals = Object.entries(j.counts ?? {})
        .filter(([, n]) => (n as number) > 0)
        .map(([t, n]) => `${t}: ${n}`)
        .join(", ");
      setMessage({ kind: "ok", text: `Import complete. ${totals || "(empty)"}` });
      router.refresh();
    } catch (e) {
      setMessage({ kind: "err", text: (e as Error).message });
    } finally {
      importingRef.current = false;
      setImporting(false);
      if (fileRef.current) fileRef.current.value = "";
    }
  }

  async function handleImportZip(file: File) {
    if (importingZipRef.current) return;
    // Same race-window fix as handleImport — claim the lock before the
    // confirm() dialog opens.
    importingZipRef.current = true;
    if (!confirm(
      "Restoring will REPLACE the database AND your library/, data/thumbs/, data/portraits/, data/category-covers/ folders.\n\n" +
      "Existing folders will be renamed to *.pre-restore-<timestamp>/ for manual rollback. The database is snapshotted first.\n\n" +
      "This can take a long time for multi-GB archives. Continue?",
    )) {
      importingZipRef.current = false;
      return;
    }

    setImportingZip(true);
    setMessage(null);
    try {
      // Stream the file as the request body so multi-GB uploads don't have
      // to be buffered into memory before sending. The route streams it
      // straight to disk on the server side.
      const res = await fetch("/api/backup/library-import", {
        method: "POST",
        headers: { "Content-Type": "application/zip" },
        body: file,
        // @ts-expect-error - Node fetch undici extension; lets the body stream.
        duplex: "half",
      });
      const j = await res.json();
      if (!res.ok) throw new Error(j.error ?? `Restore failed (${res.status})`);
      const totals = Object.entries(j.counts ?? {})
        .filter(([, n]) => (n as number) > 0)
        .map(([t, n]) => `${t}: ${n}`)
        .join(", ");
      const mediaParts = (j.mediaRestored ?? []).join(", ");
      setMessage({
        kind: "ok",
        text: `Restore complete. DB — ${totals || "(empty)"}. Media — ${mediaParts || "(none)"}. Old folders kept as *.pre-restore-* for rollback.`,
      });
      router.refresh();
    } catch (e) {
      setMessage({ kind: "err", text: (e as Error).message });
    } finally {
      importingZipRef.current = false;
      setImportingZip(false);
      if (zipFileRef.current) zipFileRef.current.value = "";
    }
  }

  return (
    <div className="py-2">
      <div className="text-sm font-medium mb-1">Backup &amp; Restore</div>
      <div className="text-xs text-[var(--color-fg-muted)] mb-3">
        <strong>Export backup</strong> — metadata only (JSON). Fast, small, safe to email.
        <br />
        <strong>Export full library</strong> — zip of <span className="font-mono">library/</span>, <span className="font-mono">data/thumbs/</span>, <span className="font-mono">data/portraits/</span>, <span className="font-mono">data/category-covers/</span> plus <span className="font-mono">database.json</span>. Skips <span className="font-mono">library/.superseded/</span>. Can be many GB; browser shows progress.
      </div>

      <div className="flex flex-wrap items-center gap-2">
        <button
          type="button"
          onClick={handleExport}
          disabled={exporting}
          className="flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg glass glass-hover disabled:opacity-50"
        >
          {exporting ? <Loader2 className="w-4 h-4 animate-spin" /> : <Download className="w-4 h-4" />}
          Export backup
        </button>

        <button
          type="button"
          onClick={handleExportAll}
          disabled={exportingAll}
          className="flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg glass glass-hover disabled:opacity-50"
        >
          {exportingAll ? <Loader2 className="w-4 h-4 animate-spin" /> : <Archive className="w-4 h-4" />}
          Export full library
        </button>

        <button
          type="button"
          onClick={() => fileRef.current?.click()}
          disabled={importing}
          className="flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg border border-amber-500/40 text-amber-200 hover:bg-amber-500/10 disabled:opacity-50"
        >
          {importing ? <Loader2 className="w-4 h-4 animate-spin" /> : <Upload className="w-4 h-4" />}
          Import backup…
        </button>
        <input
          ref={fileRef}
          type="file"
          accept="application/json,.json"
          hidden
          onChange={(e) => { const f = e.target.files?.[0]; if (f) handleImport(f); }}
        />

        <button
          type="button"
          onClick={() => zipFileRef.current?.click()}
          disabled={importingZip}
          className="flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg border border-amber-500/40 text-amber-200 hover:bg-amber-500/10 disabled:opacity-50"
        >
          {importingZip ? <Loader2 className="w-4 h-4 animate-spin" /> : <PackageOpen className="w-4 h-4" />}
          Restore full library…
        </button>
        <input
          ref={zipFileRef}
          type="file"
          accept="application/zip,.zip"
          hidden
          onChange={(e) => { const f = e.target.files?.[0]; if (f) handleImportZip(f); }}
        />
      </div>

      {message && (
        <div
          className={`mt-3 flex items-start gap-2 text-xs ${
            message.kind === "ok" ? "text-[var(--color-mint)]" : "text-red-300"
          }`}
        >
          {message.kind === "err" && <AlertTriangle className="w-4 h-4 shrink-0 mt-0.5" />}
          <span className="break-words">{message.text}</span>
        </div>
      )}
    </div>
  );
}
