import { useState } from "react"
import { Zap, Plus, X, Check, HardDrive, AlertTriangle } from "lucide-react"
import { setGroup, type ModelInfo } from "@/lib/api"
import { useModels, useGroups, useSystemStatus, useHermesBrain, useQueryClient, qk } from "@/lib/queries"
import { useDialog } from "@/lib/useDialog"
import { RoleLabel, roleMeta } from "@/components/models/ModelBadges"
import { fmtSize } from "@/lib/format"
import { cn } from "@/lib/utils"

// „Immer-bereit-Set" = die ko-residente llama-swap-Gruppe `brains`. Ihre Mitglieder
// bleiben gemeinsam warm (verdrängen sich nicht). Hier in Klartext verwaltbar,
// mit Speicher-Budget und Geländer (Hirn/Gedächtnis nicht entfernbar).
export function WarmSetManager() {
  const qc = useQueryClient()
  const { data: modelsResp } = useModels()
  const { data: groupsResp } = useGroups()
  const { data: sysStatus } = useSystemStatus()
  const { data: brain } = useHermesBrain()
  const { showAlert, dialogElement } = useDialog()
  const [picking, setPicking] = useState(false)
  const [busy, setBusy] = useState(false)

  const models = modelsResp?.models ?? []
  const running = modelsResp?.running ?? []
  const group = groupsResp?.groups?.brains
  const members = group?.members ?? []
  const byName = (name: string) => models.find((m) => m.name === name)
  const shortName = (name: string) => name.split("/").pop()?.replace(/\.gguf$/i, "") || name

  const memberModels = members.map(byName).filter(Boolean) as ModelInfo[]
  const B = 1024 ** 3
  const bud = brain?.budget
  const weightsGb = memberModels.reduce((a, m) => a + (m.size_bytes || 0), 0) / B
  const gttTotal = sysStatus?.gpu?.gtt_total || sysStatus?.gpu?.vram_total || 0
  const totalGb = bud?.gtt_gb || (gttTotal ? gttTotal / B : 0)
  // Ehrlicher Fußabdruck inkl. Arbeitsspeicher (KV-Cache) aus dem Budget; sonst nur Gewichte.
  const reservedGb = bud?.warm_projected_gb ?? weightsGb
  const pct = totalGb > 0 ? Math.min(100, (reservedGb / totalGb) * 100) : 0

  const eligible = models.filter((m) => !members.includes(m.name) && !m.incomplete)

  async function save(next: string[]) {
    setBusy(true)
    try {
      await setGroup("brains", next, group?.swap ?? false, group?.persist ?? true)
      qc.invalidateQueries({ queryKey: qk.groups })
      qc.invalidateQueries({ queryKey: qk.models })
      qc.invalidateQueries({ queryKey: qk.hermesBrain })
    } catch (e: any) {
      showAlert("Fehler", `Immer-bereit-Set konnte nicht geändert werden: ${e?.message || e}`)
    } finally {
      setBusy(false)
    }
  }

  function remove(name: string) {
    const m = byName(name)
    if (m && roleMeta(m.role).protected) {
      showAlert("Geschützt", `„${roleMeta(m.role).label}" ist lebenswichtig und bleibt immer bereit.`)
      return
    }
    void save(members.filter((x) => x !== name))
  }

  function add(name: string) {
    setPicking(false)
    void save([...members, name])
  }

  if (!group) {
    return (
      <div className="mc-card p-5">
        <Header />
        <p className="mt-3 text-xs text-muted-foreground">
          Es gibt noch kein Immer-bereit-Set. Es entsteht automatisch, sobald Lucys Hirn zugewiesen ist.
        </p>
        {dialogElement}
      </div>
    )
  }

  return (
    <div className="mc-card p-5 space-y-4">
      <Header />

      {/* Mitglieder */}
      <div className="space-y-1.5">
        {memberModels.length === 0 && (
          <div className="text-xs text-muted-foreground">Noch keine Modelle im Set.</div>
        )}
        {memberModels.map((m) => {
          const isWarm = running.includes(m.name)
          const prot = roleMeta(m.role).protected
          return (
            <div key={m.name} className="flex items-center justify-between gap-2 rounded-xl border border-border/30 bg-background/20 p-2.5">
              <div className="flex min-w-0 items-center gap-2.5">
                <span className={cn("h-2 w-2 shrink-0 rounded-full ring-2 ring-black/40", isWarm ? "bg-emerald-500 animate-pulse" : "bg-muted-foreground/40")}
                  title={isWarm ? "Gerade warm (bereit)" : "Reserviert — lädt bei Bedarf sofort"} />
                {m.role && <RoleLabel role={m.role} dense className="shrink-0" />}
                <span className="truncate font-mono text-xs font-semibold text-foreground" title={m.name}>{shortName(m.name)}</span>
                <span className="shrink-0 font-mono text-[10px] text-muted-foreground/70">{fmtSize(m.size_bytes)}</span>
              </div>
              {prot ? (
                <span className="shrink-0 text-[10px] font-semibold text-muted-foreground" title="Lebenswichtig — bleibt immer im Set.">🔒</span>
              ) : (
                <button
                  onClick={() => remove(m.name)}
                  disabled={busy}
                  className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg border border-border/40 text-muted-foreground transition-colors hover:bg-red-500/5 hover:text-red-400 cursor-pointer disabled:opacity-50"
                  title="Aus dem Immer-bereit-Set nehmen (lädt dann nur noch bei Bedarf)"
                >
                  <X className="h-3.5 w-3.5" />
                </button>
              )}
            </div>
          )
        })}
      </div>

      {/* Hinzufügen */}
      <button
        onClick={() => setPicking(true)}
        disabled={busy || eligible.length === 0}
        className="flex h-8 w-full items-center justify-center gap-1.5 rounded-lg border border-dashed border-border/50 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground transition-colors hover:border-primary/40 hover:text-primary cursor-pointer disabled:opacity-50"
      >
        <Plus className="h-3.5 w-3.5" /> Modell dauerhaft bereithalten
      </button>

      {/* Speicher-Budget */}
      <div className="rounded-xl border border-border/30 bg-background/20 p-3">
        <div className="mb-1.5 flex items-center justify-between text-[11px]">
          <span className="flex items-center gap-1.5 font-semibold uppercase tracking-wider text-muted-foreground" title="Modellgewichte + KV-Cache, berechnet aus den echten Architektur-Daten jedes Modells (Layer × KV-Köpfe × Kontext) und seiner KV-Quantisierung. So viel legt llama.cpp beim Laden wirklich für den eingestellten Kontext an — kein Aufschlag mehr.">
            <HardDrive className="h-3.5 w-3.5" /> Reserviert fürs Set
          </span>
          <span className="font-mono font-bold text-foreground">
            {reservedGb.toFixed(1)}{totalGb > 0 ? ` / ${totalGb.toFixed(0)}` : ""} GB
          </span>
        </div>
        <div className="h-2 w-full overflow-hidden rounded-full border border-border/30 bg-background/50">
          <div className={cn("h-full rounded-full transition-all duration-500", pct >= 85 ? "bg-red-500" : pct >= 65 ? "bg-amber-500" : "bg-teal-500")} style={{ width: `${pct}%` }} />
        </div>

        {bud && !bud.fits && (
          <p className="mt-2 flex items-start gap-1.5 text-[11px] text-amber-400">
            <AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" />
            <span>
              Zusammen mit dem größten Gelegenheits-Modell (~{bud.largest_ondemand_gb} GB) wird der Speicher knapp. Das Set
              bleibt dabei immer geladen — wird es wirklich eng, schlägt das Laden des großen Modells fehl (es wartet dann,
              statt das Set zu verdrängen).
            </span>
          </p>
        )}
        {bud && bud.fits && (
          <p className="mt-2 flex items-center gap-1.5 text-[11px] text-emerald-400">
            <Check className="h-3.5 w-3.5 shrink-0" /> Passt auch neben dem größten Gelegenheits-Modell — nichts wird verdrängt.
          </p>
        )}
      </div>

      {/* Auswahl-Modal */}
      {picking && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4 backdrop-blur-sm" role="dialog" aria-modal="true" aria-label="Modell zum Immer-bereit-Set hinzufügen">
          <div className="w-full max-w-md space-y-3 rounded-2xl border border-border/80 bg-card p-6 shadow-2xl animate-in fade-in zoom-in-95 duration-200">
            <div className="flex items-center justify-between border-b border-border/20 pb-2">
              <h3 className="text-xs font-bold uppercase tracking-wider text-primary">Dauerhaft bereithalten</h3>
              <button onClick={() => setPicking(false)} className="rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground cursor-pointer"><X className="h-4 w-4" /></button>
            </div>
            <p className="text-xs text-muted-foreground">Wähle ein Modell, das ohne Ladezeit bereitstehen soll:</p>
            <div className="max-h-60 space-y-1.5 overflow-y-auto pr-1">
              {eligible.map((m) => (
                <button
                  key={m.name}
                  onClick={() => add(m.name)}
                  className="flex w-full items-center justify-between gap-2 rounded-lg border border-border/30 bg-background/20 px-3 py-2.5 text-left transition-colors hover:bg-accent cursor-pointer"
                >
                  <span className="flex min-w-0 items-center gap-2">
                    {m.role && <RoleLabel role={m.role} dense className="shrink-0" />}
                    <span className="truncate font-mono text-xs font-semibold text-foreground">{shortName(m.name)}</span>
                  </span>
                  <span className="shrink-0 font-mono text-[10px] text-muted-foreground/70">{fmtSize(m.size_bytes)}</span>
                </button>
              ))}
            </div>
          </div>
        </div>
      )}

      {dialogElement}
    </div>
  )
}

function Header() {
  return (
    <div className="flex items-center gap-2">
      <Zap className="h-4.5 w-4.5 text-primary" />
      <div>
        <h2 className="text-sm font-bold uppercase tracking-wide text-foreground">Immer-bereit-Set</h2>
        <p className="text-[11px] text-muted-foreground">Diese Modelle hält Lucy immer bereit — sie antworten ohne Ladezeit.</p>
      </div>
    </div>
  )
}
