import { useEffect, useMemo, useState } from "react"
import { Library, FileText, Sparkles, Loader2, FolderOpen, Search } from "lucide-react"
import { api, type WissenDatei, type WissenFile } from "@/lib/api"
import { useWissen, useMemoryGraph } from "@/lib/queries"
import { GraphView } from "./GraphView"
import { GraphErrorBoundary } from "./GraphErrorBoundary"
import { cn } from "@/lib/utils"

// Wissens-Vault: die nächtlichen Traum-Notizen der Box als klickbares Wiki.
// [[wiki-links]] springen zur verlinkten Notiz; Markdown wird leichtgewichtig gerendert
// (bewusst ohne externe Markdown-Lib — Überschriften, Listen, fett/kursiv, Code reichen hier).

const DIR_LABEL: Record<string, string> = {
  "": "Allgemein",
  traeume: "Träume",
  muster: "Muster",
  "skill-kandidaten": "Skill-Kandidaten",
  "skill-kandidaten/beauftragt": "Skill-Kandidaten · beauftragt",
  "skill-kandidaten/verworfen": "Skill-Kandidaten · verworfen",
}

export function WissenView() {
  const { data, isLoading } = useWissen()
  const [active, setActive] = useState<string | null>(null)
  const [doc, setDoc] = useState<WissenDatei | null>(null)
  const [docLoading, setDocLoading] = useState(false)
  const [q, setQ] = useState("")
  const [showGraph, setShowGraph] = useState(false)
  
  const { data: graphData, isLoading: graphLoading } = useMemoryGraph(showGraph)

  const files = useMemo(() => data?.files ?? [], [data])

  // Name → Pfad für [[wiki-links]] (Dateiname ohne .md, case-insensitive).
  const byName = useMemo(() => {
    const m = new Map<string, string>()
    for (const f of files) m.set(f.name.toLowerCase(), f.path)
    return m
  }, [files])

  const filtered = useMemo(() => {
    if (!q.trim()) return files
    const ql = q.toLowerCase()
    return files.filter((f) => f.title.toLowerCase().includes(ql) || f.path.toLowerCase().includes(ql))
  }, [files, q])

  const groups = useMemo(() => {
    const g: { dir: string; files: WissenFile[] }[] = []
    for (const f of filtered) {
      const found = g.find((x) => x.dir === f.dir)
      if (found) found.files.push(f)
      else g.push({ dir: f.dir, files: [f] })
    }
    // INDEX zuerst, dann Träume, dann Rest alphabetisch
    return g.sort((a, b) => (a.dir === "" ? -1 : b.dir === "" ? 1 : a.dir.localeCompare(b.dir)))
  }, [filtered])

  // Beim ersten Laden: INDEX.md öffnen, falls vorhanden.
  useEffect(() => {
    if (!active && files.length) {
      const index = files.find((f) => f.path.toLowerCase() === "index.md")
      setActive(index?.path ?? files[0].path)
    }
  }, [files, active])

  useEffect(() => {
    if (!active) return
    setDocLoading(true)
    api<WissenDatei>(`/api/wissen/datei?pfad=${encodeURIComponent(active)}`)
      .then(setDoc)
      .catch(() => setDoc({ path: active, content: "*(Notiz nicht ladbar)*", mtime: 0 }))
      .finally(() => setDocLoading(false))
  }, [active])

  const openWikiLink = (name: string) => {
    const path = byName.get(name.toLowerCase())
    if (path) setActive(path)
  }

  return (
    <div className="space-y-5">
      <div className="flex items-center justify-between">
        <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">
            Wissen
          </h1>
          <p className="text-sm text-muted-foreground">
            Was sich die Box nachts erschließt — Traum-Notizen, Muster und Skill-Ideen aus dem Wissens-Vault, verlinkt wie ein Wiki.
          </p>
        </div>
        {files.length > 0 && (
          <div className="flex rounded-lg border border-border/60 bg-card p-1">
            <button
              onClick={() => setShowGraph(false)}
              className={cn("rounded-md px-3 py-1.5 text-xs font-medium transition-colors", !showGraph ? "bg-primary text-primary-foreground shadow" : "text-muted-foreground hover:bg-muted")}
            >
              Liste
            </button>
            <button
              onClick={() => setShowGraph(true)}
              className={cn("rounded-md px-3 py-1.5 text-xs font-medium transition-colors", showGraph ? "bg-primary text-primary-foreground shadow" : "text-muted-foreground hover:bg-muted")}
            >
              Graph
            </button>
          </div>
        )}
      </div>

      {data && !data.available && (
        <div className="rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300">
          Der Wissens-Vault liegt auf der Box (~/wissens-vault) — im lokalen Dev-Modus nicht verfügbar.
        </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" /> Vault wird geladen …
        </div>
      )}

      {data?.available && files.length === 0 && (
        <div className="rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground">
          Noch keine Notizen. Der nächtliche Traum (03:15) legt sie an — morgen früh steht hier die erste.
        </div>
      )}

      {showGraph ? (
        <div className="mc-card flex h-[70vh] flex-col overflow-hidden p-0 relative">
          {graphLoading && (
            <div className="absolute inset-0 flex items-center justify-center bg-background/50 z-10">
              <div className="flex items-center gap-2 rounded-2xl border border-border/60 bg-card p-6 text-xs text-muted-foreground">
                <Loader2 className="h-4 w-4 animate-spin" /> Graph wird berechnet …
              </div>
            </div>
          )}
          <GraphErrorBoundary>
            {graphData && (
              <GraphView
                data={graphData}
                selectedNodeId={active}
                onNodeSelect={(id) => {
                  if (id) {
                    setActive(id)
                    setShowGraph(false)
                  }
                }}
              />
            )}
          </GraphErrorBoundary>
        </div>
      ) : files.length > 0 && (
        <div className="flex flex-col gap-4 lg:flex-row">
          {/* Datei-Liste */}
          <aside className="w-full shrink-0 space-y-3 lg:w-72">
            <div className="relative">
              <input value={q} onChange={(e) => setQ(e.target.value)} type="search"
                placeholder="Notizen durchsuchen…" aria-label="Notizen durchsuchen"
                className="h-9 w-full rounded-lg border border-border/60 bg-card/45 pl-8 pr-3 text-xs text-foreground outline-none focus:ring-1 focus:ring-primary/50" />
              <Search className="absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground" />
            </div>
            <div className="max-h-[70vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin">
              {groups.map(({ dir, files: gf }) => (
                <div key={dir || "__root"}>
                  <p className="mb-1 flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60">
                    <FolderOpen className="h-3 w-3" /> {DIR_LABEL[dir] ?? dir}
                  </p>
                  <div className="space-y-0.5">
                    {gf.map((f) => (
                      <button key={f.path} onClick={() => setActive(f.path)}
                        className={cn("flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs transition-all cursor-pointer",
                          active === f.path ? "bg-primary/15 text-primary" : "text-muted-foreground hover:bg-accent hover:text-foreground")}>
                        <FileText className="h-3.5 w-3.5 shrink-0" />
                        <span className="truncate" title={f.title}>{f.name}</span>
                        {f.neu && <span className="ml-auto flex items-center gap-0.5 rounded bg-violet-500/15 px-1 py-0.5 text-[8px] font-bold uppercase text-violet-300"><Sparkles className="h-2.5 w-2.5" />neu</span>}
                      </button>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          </aside>

          {/* Inhalt */}
          <div className="min-w-0 flex-1 mc-card p-5">
            {docLoading ? (
              <div className="flex items-center gap-2 text-xs text-muted-foreground"><Loader2 className="h-4 w-4 animate-spin" /> Notiz wird geladen …</div>
            ) : doc ? (
              <>
                <div className="mb-3 flex items-center justify-between border-b border-border/40 pb-2">
                  <span className="flex items-center gap-1.5 font-mono text-[11px] text-muted-foreground"><Library className="h-3.5 w-3.5" /> {doc.path}</span>
                  {doc.mtime > 0 && (
                    <span className="text-[10px] text-muted-foreground/60">
                      Stand {new Date(doc.mtime * 1000).toLocaleString("de-DE", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" })}
                    </span>
                  )}
                </div>
                <Markdown text={doc.content} onWikiLink={openWikiLink} known={byName} />
              </>
            ) : null}
          </div>
        </div>
      )}
    </div>
  )
}

// ── Leichtgewichtiger Markdown-Renderer (Zeilen-basiert, XSS-frei da reines JSX) ──
function Markdown({ text, onWikiLink, known }: {
  text: string
  onWikiLink: (name: string) => void
  known: Map<string, string>
}) {
  const lines = text.split("\n")
  const out: React.ReactNode[] = []
  let list: React.ReactNode[] = []
  let code: string[] | null = null

  const flushList = (key: string) => {
    if (list.length) {
      out.push(<ul key={key} className="mb-3 ml-4 list-disc space-y-1">{list}</ul>)
      list = []
    }
  }

  lines.forEach((raw, i) => {
    const key = `l${i}`
    if (code !== null) {
      if (raw.trimEnd() === "```") {
        out.push(<pre key={key} className="mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px] leading-relaxed text-foreground/85">{code.join("\n")}</pre>)
        code = null
      } else code.push(raw)
      return
    }
    if (raw.trimStart().startsWith("```")) { flushList(key); code = []; return }

    const line = raw.trimEnd()
    if (!line.trim()) { flushList(key); return }

    const h = line.match(/^(#{1,4})\s+(.*)$/)
    if (h) {
      flushList(key)
      const level = h[1].length
      const cls = level === 1 ? "text-lg font-bold mt-1 mb-3" : level === 2 ? "text-base font-bold mt-4 mb-2" : "text-sm font-bold mt-3 mb-1.5"
      out.push(<p key={key} className={cn(cls, "font-space text-foreground")}>{inline(h[2], onWikiLink, known, key)}</p>)
      return
    }
    const li = line.match(/^\s*[-*•]\s+(.*)$/)
    if (li) {
      list.push(<li key={key} className="text-xs leading-relaxed text-foreground/85">{inline(li[1], onWikiLink, known, key)}</li>)
      return
    }
    flushList(key)
    out.push(<p key={key} className="mb-2 text-xs leading-relaxed text-foreground/85">{inline(line, onWikiLink, known, key)}</p>)
  })
  flushList("end")
  if (code !== null) out.push(<pre key="code-end" className="mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px]">{(code as string[]).join("\n")}</pre>)

  return <div className="max-w-3xl">{out}</div>
}

// Inline: [[wiki-links]], **fett**, *kursiv*, `code` — per Regex-Split, reines JSX (kein HTML-Inject).
function inline(text: string, onWikiLink: (n: string) => void, known: Map<string, string>, keyBase: string): React.ReactNode[] {
  const parts = text.split(/(\[\[[^\]]+\]\]|\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g)
  return parts.map((p, i) => {
    const key = `${keyBase}-${i}`
    const wiki = p.match(/^\[\[([^\]|]+)(?:\|([^\]]+))?\]\]$/)
    if (wiki) {
      const target = wiki[1].trim()
      const label = (wiki[2] ?? wiki[1]).trim()
      const exists = known.has(target.toLowerCase())
      return exists ? (
        <button key={key} onClick={() => onWikiLink(target)}
          className="rounded bg-primary/10 px-1 text-primary underline decoration-primary/40 underline-offset-2 hover:bg-primary/20 cursor-pointer">{label}</button>
      ) : (
        <span key={key} className="rounded bg-background/40 px-1 text-muted-foreground" title="Notiz existiert (noch) nicht">{label}</span>
      )
    }
    if (p.startsWith("**") && p.endsWith("**")) return <b key={key} className="font-semibold text-foreground">{p.slice(2, -2)}</b>
    if (p.startsWith("*") && p.endsWith("*") && p.length > 2) return <i key={key}>{p.slice(1, -1)}</i>
    if (p.startsWith("`") && p.endsWith("`")) return <code key={key} className="rounded bg-background/50 px-1 font-mono text-[11px] text-teal-300">{p.slice(1, -1)}</code>
    return <span key={key}>{p}</span>
  })
}
