import { useEffect, useMemo, useRef, useState } from "react"
import Graph from "graphology"
import forceAtlas2 from "graphology-layout-forceatlas2"
import Sigma from "sigma"
import { Trash2, Sparkles, Share2, RefreshCw } from "lucide-react"
import { type MemoryGraph } from "@/lib/api"

// Sigma.js v3 + graphology: graph-optimiertes WebGL (kein three.js-Ballast) → skaliert auf tausende
// Fakten flüssig. Im App-Look gestylt; Hover hebt den Knoten + seine Nachbarn hervor, der Rest dimmt.

const CAT_COLOR: Record<string, string> = {
  identity: "#22d3ee", knowledge: "#6366f1", rules: "#a78bfa", events: "#fbbf24",
}
const CAT_LABEL: Record<string, string> = {
  identity: "Identität", knowledge: "Wissen", rules: "Regeln", events: "Ereignisse",
}
const AUTO = new Set(["auto", "agent", "hermes"])
const DIM_NODE = "#1f2937"
const DIM_EDGE = "#0f172a"
const EDGE = "#243044"
const EDGE_HI = "#4b5563"

export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id: string) => void }) {
  const [selected, setSelected] = useState<string | null>(null)
  const containerRef = useRef<HTMLDivElement>(null)
  const sigmaRef = useRef<Sigma | null>(null)
  const graphRef = useRef<Graph | null>(null)
  const active = useRef<string | null>(null)        // hervorgehobener Knoten (hover oder Auswahl)
  const selectedRef = useRef<string | null>(null)   // aktuelle Auswahl (für leaveNode ohne Effekt-Neulauf)

  const degree = useMemo(() => {
    const d: Record<string, number> = {}
    data.edges.forEach((e) => { d[e.source] = (d[e.source] || 0) + 1; d[e.target] = (d[e.target] || 0) + 1 })
    return d
  }, [data])

  // Graph (neu)bauen + Layout + Sigma rendern, wenn sich die Daten ändern.
  useEffect(() => {
    if (!containerRef.current || !data.nodes.length) return
    const graph = new Graph()
    const N = data.nodes.length
    data.nodes.forEach((n, i) => {
      const a = (2 * Math.PI * i) / N
      graph.addNode(n.id, {
        x: Math.cos(a), y: Math.sin(a),                 // Startposition (Kreis) für ForceAtlas2
        size: 4 + Math.min(degree[n.id] || 0, 12) * 1.4,
        color: CAT_COLOR[n.category] || "#64748b",
        label: n.content.length > 48 ? n.content.slice(0, 47) + "…" : n.content,
      })
    })
    data.edges.forEach((e) => {
      if (graph.hasNode(e.source) && graph.hasNode(e.target) && !graph.hasEdge(e.source, e.target))
        graph.addEdge(e.source, e.target, { size: 0.6 + (e.weight || 0), color: EDGE })
    })
    forceAtlas2.assign(graph, { iterations: 220, settings: forceAtlas2.inferSettings(graph) })
    graphRef.current = graph

    const renderer = new Sigma(graph, containerRef.current, {
      renderLabels: true,
      labelColor: { color: "#cbd5e1" },
      labelSize: 11,
      labelWeight: "500",
      labelDensity: 0.6,
      labelRenderedSizeThreshold: N > 120 ? 12 : 0,   // bei vielen Knoten nur große labeln (entklumpen)
      defaultEdgeColor: EDGE,
      minCameraRatio: 0.1,
      maxCameraRatio: 4,
      nodeReducer: (id, attrs) => {
        const a = active.current
        if (!a) return attrs
        if (id === a || graph.areNeighbors(a, id)) return attrs
        return { ...attrs, color: DIM_NODE, label: "" }
      },
      edgeReducer: (edge, attrs) => {
        const a = active.current
        if (!a) return attrs
        const ext = graph.extremities(edge)
        if (ext[0] === a || ext[1] === a) return { ...attrs, color: EDGE_HI, size: (attrs.size || 1) * 1.6 }
        return { ...attrs, color: DIM_EDGE }
      },
    })
    sigmaRef.current = renderer
    let dragged: string | null = null        // gerade gezogener Knoten (für Hover/Drag-Logik)

    renderer.on("clickNode", ({ node }) => setSelected(node))
    renderer.on("clickStage", () => setSelected(null))
    renderer.on("enterNode", ({ node }) => { if (!dragged) { active.current = node; renderer.refresh() } containerRef.current!.style.cursor = dragged ? "grabbing" : "grab" })
    renderer.on("leaveNode", () => { if (!dragged) { active.current = selectedRef.current; renderer.refresh(); containerRef.current!.style.cursor = "default" } })

    // Knoten ziehen (Sigma hat kein eingebautes Dragging → Standard-Muster mit dem Mouse-Captor).
    renderer.on("downNode", (e) => {
      dragged = e.node
      graph.setNodeAttribute(dragged, "highlighted", true)
      if (!renderer.getCustomBBox()) renderer.setCustomBBox(renderer.getBBox())
      containerRef.current!.style.cursor = "grabbing"
    })
    const mc = renderer.getMouseCaptor()
    mc.on("mousemovebody", (e) => {
      if (!dragged) return
      const pos = renderer.viewportToGraph(e)
      graph.setNodeAttribute(dragged, "x", pos.x)
      graph.setNodeAttribute(dragged, "y", pos.y)
      e.preventSigmaDefault(); e.original.preventDefault(); e.original.stopPropagation()
    })
    mc.on("mouseup", () => {
      if (dragged) graph.removeNodeAttribute(dragged, "highlighted")
      dragged = null
      if (containerRef.current) containerRef.current.style.cursor = "default"
    })

    return () => { renderer.kill(); sigmaRef.current = null; graphRef.current = null }
  }, [data, degree])

  // Auswahl spiegelt die Hervorhebung (auch ohne Hover).
  useEffect(() => {
    selectedRef.current = selected
    active.current = selected
    sigmaRef.current?.refresh()
  }, [selected])

  // Layout neu berechnen (nach manuellem Verschieben „aufräumen").
  const relayout = () => {
    const g = graphRef.current, s = sigmaRef.current
    if (!g || !s) return
    forceAtlas2.assign(g, { iterations: 220, settings: forceAtlas2.inferSettings(g) })
    s.refresh()
  }

  const sel = selected ? data.nodes.find((n) => n.id === selected) ?? null : null
  const neighbors = useMemo(() => {
    if (!selected) return []
    const ids = new Set<string>()
    data.edges.forEach((e) => {
      if (e.source === selected) ids.add(e.target)
      if (e.target === selected) ids.add(e.source)
    })
    return data.nodes.filter((n) => ids.has(n.id))
  }, [selected, data])

  if (!data.nodes.length) {
    return (
      <div className="text-xs text-muted-foreground border border-dashed border-border/60 rounded-2xl p-12 text-center">
        Noch keine Fakten — der Graph füllt sich, sobald Hermes lernt oder du Einträge anlegst.
      </div>
    )
  }

  return (
    <div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-3">
      <div className="relative h-[calc(100vh-13rem)] min-h-[560px] rounded-2xl border border-border/60 bg-[#070a0f] overflow-hidden">
        <div ref={containerRef} className="absolute inset-0" />
        <button onClick={relayout} title="Knoten neu anordnen"
          className="absolute right-3 top-3 z-10 flex items-center gap-1.5 rounded-lg border border-border/60 bg-background/50 px-2.5 py-1.5 text-[11px] text-muted-foreground hover:text-foreground backdrop-blur-sm transition-colors">
          <RefreshCw className="h-3.5 w-3.5" /> Neu anordnen
        </button>
        {/* Legende */}
        <div className="absolute left-3 bottom-3 flex flex-wrap gap-2 rounded-lg bg-background/40 px-2.5 py-1.5 backdrop-blur-sm">
          {Object.entries(CAT_LABEL).map(([k, l]) => (
            <span key={k} className="flex items-center gap-1 text-[10px] text-muted-foreground">
              <span className="h-2 w-2 rounded-full" style={{ background: CAT_COLOR[k] }} />{l}
            </span>
          ))}
        </div>
      </div>

      <div className="rounded-2xl border border-border/60 bg-card/45 backdrop-blur-md p-4 h-[calc(100vh-13rem)] min-h-[560px] overflow-y-auto scrollbar-thin">
        {sel ? (
          <>
            <div className="flex items-center gap-2 mb-3">
              <span className="w-3 h-3 rounded-full" style={{ background: CAT_COLOR[sel.category], boxShadow: `0 0 0 2px ${AUTO.has(sel.source) ? "#34d399" : "#475569"}` }} />
              <span className="text-[10px] font-bold uppercase tracking-wider" style={{ color: CAT_COLOR[sel.category] }}>
                {CAT_LABEL[sel.category] || sel.category}
              </span>
              <span className={`ml-auto text-[10px] font-mono flex items-center gap-1 ${AUTO.has(sel.source) ? "text-emerald-400" : "text-muted-foreground/70"}`}>
                {AUTO.has(sel.source) && <Sparkles className="h-2.5 w-2.5" />}{sel.source}
              </span>
            </div>
            <div className="text-sm text-foreground leading-relaxed mb-4 break-words">{sel.content}</div>
            <div className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground/70 mb-2 flex items-center gap-1">
              <Share2 className="h-3 w-3" /> verwandte Fakten
            </div>
            <div className="flex flex-col gap-1.5 mb-4">
              {neighbors.length ? neighbors.map((n) => (
                <button key={n.id} onClick={() => setSelected(n.id)}
                  className="flex items-center gap-2 text-[11px] text-muted-foreground hover:text-foreground text-left transition-colors">
                  <span className="w-1.5 h-1.5 rounded-full shrink-0" style={{ background: CAT_COLOR[n.category] }} />
                  <span className="truncate">{n.content}</span>
                </button>
              )) : <span className="text-[11px] text-muted-foreground/60">—</span>}
            </div>
            <button onClick={() => { onDelete(sel.id); setSelected(null) }}
              className="flex items-center gap-1.5 text-[11px] text-red-400 border border-red-500/20 rounded-lg px-2.5 py-1.5 hover:bg-red-500/5 transition-all">
              <Trash2 className="h-3.5 w-3.5" /> vergessen
            </button>
          </>
        ) : (
          <div className="h-full flex flex-col items-center justify-center text-center text-muted-foreground/70 gap-2 pt-24">
            <Share2 className="h-6 w-6" />
            <div className="text-xs max-w-[180px] leading-relaxed">
              Auf einen Knoten klicken, um den Fakt und seine semantischen Nachbarn zu sehen. Hover hebt das Netz hervor.
            </div>
          </div>
        )}
      </div>
    </div>
  )
}
