import { useEffect, useState } from "react"
import { Download, Search, AlertTriangle, ChevronDown, ChevronUp, Heart, Check, Flame } from "lucide-react"
import { api, type FitResp } from "@/lib/api"
import { useModels } from "@/lib/queries"
import { cn } from "@/lib/utils"

type Hit = { repo: string; downloads: number; likes: number }

const CHIPS: { key: string; label: string; q: string }[] = [
  { key: "popular", label: "Beliebt", q: "" },
  { key: "coder", label: "Coder", q: "coder" },
  { key: "vision", label: "Vision", q: "vision" },
  { key: "reasoning", label: "Reasoning", q: "reasoning" },
  { key: "small", label: "Klein (≤4B)", q: "3B" },
]
const ROLE_OPTS = ["fast", "heavy", "coder", "vision", "hermes", "scout"]

const _nf = new Intl.NumberFormat(undefined, { notation: "compact", maximumFractionDigits: 1 })
function fmtN(n: number): string {
  return _nf.format(n)
}

export function ModelBrowse() {
  const { data: modelsData } = useModels()
  const installed = modelsData?.models ?? []

  const [q, setQ] = useState("")
  const [results, setResults] = useState<Hit[]>([])
  const [loading, setLoading] = useState(false)
  const [msg, setMsg] = useState("")
  const [chip, setChip] = useState("popular")
  const [direct, setDirect] = useState("")

  // Pro aufgeklapptem Treffer: Quant/Rolle/Fit/Install-Zustand.
  const [open, setOpen] = useState<string | null>(null)
  const [quants, setQuants] = useState<string[]>([])
  const [quant, setQuant] = useState("Q4_K_M")
  const [role, setRole] = useState("")
  const [fit, setFit] = useState<FitResp | null>(null)
  const [oomArmed, setOomArmed] = useState(false)
  const [installing, setInstalling] = useState<Record<string, string>>({})

  async function runSearch(query: string) {
    setLoading(true)
    setMsg("")
    try {
      const d = await api<{ results: Hit[] }>(`/api/hf/search?q=${encodeURIComponent(query)}`)
      setResults(d.results)
      if (!d.results.length) setMsg("Keine Ergebnisse.")
    } catch (e) {
      setMsg(`Suche fehlgeschlagen: ${e}`)
    } finally {
      setLoading(false)
    }
  }

  // Beim Öffnen: Trending laden, damit man sofort stöbern kann (ohne zu tippen).
  useEffect(() => { runSearch("") }, [])

  function pickChip(c: typeof CHIPS[number]) {
    setChip(c.key)
    setQ("")
    runSearch(c.q)
  }

  function submitSearch() {
    setChip("")
    runSearch(q)
  }

  async function refreshFit(repo: string, qq: string, rl: string) {
    setOomArmed(false)
    try {
      const d = await api<FitResp>(
        `/api/fit?params_b=0&quant=${encodeURIComponent(qq)}&ctx=8192&name=${encodeURIComponent(repo)}&role=${encodeURIComponent(rl)}`
      )
      setFit(d)
    } catch { setFit(null) }
  }

  async function toggleExpand(repo: string) {
    if (open === repo) { setOpen(null); return }
    setOpen(repo)
    setQuants([]); setFit(null); setRole(""); setOomArmed(false)
    setMsg("Analysiere Repository…")
    try {
      const d = await api<{ repo: string; quants: string[] }>(`/api/hf/quants?repo=${encodeURIComponent(repo)}`)
      setQuants(d.quants)
      const pick = d.quants.includes("Q4_K_M") ? "Q4_K_M" : (d.quants[0] || "Q4_K_M")
      setQuant(pick)
      setMsg(d.quants.length ? "" : "Keine GGUF-Dateien in diesem Repo gefunden.")
      if (d.quants.length) refreshFit(d.repo, pick, "")
    } catch (e) {
      setMsg(`Fehler: ${e}`)
    }
  }

  function loadDirect() {
    const r = direct.trim()
    if (!r) return
    // Treffer-Liste mit dem Direkt-Repo vorn ergänzen + aufklappen.
    setResults((rs) => rs.some((x) => x.repo === r) ? rs : [{ repo: r, downloads: 0, likes: 0 }, ...rs])
    setDirect("")
    toggleExpand(r)
  }

  // Welches Modell hält die gewählte Rolle? (exklusiv → würde übernommen)
  const roleHolder = role ? installed.find((m) => (m.role || "").toLowerCase() === role) : undefined

  async function install(repo: string) {
    if (fit?.fit.level === "too_tight" && !oomArmed) { setOomArmed(true); return }
    setInstalling((s) => ({ ...s, [repo]: "Starte…" }))
    try {
      await api("/api/models/install", {
        method: "POST",
        body: JSON.stringify({ repo, quant, role: role || undefined, jinja: true }),
      })
      setInstalling((s) => ({ ...s, [repo]: "Download läuft" }))
      setOomArmed(false)
    } catch (e) {
      setInstalling((s) => ({ ...s, [repo]: "Fehler" }))
    }
  }

  const isInstalled = (repo: string) => {
    const tail = repo.split("/").pop()?.toLowerCase().replace(/-gguf$/i, "") || ""
    return tail.length > 3 && installed.some((m) => m.name.toLowerCase().includes(tail))
  }

  const fitTone = (lvl?: string) =>
    lvl === "perfect" ? "text-emerald-400 border-emerald-500/40 bg-emerald-500/10"
    : lvl === "marginal" ? "text-amber-400 border-amber-500/40 bg-amber-500/10"
    : "text-red-400 border-red-500/40 bg-red-500/10"

  return (
    <div className="space-y-5">
      {/* Suchleiste — immer sichtbar */}
      <div className="flex gap-2">
        <div className="relative flex-1">
          <Search className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
          <input
            value={q}
            onChange={(e) => setQ(e.target.value)}
            onKeyDown={(e) => e.key === "Enter" && submitSearch()}
            aria-label="HuggingFace durchsuchen"
            type="search"
            spellCheck={false}
            placeholder="HuggingFace durchsuchen (z.B. Qwen Coder, Llama-3.1, gemma)…"
            className="w-full h-10 pl-9 pr-3 rounded-xl border border-border/60 bg-background/40 text-xs outline-none focus:ring-1 focus:ring-primary/50 text-foreground"
          />
        </div>
        <button
          onClick={submitSearch}
          className="h-10 px-5 rounded-xl bg-primary text-primary-foreground text-xs font-semibold hover:opacity-90 transition-all cursor-pointer"
        >
          Suchen
        </button>
      </div>

      {/* Filter-Chips zum Stöbern */}
      <div className="flex items-center gap-2 flex-wrap">
        {CHIPS.map((c) => (
          <button
            key={c.key}
            onClick={() => pickChip(c)}
            className={cn(
              "flex items-center gap-1.5 rounded-full px-3 py-1.5 text-[11px] font-semibold transition-all cursor-pointer border",
              chip === c.key
                ? "bg-primary/15 text-primary border-primary/30"
                : "border-border/50 text-muted-foreground hover:text-foreground hover:border-border",
            )}
          >
            {c.key === "popular" && <Flame className="h-3 w-3" />}
            {c.label}
          </button>
        ))}
      </div>

      {/* Treffer */}
      {loading ? (
        <div className="text-xs text-muted-foreground py-10 text-center">Lade GGUF-Modelle von HuggingFace…</div>
      ) : (
        <div className="space-y-2">
          {results.map((r) => {
            const isOpen = open === r.repo
            const inst = isInstalled(r.repo)
            const author = r.repo.includes("/") ? r.repo.split("/")[0] : "—"
            const name = r.repo.split("/").pop()
            return (
              <div
                key={r.repo}
                className={cn(
                  "mc-card-sm",
                  isOpen ? "border-primary/40 shadow-lg shadow-primary/5" : "border-border/50 hover:border-primary/30",
                )}
              >
                {/* Kopf-Zeile (klickbar) */}
                <button
                  onClick={() => toggleExpand(r.repo)}
                  className="w-full flex items-center gap-3 p-3.5 text-left cursor-pointer"
                >
                  <div className="min-w-0 flex-1">
                    <div className="flex items-center gap-2 flex-wrap">
                      <span className="text-xs font-bold font-mono text-foreground truncate max-w-[280px]" title={r.repo}>{name}</span>
                      {inst && (
                        <span className="flex items-center gap-1 text-[8px] font-bold text-emerald-400 uppercase tracking-wider bg-emerald-500/10 border border-emerald-500/20 px-1.5 py-0.5 rounded">
                          <Check className="h-2.5 w-2.5" /> installiert
                        </span>
                      )}
                    </div>
                    <div className="text-[10px] text-muted-foreground/80 flex items-center gap-3 mt-0.5 font-mono">
                      <span>{author}</span>
                      <span className="flex items-center gap-1"><Download className="h-3 w-3" /> {fmtN(r.downloads)}</span>
                      {r.likes > 0 && <span className="flex items-center gap-1"><Heart className="h-3 w-3" /> {fmtN(r.likes)}</span>}
                    </div>
                  </div>
                  {isOpen ? <ChevronUp className="h-4 w-4 text-muted-foreground shrink-0" /> : <ChevronDown className="h-4 w-4 text-muted-foreground shrink-0" />}
                </button>

                {/* Aufgeklappt: Quant + Rolle + Fit + Install */}
                {isOpen && (
                  <div className="border-t border-border/30 p-3.5 space-y-3">
                    {quants.length === 0 ? (
                      <div className="text-[11px] text-muted-foreground font-mono">{msg || "Lade Quants…"}</div>
                    ) : (
                      <>
                        <div className="flex flex-wrap items-center gap-2">
                          <label className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Quant</label>
                          <select
                            value={quant}
                            onChange={(e) => { setQuant(e.target.value); refreshFit(r.repo, e.target.value, role) }}
                            aria-label="Quantisierung"
                            className="h-8 rounded-lg border border-border/60 bg-background/40 px-2 text-xs text-foreground font-semibold outline-none"
                          >
                            {quants.map((qq) => <option key={qq} value={qq} className="bg-popover text-foreground">{qq}</option>)}
                          </select>
                          <label className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground ml-1">Rolle</label>
                          <select
                            value={role}
                            onChange={(e) => { setRole(e.target.value); refreshFit(r.repo, quant, e.target.value) }}
                            aria-label="Rolle"
                            title="Optional — bestimmt Auto-Konfiguration + setup-bewussten Kontext"
                            className="h-8 rounded-lg border border-border/60 bg-background/40 px-2 text-xs text-foreground font-semibold outline-none"
                          >
                            <option value="" className="bg-popover text-foreground">keine</option>
                            {ROLE_OPTS.map((rl) => <option key={rl} value={rl} className="bg-popover text-foreground">{rl}</option>)}
                          </select>
                          <button
                            onClick={() => install(r.repo)}
                            disabled={!!installing[r.repo] && installing[r.repo] === "Download läuft"}
                            className={cn(
                              "h-8 px-3.5 ml-auto rounded-lg text-xs font-semibold transition-all cursor-pointer flex items-center gap-1.5",
                              oomArmed ? "bg-red-500 text-white hover:opacity-90" : "bg-primary text-primary-foreground hover:opacity-90",
                            )}
                          >
                            {installing[r.repo] ? installing[r.repo]
                              : oomArmed ? <><AlertTriangle className="h-3.5 w-3.5" /> OOM — trotzdem laden</>
                              : <><Download className="h-3.5 w-3.5" /> Herunterladen</>}
                          </button>
                        </div>

                        {/* Fit-Ampel */}
                        {fit && (
                          <div className={cn("flex flex-wrap items-center gap-x-3 gap-y-1 rounded-lg border px-3 py-2 text-[11px] font-medium", fitTone(fit.fit.level))}>
                            <span className="font-bold uppercase tracking-wide">{fit.fit.text}</span>
                            <span className="font-mono opacity-90">~{fit.params_b}B · ~{fit.fit.req_gb} GB / {fit.sys_ram_gb} GB · ~{fit.fit.tps} t/s</span>
                            {fit.fit.level !== "too_tight"
                              ? <span className="font-mono opacity-80">ctx → {(fit.assigned_ctx / 1024).toFixed(0)}k</span>
                              : <span className="opacity-90">— passt nicht, würde beim Laden abstürzen (OOM).</span>}
                          </div>
                        )}

                        {/* Rollen-Übernahme-Warnung */}
                        {roleHolder && (
                          <div className="flex items-start gap-2 rounded-lg border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-[11px] text-amber-400">
                            <AlertTriangle className="h-3.5 w-3.5 mt-0.5 shrink-0" />
                            <span>Rolle <strong>„{role}"</strong> hält aktuell <strong>{roleHolder.name.split("/").pop()}</strong> — wird beim Download übernommen.</span>
                          </div>
                        )}
                      </>
                    )}
                  </div>
                )}
              </div>
            )
          })}
          {!results.length && !loading && <div className="text-xs text-muted-foreground py-10 text-center">{msg || "Keine Ergebnisse."}</div>}
        </div>
      )}

      {/* Direkt-Eingabe (Power-User: exaktes Repo/URL) */}
      <div className="border-t border-border/30 pt-4 flex flex-col sm:flex-row gap-2 items-stretch sm:items-center">
        <span className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground shrink-0">Direkt:</span>
        <input
          value={direct}
          onChange={(e) => setDirect(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && loadDirect()}
          aria-label="Repository oder URL direkt eingeben"
          spellCheck={false}
          placeholder="org/repo oder HF-URL (z.B. unsloth/Qwen2.5-Coder-7B-Instruct-GGUF)"
          className="flex-1 h-9 rounded-lg border border-border/60 bg-background/40 px-3 text-xs outline-none focus:ring-1 focus:ring-primary/50 text-foreground font-mono"
        />
        <button
          onClick={loadDirect}
          className="h-9 px-4 rounded-lg border border-border/60 bg-background/20 text-xs font-semibold hover:border-primary/50 hover:bg-background/40 transition-all cursor-pointer whitespace-nowrap"
        >
          Laden
        </button>
      </div>
    </div>
  )
}
