import { useEffect, useMemo, useRef, useState } from "react"
import ForceGraph2D from "react-force-graph-2d"
import { Trash2, Sparkles, Share2, RefreshCw } from "lucide-react"
import { type MemoryGraph } from "@/lib/api"

const CAT_COLOR: Record<string, string> = {
  identity: "#00f5ff", knowledge: "#3b82f6", rules: "#d946ef", 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_EDGE = "rgba(15, 23, 42, 0.01)"
const EDGE = "rgba(255, 255, 255, 0.05)"
const EDGE_HI = "rgba(99, 102, 241, 0.85)"

export function GraphView({ data, onDelete }: { data: MemoryGraph; onDelete: (id: string) => void }) {
  const [selected, setSelected] = useState<string | null>(null)
  const [hovered, setHovered] = useState<string | null>(null)
  const containerRef = useRef<HTMLDivElement>(null)
  const fgRef = useRef<any>(null)

  const [dimensions, setDimensions] = useState({ width: 500, height: 560 })

  // ResizeObserver für präzise Canvas-Dimensionen im Layout
  useEffect(() => {
    if (!containerRef.current) return
    const resizeObserver = new ResizeObserver((entries) => {
      for (let entry of entries) {
        if (entry.contentRect.width > 0 && entry.contentRect.height > 0) {
          setDimensions({
            width: Math.floor(entry.contentRect.width),
            height: Math.floor(entry.contentRect.height),
          })
        }
      }
    })
    resizeObserver.observe(containerRef.current)
    return () => resizeObserver.disconnect()
  }, [])

  // Graphology-Daten in react-force-graph-2d-kompatibles Format konvertieren
  const graphData = useMemo(() => {
    return {
      nodes: data.nodes.map((n) => ({ ...n })),
      links: data.edges.map((e) => ({
        source: e.source,
        target: e.target,
        weight: e.weight || 0,
      })),
    }
  }, [data])

  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])

  // D3 Force Engine tunen (Luftige Struktur, kein Überlappen)
  useEffect(() => {
    const fg = fgRef.current
    if (!fg) return
    
    // Sicherstellen, dass D3-Kräfte existieren, bevor wir aufgerufen werden
    const charge = fg.d3Force("charge")
    if (charge) charge.strength(-140)
    
    const link = fg.d3Force("link")
    if (link) link.distance(60)
  }, [graphData])

  const activeNode = hovered || selected

  // Layout zurücksetzen/neu anschubsen
  const relayout = () => {
    if (!fgRef.current) return
    fgRef.current.zoomToFit(400, 40)
  }

  const sel = selected ? data.nodes.find((n) => n.id === selected) ?? null : null
  
  // Nachbarn über das statische, sichere Props-Array ermitteln
  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])

  // Nachbarmenge für extrem schnellen Lookup im Render-Loop
  const connectedNodes = useMemo(() => {
    if (!activeNode) return new Set<string>()
    const set = new Set<string>()
    data.edges.forEach((e) => {
      if (e.source === activeNode) set.add(e.target)
      if (e.target === activeNode) set.add(e.source)
    })
    return set
  }, [activeNode, 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 overflow-hidden"
        ref={containerRef}
        style={{ background: "radial-gradient(circle at center, rgba(59, 130, 246, 0.08) 0%, #030712 100%)" }}>
        
        <ForceGraph2D
          ref={fgRef}
          width={dimensions.width}
          height={dimensions.height}
          graphData={graphData}
          backgroundColor="rgba(0,0,0,0)"
          nodeRelSize={4}
          cooldownTicks={100}
          
          // Hover- und Klick-Logik
          onNodeClick={(node: any) => setSelected(node.id)}
          onBackgroundClick={() => setSelected(null)}
          onNodeHover={(node: any) => {
            setHovered(node ? node.id : null)
            if (containerRef.current) {
              containerRef.current.style.cursor = node ? "pointer" : "default"
            }
          }}
          
          // Kanten-Animationen & Partikelfluss
          linkColor={(link: any) => {
            const sId = typeof link.source === "object" ? link.source.id : link.source
            const tId = typeof link.target === "object" ? link.target.id : link.target
            if (activeNode === sId || activeNode === tId) return EDGE_HI
            return activeNode ? DIM_EDGE : EDGE
          }}
          linkWidth={(link: any) => {
            const sId = typeof link.source === "object" ? link.source.id : link.source
            const tId = typeof link.target === "object" ? link.target.id : link.target
            return (activeNode === sId || activeNode === tId) ? 2.2 : 0.8
          }}
          
          // Daten-Partikel fließen auf den aktiven Verbindungen
          linkDirectionalParticles={(link: any) => {
            const sId = typeof link.source === "object" ? link.source.id : link.source
            const tId = typeof link.target === "object" ? link.target.id : link.target
            return (activeNode === sId || activeNode === tId) ? 4 : 0
          }}
          linkDirectionalParticleWidth={2.2}
          linkDirectionalParticleSpeed={0.006}
          
          // Custom Canvas Rendering für glühende Knoten und 100% lesbare Text-Pillen
          nodeCanvasObject={(node: any, ctx: CanvasRenderingContext2D, globalScale: number) => {
            const x = node.x ?? 0
            const y = node.y ?? 0
            const size = 5.5 + Math.min(degree[node.id] || 0, 12) * 0.9
            const color = CAT_COLOR[node.category] || "#64748b"
            const isHovered = node.id === hovered
            const isSelected = node.id === selected
            
            // Abgedunkelter Zustand, wenn ein anderer Knoten aktiv ist und dieser kein Nachbar ist
            const isDimmed = activeNode && node.id !== activeNode && !connectedNodes.has(node.id)
            const finalColor = isDimmed ? "#1e293b" : color

            // 1. Zeichne weichen Glow-Schatten
            if (!isDimmed) {
              ctx.shadowColor = color
              ctx.shadowBlur = isHovered || isSelected ? 12 : 6
            }
            
            // 2. Zeichne den Knoten-Kreis
            ctx.beginPath()
            ctx.arc(x, y, size, 0, 2 * Math.PI, false)
            ctx.fillStyle = finalColor
            ctx.fill()
            ctx.shadowBlur = 0 // Schatten zurücksetzen

            // 3. Zeichne weißen Außenring bei Interaktivität
            if (isHovered || isSelected) {
              ctx.beginPath()
              ctx.arc(x, y, size + 2, 0, 2 * Math.PI, false)
              ctx.strokeStyle = isSelected ? "#ffffff" : "rgba(255, 255, 255, 0.7)"
              ctx.lineWidth = 1.5
              ctx.stroke()
            }

            // 4. Typografie (Immer scharf & mitskalierend gezeichnet)
            const label = node.content.length > 30 ? node.content.slice(0, 29) + "…" : node.content
            const baseFontSize = isHovered || isSelected ? 10 : 9
            const fontSize = baseFontSize / Math.max(0.6, globalScale) // Verhindert Riesen-Schriften beim Rauszoomen
            
            ctx.font = `${isHovered || isSelected ? "bold" : "500"} ${fontSize}px "Space Grotesk", sans-serif`
            const textWidth = ctx.measureText(label).width
            const bpad = 4 / Math.max(0.6, globalScale)
            
            // Render Label-Hintergrund für absolute Lesbarkeit
            ctx.fillStyle = isDimmed ? "rgba(10, 15, 25, 0.45)" : "rgba(8, 12, 20, 0.85)"
            ctx.beginPath()
            
            // Zeichne abgerundete Pille unter dem Knoten
            const rx = x - textWidth / 2 - bpad
            const ry = y + size + (5 / Math.max(0.6, globalScale))
            const rw = textWidth + bpad * 2
            const rh = fontSize + bpad * 1.5
            const rad = 4 / Math.max(0.6, globalScale)
            
            ctx.beginPath()
            ctx.moveTo(rx + rad, ry)
            ctx.lineTo(rx + rw - rad, ry)
            ctx.quadraticCurveTo(rx + rw, ry, rx + rw, ry + rad)
            ctx.lineTo(rx + rw, ry + rh - rad)
            ctx.quadraticCurveTo(rx + rw, ry + rh, rx + rw - rad, ry + rh)
            ctx.lineTo(rx + rad, ry + rh)
            ctx.quadraticCurveTo(rx, ry + rh, rx, ry + rh - rad)
            ctx.lineTo(rx, ry + rad)
            ctx.quadraticCurveTo(rx, ry, rx + rad, ry)
            ctx.closePath()
            ctx.fill()

            // Feine Kontur für die Text-Pille
            ctx.strokeStyle = isHovered || isSelected ? "rgba(255, 255, 255, 0.15)" : "rgba(255, 255, 255, 0.05)"
            ctx.lineWidth = 0.5 / Math.max(0.6, globalScale)
            ctx.stroke()

            // Text zeichnen
            ctx.textAlign = "center"
            ctx.textBaseline = "top"
            ctx.fillStyle = isDimmed 
              ? "#475569" 
              : (isHovered || isSelected ? "#ffffff" : "#cbd5e1")
            
            ctx.fillText(label, x, y + size + (6 / Math.max(0.6, globalScale)))
          }}
        />

        <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 cursor-pointer">
          <RefreshCw className="h-3.5 w-3.5" /> Zentrieren
        </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 cursor-pointer">
                  <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 cursor-pointer">
              <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>
  )
}
