"use client";
import { useEffect, useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { Folder, Loader2, RefreshCw, Save, Plus, Trash2, FolderOpen, Cpu } from "lucide-react";
import {
  setVideoLibraryPath,
  setVideoExtraPaths,
  setTranscodeMode,
} from "@/app/actions/settings";
import { useSettings } from "./SettingsProvider";
import { PartSuffixPatterns } from "./PartSuffixPatterns";
import { dispatchVideoStatusRefresh } from "@/components/video/videoStatusEvents";
import { cn } from "@/lib/utils";

interface ScanResult {
  count: number;
  codes: number;
  rootsScanned: string[];
  elapsedMs: number;
}

export function VideoLibrarySettings() {
  const { settings } = useSettings();
  const [, startTranscode] = useTransition();
  const [transcodeMode, setLocalTranscodeMode] = useState(settings.transcodeMode);
  // Stay in sync if the server-side value changes (e.g. user edits in
  // another tab and we router.refresh).
  useEffect(() => { setLocalTranscodeMode(settings.transcodeMode); }, [settings.transcodeMode]);
  const router = useRouter();
  const [draftMain, setDraftMain] = useState(settings.videoLibraryPath);
  const [draftExtras, setDraftExtras] = useState<string[]>(settings.videoExtraPaths ?? []);
  const [newExtra, setNewExtra] = useState("");
  const [saving, setSaving] = useState(false);
  const [rescanning, setRescanning] = useState(false);
  const [result, setResult] = useState<ScanResult | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [, start] = useTransition();
  const [picking, setPicking] = useState<string | null>(null);

  async function pickFolder(start: string): Promise<string | null> {
    try {
      const r = await fetch("/api/pick-folder", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ start }),
      });
      const j = await r.json();
      if (!r.ok) throw new Error(j.error ?? `picker failed (${r.status})`);
      return typeof j.path === "string" && j.path ? j.path : null;
    } catch (e) {
      setError((e as Error).message);
      return null;
    }
  }

  async function browseMain() {
    setPicking("main");
    try {
      const p = await pickFolder(draftMain);
      if (p) setDraftMain(p);
    } finally {
      setPicking(null);
    }
  }

  async function browseExtra(idx: number) {
    setPicking(`extra-${idx}`);
    try {
      const p = await pickFolder(draftExtras[idx] ?? "");
      if (p) updateExtra(idx, p);
    } finally {
      setPicking(null);
    }
  }

  async function browseNewExtra() {
    setPicking("new");
    try {
      const p = await pickFolder(newExtra);
      if (p) {
        if (!draftExtras.includes(p)) {
          setDraftExtras((cur) => [...cur, p]);
          setNewExtra("");
        } else {
          setNewExtra(p);
        }
      }
    } finally {
      setPicking(null);
    }
  }

  const mainDirty = draftMain.trim() !== (settings.videoLibraryPath ?? "");
  const extrasDirty =
    draftExtras.length !== (settings.videoExtraPaths ?? []).length ||
    draftExtras.some((v, i) => v !== (settings.videoExtraPaths ?? [])[i]);
  const dirty = mainDirty || extrasDirty;
  const hasAnyConfigured = !!settings.videoLibraryPath || (settings.videoExtraPaths ?? []).length > 0;

  function addExtra() {
    const v = newExtra.trim();
    if (!v) return;
    if (draftExtras.includes(v)) return;
    setDraftExtras((cur) => [...cur, v]);
    setNewExtra("");
  }

  function removeExtra(idx: number) {
    setDraftExtras((cur) => cur.filter((_, i) => i !== idx));
  }

  function updateExtra(idx: number, value: string) {
    setDraftExtras((cur) => cur.map((v, i) => i === idx ? value : v));
  }

  async function save() {
    setSaving(true);
    setError(null);
    try {
      if (mainDirty) await setVideoLibraryPath(draftMain);
      if (extrasDirty) await setVideoExtraPaths(draftExtras);
      const r = await fetch("/api/video-rescan", { method: "POST" });
      const j = await r.json();
      if (!r.ok) throw new Error(j.error ?? `rescan failed (${r.status})`);
      setResult(j);
      dispatchVideoStatusRefresh();
      start(() => router.refresh());
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setSaving(false);
    }
  }

  async function rescan(opts: { force?: boolean } = {}) {
    setRescanning(true);
    setError(null);
    try {
      const url = opts.force ? "/api/video-rescan?force=1" : "/api/video-rescan";
      const r = await fetch(url, { method: "POST" });
      const j = await r.json();
      if (!r.ok) throw new Error(j.error ?? `rescan failed (${r.status})`);
      setResult(j);
      dispatchVideoStatusRefresh();
      start(() => router.refresh());
    } catch (e) {
      setError((e as Error).message);
    } finally {
      setRescanning(false);
    }
  }

  return (
    <div className="py-2 space-y-5">
      {/* Main folder ---------------------------------------------------- */}
      <div>
        <div className="text-sm font-medium mb-1">Main Library Folder</div>
        <div className="text-xs text-[var(--color-fg-muted)] mb-2">
          Recursive scan. Expected to follow the same letter-bucket layout as the cover library, e.g.{" "}
          <span className="font-mono">D:\JAV\A-E\A\AOZ-200Z.mp4</span>.
        </div>
        <div className="flex items-center gap-2">
          <div className="relative flex-1">
            <Folder className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--color-fg-muted)] pointer-events-none" />
            <input
              type="text"
              value={draftMain}
              onChange={(e) => setDraftMain(e.target.value)}
              placeholder="D:\JAV"
              className="w-full glass rounded-lg pl-9 pr-3 py-1.5 text-sm font-mono outline-none focus:border-[var(--color-cyan)]"
            />
          </div>
          <button
            type="button"
            onClick={browseMain}
            disabled={picking !== null}
            title="Browse for folder"
            className="inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg glass glass-hover disabled:opacity-40"
          >
            {picking === "main" ? <Loader2 className="w-4 h-4 animate-spin" /> : <FolderOpen className="w-4 h-4" />}
            Browse
          </button>
        </div>
      </div>

      {/* Additional folders --------------------------------------------- */}
      <div>
        <div className="text-sm font-medium mb-1">Additional Folders</div>
        <div className="text-xs text-[var(--color-fg-muted)] mb-2">
          Flat folders where videos sit directly inside (e.g. <span className="font-mono">E:\JAV\IBW-203.mp4</span>). Subfolders are still walked. One absolute path per row.
        </div>
        <div className="space-y-1.5">
          {draftExtras.length === 0 ? (
            <div className="text-xs text-[var(--color-fg-muted)] italic px-3 py-2 rounded-lg border border-dashed border-[var(--color-glass-border)]">
              No additional folders.
            </div>
          ) : (
            draftExtras.map((p, i) => (
              <div key={i} className="flex items-center gap-2">
                <div className="relative flex-1">
                  <Folder className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--color-fg-muted)] pointer-events-none" />
                  <input
                    type="text"
                    value={p}
                    onChange={(e) => updateExtra(i, e.target.value)}
                    className="w-full glass rounded-lg pl-9 pr-3 py-1.5 text-sm font-mono outline-none focus:border-[var(--color-cyan)]"
                  />
                </div>
                <button
                  type="button"
                  onClick={() => browseExtra(i)}
                  disabled={picking !== null}
                  title="Browse for folder"
                  className="p-1.5 rounded-lg glass glass-hover disabled:opacity-40"
                >
                  {picking === `extra-${i}` ? <Loader2 className="w-4 h-4 animate-spin" /> : <FolderOpen className="w-4 h-4" />}
                </button>
                <button
                  type="button"
                  onClick={() => removeExtra(i)}
                  title="Remove"
                  className="p-1.5 rounded-lg border border-red-500/30 text-red-300 hover:bg-red-500/10"
                >
                  <Trash2 className="w-4 h-4" />
                </button>
              </div>
            ))
          )}
        </div>
        <div className="flex items-center gap-2 mt-2">
          <div className="relative flex-1">
            <Folder className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--color-fg-muted)] pointer-events-none" />
            <input
              type="text"
              value={newExtra}
              onChange={(e) => setNewExtra(e.target.value)}
              onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); addExtra(); } }}
              placeholder="E:\JAV"
              className="w-full glass rounded-lg pl-9 pr-3 py-1.5 text-sm font-mono outline-none focus:border-[var(--color-cyan)]"
            />
          </div>
          <button
            type="button"
            onClick={browseNewExtra}
            disabled={picking !== null}
            title="Browse for folder"
            className="inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg glass glass-hover disabled:opacity-40"
          >
            {picking === "new" ? <Loader2 className="w-4 h-4 animate-spin" /> : <FolderOpen className="w-4 h-4" />}
            Browse
          </button>
          <button
            type="button"
            onClick={addExtra}
            disabled={!newExtra.trim()}
            className="inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg glass glass-hover disabled:opacity-40"
          >
            <Plus className="w-4 h-4" /> Add
          </button>
        </div>
      </div>

      {/* Action row ----------------------------------------------------- */}
      <div className="flex items-center gap-2 pt-3 border-t border-[var(--color-glass-border)]">
        <button
          type="button"
          onClick={save}
          disabled={!dirty || saving}
          className="inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg bg-[var(--color-cyan)] text-black font-medium disabled:opacity-40 disabled:cursor-not-allowed"
        >
          {saving ? <Loader2 className="w-4 h-4 animate-spin" /> : <Save className="w-4 h-4" />}
          Save &amp; Rescan
        </button>
        <button
          type="button"
          onClick={() => rescan()}
          disabled={rescanning || saving || !hasAnyConfigured}
          title={hasAnyConfigured ? "Incremental — only re-walk folders whose mtime changed" : "Configure a folder first"}
          className="inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg glass glass-hover disabled:opacity-40"
        >
          {rescanning ? <Loader2 className="w-4 h-4 animate-spin" /> : <RefreshCw className="w-4 h-4" />}
          Re-scan Only
        </button>
        <button
          type="button"
          onClick={() => rescan({ force: true })}
          disabled={rescanning || saving || !hasAnyConfigured}
          title={hasAnyConfigured ? "Force full rescan — bypass dir-mtime cache (use after content edits without rename)" : "Configure a folder first"}
          className="inline-flex items-center gap-1.5 text-sm px-3 py-1.5 rounded-lg border border-amber-400/30 text-amber-200 hover:bg-amber-400/10 disabled:opacity-40"
        >
          {rescanning ? <Loader2 className="w-4 h-4 animate-spin" /> : <RefreshCw className="w-4 h-4" />}
          Force Full
        </button>
      </div>

      {result && (
        <div className="text-xs text-[var(--color-mint)] bg-[var(--color-mint)]/5 border border-[var(--color-mint)]/25 rounded-lg px-3 py-2 space-y-0.5">
          <div>
            Scanned in {result.elapsedMs}ms. <strong>{result.count}</strong> file{result.count === 1 ? "" : "s"} matched, <strong>{result.codes}</strong> unique code{result.codes === 1 ? "" : "s"}.
          </div>
          {result.rootsScanned.length > 0 && (
            <div className="text-[10px] font-mono text-[var(--color-fg-muted)]">
              roots: {result.rootsScanned.join("  ·  ")}
            </div>
          )}
        </div>
      )}
      {error && (
        <div className="text-xs text-red-300 bg-red-500/5 border border-red-500/25 rounded-lg px-3 py-2">
          {error}
        </div>
      )}

      {/* Playback transcode mode --------------------------------------- */}
      <div className="pt-3 border-t border-[var(--color-glass-border)] space-y-3">
        <div className="space-y-1">
          <div className="text-sm font-medium flex items-center gap-1.5">
            <Cpu className="w-3.5 h-3.5 text-[var(--color-cyan)]" />
            Playback Transcoding (NVENC)
          </div>
          <div className="text-xs text-[var(--color-fg-muted)] max-w-2xl">
            Live HLS re-encode with NVENC, dropping B-frames to bypass Chromium&apos;s
            H.264 sink reorder bug. Requires NVIDIA GPU + ffmpeg with
            <span className="font-mono"> h264_nvenc</span>. Auto modes detect
            whether transcoding is needed per file.
          </div>
        </div>
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
          {([
            {
              value: "off",
              title: "Off",
              desc: "Always serve the original file. Stuttery H.264 files will stutter.",
            },
            {
              value: "always",
              title: "Always Transcode",
              desc: "Every file goes through NVENC HLS. Bullet-proof, slight quality loss.",
            },
            {
              value: "auto-predicate",
              title: "Auto · Predicate",
              desc: "Probe codec on first play. Transcode only H.264 files with B-frames.",
            },
            {
              value: "auto-runtime",
              title: "Auto · Runtime",
              desc: "Measure dropped frames in a brief pre-roll, decide and remember per file.",
            },
          ] as const).map((opt) => {
            const active = transcodeMode === opt.value;
            return (
              <button
                key={opt.value}
                type="button"
                onClick={() => {
                  setLocalTranscodeMode(opt.value);
                  startTranscode(() => {
                    void setTranscodeMode(opt.value);
                  });
                }}
                className={cn(
                  "text-left rounded-lg border px-3 py-2 transition-colors",
                  active
                    ? "border-[var(--color-cyan)] bg-[var(--color-cyan)]/10 shadow-[var(--shadow-glow-cyan)]"
                    : "border-[var(--color-glass-border)] bg-[var(--color-glass)]/30 hover:border-[var(--color-glass-border-strong)]",
                )}
              >
                <div className={cn(
                  "text-sm font-medium",
                  active ? "text-[var(--color-cyan)]" : "text-[var(--color-fg)]",
                )}>{opt.title}</div>
                <div className="text-[11px] text-[var(--color-fg-muted)] mt-0.5 leading-snug">{opt.desc}</div>
              </button>
            );
          })}
        </div>
      </div>

      {/* Part suffix patterns ------------------------------------------ */}
      <PartSuffixPatterns />
    </div>
  );
}
