import { useState } from "react"
import {
  Inbox, Check, X, GitBranch, FileDiff, Loader2, AlertTriangle, Sparkles,
  ChevronDown, ChevronRight, Clock, Hammer, RefreshCw, Lightbulb, Send,
} from "lucide-react"
import { api, type AuftragItem, type IdeenItem, type SkillKandidat } from "@/lib/api"
import { useAuftragsbuch, useIdeen, useQueryClient, qk } from "@/lib/queries"
import { useDialog } from "@/lib/useDialog"
import { cn } from "@/lib/utils"

// Auftragsbuch = die Vorschlags-Inbox der Box. Werkstatt/Orchestrator liefern propose-only-
// Branches, der Traum liefert Skill-Kandidaten — hier fällt die Entscheidung per Klick.
// Annehmen (Branch) = Merge→Push→Deploy→Health mit Auto-Rollback; alles detached auf der Box.

const KIND_LABEL: Record<string, string> = {
  wartung: "Werkstatt",
  orchestrator: "Orchestrator",
  doku: "Doku",
  feature: "Feature",
}

const STATE_UI: Record<string, { label: string; cls: string; spin?: boolean }> = {
  laeuft: { label: "wird eingespielt …", cls: "text-sky-300 border-sky-500/40 bg-sky-500/10", spin: true },
  rollback: { label: "Rollback läuft …", cls: "text-amber-300 border-amber-500/40 bg-amber-500/10", spin: true },
  eingespielt: { label: "eingespielt ✓", cls: "text-emerald-300 border-emerald-500/40 bg-emerald-500/10" },
  fehlgeschlagen: { label: "fehlgeschlagen", cls: "text-red-300 border-red-500/40 bg-red-500/10" },
  zurueckgerollt: { label: "zurückgerollt", cls: "text-amber-300 border-amber-500/40 bg-amber-500/10" },
  kritisch: { label: "KRITISCH — Box prüfen", cls: "text-red-200 border-red-500/60 bg-red-500/20" },
  abgelehnt: { label: "abgelehnt", cls: "text-muted-foreground border-border/60 bg-background/30" },
}

function fmtWhen(ts: number | null | undefined): string {
  if (!ts) return "—"
  return new Date(ts * 1000).toLocaleString("de-DE", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" })
}

export function AuftragsbuchView() {
  const { data, isLoading, refetch, isFetching } = useAuftragsbuch()
  const qc = useQueryClient()
  const { showAlert, showConfirm, dialogElement } = useDialog()
  const [busy, setBusy] = useState<Record<string, boolean>>({})

  const reload = () => qc.invalidateQueries({ queryKey: qk.auftragsbuch })

  async function act(key: string, path: string, body: object, done?: string) {
    setBusy((b) => ({ ...b, [key]: true }))
    try {
      await api(path, { method: "POST", body: JSON.stringify(body) })
      if (done) showAlert("Erledigt", done)
      reload()
    } catch (e: any) {
      showAlert("Das hat nicht geklappt", String(e?.message || e))
    } finally {
      setBusy((b) => ({ ...b, [key]: false }))
    }
  }

  const items = data?.items ?? []
  const kandidaten = data?.skill_kandidaten ?? []
  const empty = items.length === 0 && kandidaten.length === 0

  return (
    <div className="space-y-6">
      <div className="flex items-center justify-between gap-3">
        <div>
          <h1 className="bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent">
            Auftragsbuch
          </h1>
          <p className="text-sm text-muted-foreground">
            Was die Box gebaut oder sich ausgedacht hat — du entscheidest. Annehmen spielt es mit Fangnetz ein (Health-Check, automatischer Rollback).
          </p>
        </div>
        <button onClick={() => refetch()}
          className="flex h-9 items-center gap-1.5 rounded-lg border border-border/60 bg-card/45 px-3 text-xs font-semibold text-muted-foreground hover:text-foreground hover:border-primary/40 transition-all cursor-pointer">
          <RefreshCw className={cn("h-3.5 w-3.5", isFetching && "animate-spin")} /> Aktualisieren
        </button>
      </div>

      {data && !data.available && (
        <div className="rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300">
          Das Auftragsbuch lebt auf der Box (liest die Gitea-Branches dort). Im lokalen Dev-Modus ist hier nichts zu sehen.
        </div>
      )}

      {isLoading && (
        <div className="flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground">
          <Loader2 className="h-4 w-4 animate-spin" /> Vorschläge werden geladen …
        </div>
      )}

      {data?.available && empty && (
        <div className="flex items-center gap-3 rounded-2xl border border-emerald-500/25 bg-emerald-500/5 p-5">
          <Check className="h-5 w-5 shrink-0 text-emerald-400" />
          <div>
            <p className="text-sm font-semibold text-foreground">Nichts zu entscheiden</p>
            <p className="text-xs text-muted-foreground">Keine offenen Vorschläge. Werkstatt, Orchestrator und Traum melden sich hier, sobald es etwas gibt.</p>
          </div>
        </div>
      )}

      <IdeenSection />

      {items.length > 0 && (
        <section className="space-y-3">
          <SectionLabel icon={GitBranch}>Fertige Patches ({items.length})</SectionLabel>
          {items.map((it) => (
            <ProposalCard key={`${it.repo ?? "mc2"}:${it.branch}`} item={it} busy={busy} onAct={act} showConfirm={showConfirm} />
          ))}
        </section>
      )}

      {kandidaten.length > 0 && (
        <section className="space-y-3">
          <SectionLabel icon={Sparkles}>Skill-Kandidaten aus dem Traum ({kandidaten.length})</SectionLabel>
          {kandidaten.map((k) => (
            <KandidatCard key={k.file} k={k} busy={busy} onAct={act} showConfirm={showConfirm} />
          ))}
        </section>
      )}

      {dialogElement}
    </div>
  )
}

function SectionLabel({ icon: Icon, children }: { icon: any; children: React.ReactNode }) {
  return (
    <p className="flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">
      <Icon className="h-3.5 w-3.5" /> {children}
    </p>
  )
}

// ── Ideen-Queue (natives Hermes-Kanban) ──────────────────────────────────────
// Eine Queue, drei Türen (Zentrale/Telegram/Lucy). Idee rein → die Box arbeitet sie
// selbst aus (Specifier) und baut (Werkstatt-Profil) → Code kommt als Karte zurück.

const IDEEN_STATE: Record<string, { label: string; cls: string; spin?: boolean }> = {
  triage: { label: "wird ausgearbeitet …", cls: "text-violet-300 border-violet-500/40 bg-violet-500/10", spin: true },
  todo: { label: "wartet", cls: "text-muted-foreground border-border/60 bg-background/30" },
  scheduled: { label: "geplant", cls: "text-muted-foreground border-border/60 bg-background/30" },
  ready: { label: "wartet", cls: "text-muted-foreground border-border/60 bg-background/30" },
  running: { label: "in Arbeit …", cls: "text-sky-300 border-sky-500/40 bg-sky-500/10", spin: true },
  review: { label: "im Review", cls: "text-amber-300 border-amber-500/40 bg-amber-500/10" },
  blocked: { label: "hängt — braucht dich", cls: "text-red-300 border-red-500/40 bg-red-500/10" },
  done: { label: "fertig ✓", cls: "text-emerald-300 border-emerald-500/40 bg-emerald-500/10" },
}

function IdeenSection() {
  const { data, isLoading } = useIdeen()
  const qc = useQueryClient()
  const { showAlert, dialogElement } = useDialog()
  const [text, setText] = useState("")
  const [sending, setSending] = useState(false)
  const [busy, setBusy] = useState<Record<string, boolean>>({})

  const items = data?.items ?? []
  const reload = () => qc.invalidateQueries({ queryKey: qk.ideen })

  async function submit() {
    const t = text.trim()
    if (t.length < 3 || sending) return
    setSending(true)
    try {
      // Lange Eingaben: Kurzfassung als Titel, alles als Notiz — der Specifier der Box
      // macht daraus ohnehin einen sauberen Auftrag.
      const titel = t.length > 160 ? t.slice(0, 157) + "…" : t
      await api("/api/ideen", { method: "POST", body: JSON.stringify({ titel, notiz: t.length > 160 ? t : "" }) })
      setText("")
      reload()
    } catch (e: any) {
      showAlert("Das hat nicht geklappt", String(e?.message || e))
    } finally {
      setSending(false)
    }
  }

  async function archive(id: string) {
    setBusy((b) => ({ ...b, [id]: true }))
    try {
      await api("/api/ideen/archivieren", { method: "POST", body: JSON.stringify({ id }) })
      reload()
    } catch (e: any) {
      showAlert("Das hat nicht geklappt", String(e?.message || e))
    } finally {
      setBusy((b) => ({ ...b, [id]: false }))
    }
  }

  // Windows-Dev / Queue nicht verfügbar → Sektion still weglassen (wie der Rest der Seite).
  if (data && !data.available) return null

  return (
    <section className="space-y-3">
      <SectionLabel icon={Lightbulb}>
        Ideen-Queue{data ? ` — ${data.offen} offen` : ""}
      </SectionLabel>
      <div className="rounded-2xl border border-border/60 bg-card/45 p-4 shadow-lg shadow-black/15 backdrop-blur-md space-y-3">
        <div className="flex gap-2">
          <input
            value={text}
            onChange={(e) => setText(e.target.value)}
            onKeyDown={(e) => { if (e.key === "Enter") submit() }}
            placeholder="Neue Idee — einfach hinschreiben, die Box macht den Rest"
            className="h-9 min-w-0 flex-1 rounded-lg border border-border/60 bg-background/50 px-3 text-sm text-foreground placeholder:text-muted-foreground/50 focus:border-primary/50 focus:outline-none"
          />
          <button
            onClick={submit}
            disabled={sending || text.trim().length < 3}
            className="flex h-9 shrink-0 items-center gap-1.5 rounded-lg border border-primary/40 bg-primary/10 px-3 text-[11px] font-bold uppercase tracking-wide text-primary transition-all hover:bg-primary/20 cursor-pointer disabled:opacity-50">
            {sending ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Send className="h-3.5 w-3.5" />} In die Queue
          </button>
        </div>
        <p className="text-[11px] text-muted-foreground">
          Eine Queue, alle Türen: hier, per Telegram oder im Gespräch mit Lucy. Die Box arbeitet Ideen selbst
          aus und baut — Code-Ergebnisse erscheinen als Karte unter „Fertige Patches", nichts davon geht ohne
          deinen Klick live. Nur reine Doku-Bagatellen zieht die Box nachts selbst ein (mit Fangnetz und Meldung).
        </p>

        {isLoading && (
          <p className="flex items-center gap-2 text-xs text-muted-foreground"><Loader2 className="h-3.5 w-3.5 animate-spin" /> Queue wird geladen …</p>
        )}
        {data?.fehler && (
          <p className="text-xs text-amber-300">Queue gerade nicht lesbar — neuer Versuch läuft.</p>
        )}

        {items.length > 0 && (
          <ul className="space-y-1.5">
            {items.map((it: IdeenItem) => {
              const ui = IDEEN_STATE[it.status] ?? { label: it.status, cls: "text-muted-foreground border-border/60 bg-background/30" }
              return (
                <li key={it.id} className="flex items-center gap-2 rounded-xl border border-border/40 bg-background/30 px-3 py-2">
                  <span className={cn("flex shrink-0 items-center gap-1.5 rounded-lg border px-2 py-0.5 text-[10px] font-bold", ui.cls)}>
                    {ui.spin && <Loader2 className="h-3 w-3 animate-spin" />}
                    {ui.label}
                  </span>
                  <span className="min-w-0 flex-1 truncate text-xs text-foreground" title={it.body || it.titel}>{it.titel}</span>
                  <span className="flex shrink-0 items-center gap-1 text-[10px] text-muted-foreground/70"><Clock className="h-3 w-3" />{fmtWhen(it.erstellt)}</span>
                  {(it.status === "done" || it.status === "blocked") && (
                    <button
                      disabled={!!busy[it.id]}
                      onClick={() => archive(it.id)}
                      title="Aus der Liste räumen (wandert ins Kanban-Archiv)"
                      className="shrink-0 rounded-md p-1 text-muted-foreground/60 transition-colors hover:text-foreground cursor-pointer disabled:opacity-50">
                      {busy[it.id] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <X className="h-3.5 w-3.5" />}
                    </button>
                  )}
                </li>
              )
            })}
          </ul>
        )}
        {data?.available && !isLoading && items.length === 0 && (
          <p className="text-xs text-muted-foreground/70">Queue ist leer — wirf der Box eine Idee zu.</p>
        )}
      </div>
      {dialogElement}
    </section>
  )
}

function StateBadge({ item }: { item: AuftragItem }) {
  const st = item.status
  if (!st) return null
  const ui = STATE_UI[st.state]
  if (!ui) return null
  return (
    <span className={cn("flex items-center gap-1.5 rounded-lg border px-2 py-0.5 text-[10px] font-bold", ui.cls)} title={st.detail}>
      {ui.spin && <Loader2 className="h-3 w-3 animate-spin" />}
      {ui.label}
    </span>
  )
}

function ProposalCard({
  item, busy, onAct, showConfirm,
}: {
  item: AuftragItem
  busy: Record<string, boolean>
  onAct: (key: string, path: string, body: object, done?: string) => void
  showConfirm: (title: string, msg: string, ok: () => void) => void
}) {
  const [open, setOpen] = useState(false)
  const [diff, setDiff] = useState<string | null>(null)
  const [diffLoading, setDiffLoading] = useState(false)
  const running = item.status?.state === "laeuft" || item.status?.state === "rollback"
  const decided = item.status?.state === "eingespielt"
  // Lucy-Karten (Desktop-App): Annehmen baut am PC (dist) statt auf der Box zu deployen.
  const repo = item.repo ?? "mc2"
  const isLucy = repo === "lucy"
  const k = `${repo}:${item.branch}`

  async function loadDiff() {
    const next = !open
    setOpen(next)
    if (next && diff === null) {
      setDiffLoading(true)
      try {
        const r = await api<{ diff: string; truncated: boolean }>(`/api/auftragsbuch/diff?branch=${encodeURIComponent(item.branch)}&repo=${repo}`)
        setDiff(r.diff + (r.truncated ? "\n… (gekürzt)" : ""))
      } catch (e: any) {
        setDiff(`(Diff nicht ladbar: ${e?.message || e})`)
      } finally {
        setDiffLoading(false)
      }
    }
  }

  return (
    <div className="rounded-2xl border border-border/60 bg-card/45 p-4 shadow-lg shadow-black/15 backdrop-blur-md space-y-3">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div className="min-w-0">
          <div className="flex flex-wrap items-center gap-2">
            <span className="rounded-md border border-primary/30 bg-primary/10 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-primary">
              {KIND_LABEL[item.kind] ?? item.kind}
            </span>
            {isLucy && (
              <span className="rounded-md border border-pink-500/40 bg-pink-500/10 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-pink-300"
                title="Vorschlag fürs Lucy-Repo — Annehmen baut die App am PC neu und startet sie neu, wenn sie läuft.">
                Lucy
              </span>
            )}
            <span className="font-mono text-[11px] text-muted-foreground truncate" title={item.branch}>{item.branch}</span>
            <span className="flex items-center gap-1 text-[10px] text-muted-foreground/70"><Clock className="h-3 w-3" />{fmtWhen(item.ts)}</span>
            <StateBadge item={item} />
          </div>
          <p className="mt-1.5 text-sm font-semibold text-foreground">{item.subject || "(ohne Titel)"}</p>
          {item.body && <p className="mt-1 whitespace-pre-wrap text-xs leading-relaxed text-muted-foreground line-clamp-6">{item.body}</p>}
        </div>

        {!running && !decided && (
          <div className="flex shrink-0 items-center gap-2">
            <button
              disabled={!!busy[k]}
              onClick={() =>
                showConfirm("Vorschlag annehmen?",
                  isLucy
                    ? `'${item.branch}' wird im Lucy-Repo auf main gemergt, dann baut dein PC die App neu (dist) und startet Lucy neu, falls sie gerade läuft. Der PC muss dafür an sein. Schlägt Build oder Start fehl, dreht die Box den Merge automatisch zurück — die laufende Lucy bleibt die alte.`
                    : `'${item.branch}' wird auf main gemergt und live eingespielt. Die Zentrale startet dabei kurz neu. Geht der Health-Check danach rot, rollt die Box automatisch zurück.${item.frontend_ohne_build ? "\n\nAchtung: Enthält Frontend-Quelltext ohne gebautes Bundle — die Oberfläche bleibt alt, bis am PC gebaut wird." : ""}`,
                  () => onAct(k, "/api/auftragsbuch/annehmen", { branch: item.branch, repo }))}
              className="flex h-9 items-center gap-1.5 rounded-lg border border-emerald-500/40 bg-emerald-500/10 px-3 text-[11px] font-bold uppercase tracking-wide text-emerald-300 transition-all hover:bg-emerald-500/20 cursor-pointer disabled:opacity-50">
              {busy[k] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Check className="h-3.5 w-3.5" />} Annehmen
            </button>
            <button
              disabled={!!busy["ab:" + k]}
              onClick={() =>
                showConfirm("Vorschlag ablehnen?",
                  `Der Branch '${item.branch}' wird auf Gitea gelöscht. Die Arbeit bleibt in der Historie, aber die Karte verschwindet.`,
                  () => onAct("ab:" + k, "/api/auftragsbuch/ablehnen", { branch: item.branch, repo }))}
              className="flex h-9 items-center gap-1.5 rounded-lg border border-red-500/30 bg-red-500/5 px-3 text-[11px] font-bold uppercase tracking-wide text-red-300/90 transition-all hover:bg-red-500/15 cursor-pointer disabled:opacity-50">
              {busy["ab:" + k] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <X className="h-3.5 w-3.5" />} Ablehnen
            </button>
          </div>
        )}
      </div>

      {item.status && (running || !decided) && item.status.detail && (
        <p className="text-[11px] text-muted-foreground">{item.status.detail}</p>
      )}

      <div className="flex flex-wrap items-center gap-2 text-[11px] text-muted-foreground">
        <span className="rounded bg-background/40 px-1.5 py-0.5 font-mono">{item.ahead} Commit{item.ahead === 1 ? "" : "s"}</span>
        {item.shortstat && <span className="rounded bg-background/40 px-1.5 py-0.5 font-mono">{item.shortstat.trim()}</span>}
        {item.behind > 0 && (
          <span className="flex items-center gap-1 rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-300" title="main ist seit diesem Vorschlag weitergelaufen">
            <AlertTriangle className="h-3 w-3" /> {item.behind} hinter main
          </span>
        )}
        {item.frontend_ohne_build && (
          <span className="flex items-center gap-1 rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-300" title="Die Box kann kein Frontend bauen (kein Node) — nach dem Annehmen bleibt die Oberfläche alt, bis am PC gebaut wird.">
            <AlertTriangle className="h-3 w-3" /> Frontend ohne Build
          </span>
        )}
      </div>

      <div>
        <button onClick={loadDiff}
          className="flex items-center gap-1.5 text-[11px] font-semibold text-muted-foreground hover:text-foreground transition-colors cursor-pointer">
          {open ? <ChevronDown className="h-3.5 w-3.5" /> : <ChevronRight className="h-3.5 w-3.5" />}
          <FileDiff className="h-3.5 w-3.5" /> {item.files.length} Datei{item.files.length === 1 ? "" : "en"} — Änderungen ansehen
        </button>
        {open && (
          <div className="mt-2 space-y-2">
            <div className="flex flex-wrap gap-1.5">
              {item.files.map((f) => (
                <span key={f.path} className="rounded bg-background/40 px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground">
                  <b className={cn(f.status.startsWith("A") && "text-emerald-400", f.status.startsWith("D") && "text-red-400", f.status.startsWith("M") && "text-sky-400")}>{f.status}</b> {f.path}
                </span>
              ))}
              {item.files_truncated && <span className="text-[10px] text-muted-foreground/60">…</span>}
            </div>
            <div className="max-h-96 overflow-auto rounded-xl border border-border/50 bg-background/50 p-3">
              {diffLoading
                ? <span className="text-[11px] text-muted-foreground">Diff wird geladen …</span>
                : <pre className="text-[10px] leading-relaxed text-foreground/80 whitespace-pre">{
                    (diff ?? "").split("\n").map((ln, i) => (
                      <span key={i} className={cn("block", ln.startsWith("+") && !ln.startsWith("+++") && "text-emerald-300/90 bg-emerald-500/5", ln.startsWith("-") && !ln.startsWith("---") && "text-red-300/90 bg-red-500/5", (ln.startsWith("@@") || ln.startsWith("diff ")) && "text-sky-300/90")}>{ln || " "}</span>
                    ))
                  }</pre>}
            </div>
          </div>
        )}
      </div>
    </div>
  )
}

function KandidatCard({
  k, busy, onAct, showConfirm,
}: {
  k: SkillKandidat
  busy: Record<string, boolean>
  onAct: (key: string, path: string, body: object, done?: string) => void
  showConfirm: (title: string, msg: string, ok: () => void) => void
}) {
  const [open, setOpen] = useState(false)
  return (
    <div className="rounded-2xl border border-violet-500/25 bg-violet-500/[0.04] p-4 shadow-lg shadow-black/15 backdrop-blur-md space-y-3">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div className="min-w-0">
          <div className="flex flex-wrap items-center gap-2">
            <span className="rounded-md border border-violet-500/30 bg-violet-500/10 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-violet-300">Traum-Idee</span>
            <span className="font-mono text-[11px] text-muted-foreground truncate">{k.file}</span>
            <span className="flex items-center gap-1 text-[10px] text-muted-foreground/70"><Clock className="h-3 w-3" />{fmtWhen(k.mtime)}</span>
          </div>
          <p className="mt-1.5 text-sm font-semibold text-foreground">{k.title}</p>
        </div>
        <div className="flex shrink-0 items-center gap-2">
          <button
            disabled={!!busy["sk:" + k.file]}
            onClick={() =>
              showConfirm("Werkstatt beauftragen?",
                `Der Kandidat '${k.title}' geht als Auftrag an die Werkstatt (Orchestrator, propose-only). Das Ergebnis erscheint später als neuer Patch-Vorschlag hier im Auftragsbuch — nichts geht ungefragt live.`,
                () => onAct("sk:" + k.file, "/api/auftragsbuch/skill/annehmen", { file: k.file },
                  "Die Werkstatt arbeitet. Das Ergebnis taucht als neuer Vorschlag im Auftragsbuch auf (das kann einige Minuten dauern)."))}
            className="flex h-9 items-center gap-1.5 rounded-lg border border-violet-500/40 bg-violet-500/10 px-3 text-[11px] font-bold uppercase tracking-wide text-violet-300 transition-all hover:bg-violet-500/20 cursor-pointer disabled:opacity-50">
            {busy["sk:" + k.file] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Hammer className="h-3.5 w-3.5" />} Beauftragen
          </button>
          <button
            disabled={!!busy["skab:" + k.file]}
            onClick={() =>
              showConfirm("Kandidat verwerfen?",
                `'${k.title}' wandert ins Archiv des Wissens-Vaults (verworfen/). Der Traum schlägt ihn nicht erneut vor.`,
                () => onAct("skab:" + k.file, "/api/auftragsbuch/skill/ablehnen", { file: k.file }))}
            className="flex h-9 items-center gap-1.5 rounded-lg border border-red-500/30 bg-red-500/5 px-3 text-[11px] font-bold uppercase tracking-wide text-red-300/90 transition-all hover:bg-red-500/15 cursor-pointer disabled:opacity-50">
            {busy["skab:" + k.file] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <X className="h-3.5 w-3.5" />} Verwerfen
          </button>
        </div>
      </div>
      <button onClick={() => setOpen((o) => !o)}
        className="flex items-center gap-1.5 text-[11px] font-semibold text-muted-foreground hover:text-foreground transition-colors cursor-pointer">
        {open ? <ChevronDown className="h-3.5 w-3.5" /> : <ChevronRight className="h-3.5 w-3.5" />}
        <Inbox className="h-3.5 w-3.5" /> Vorschlag lesen
      </button>
      {open && (
        <pre className="max-h-72 overflow-auto rounded-xl border border-border/50 bg-background/50 p-3 text-[11px] leading-relaxed text-foreground/85 whitespace-pre-wrap">{k.preview}</pre>
      )}
    </div>
  )
}
