"use client";

import {
  useEffect,
  useState,
  useRef,
  useCallback,
  useMemo,
  type MutableRefObject,
} from "react";
import dynamic from "next/dynamic";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
  X,
  Link2,
  Maximize2,
  ZoomIn,
  ZoomOut,
  RotateCcw,
  Eye,
  EyeOff,
} from "lucide-react";
import { apiFetch } from "@/lib/api";

const ForceGraph2D = dynamic(() => import("react-force-graph-2d"), {
  ssr: false,
});

interface MemoryNode {
  id: string;
  memoryType: string;
  text: string;
  summary?: string;
  tags: string[];
  status: string;
  confidence?: number;
  authorId?: string;
  authorName?: string;
  createdAt: string;
  linkCount: number;
}

interface MemoryEdge {
  id: string;
  source: string;
  target: string;
  linkType: string;
}

interface GraphNode extends MemoryNode {
  x?: number;
  y?: number;
  fx?: number | null;
  fy?: number | null;
  __bckgDimensions?: [number, number];
}

interface GraphLink {
  id: string;
  source: GraphNode | string;
  target: GraphNode | string;
  linkType: string;
}

interface MemoryGraphProps {
  org: string;
  repo: string;
  onMemoryClick?: (memory: MemoryNode) => void;
}

const TYPE_COLORS: Record<string, string> = {
  episodic: "#fafafa",
  semantic: "#a1a1aa",
  procedural: "#52525b",
};

const LINK_TYPE_COLORS: Record<string, string> = {
  related_to: "#d4d4d8",
  derived_from: "#a1a1aa",
  contradicts: "#fafafa",
  depends_on: "#71717a",
};

const TYPE_LABELS: Record<string, string> = {
  related_to: "Related to",
  derived_from: "Derived from",
  contradicts: "Contradicts",
  depends_on: "Depends on",
};

const typeColorClasses: Record<string, string> = {
  episodic: "bg-white/[0.08] text-foreground",
  semantic: "bg-white/[0.06] text-muted-foreground",
  procedural: "bg-white/[0.04] text-muted-foreground/80",
};

const linkTypeColorClasses: Record<string, string> = {
  related_to: "bg-white/[0.08] text-foreground",
  derived_from: "bg-white/[0.06] text-muted-foreground",
  contradicts: "bg-white/[0.08] text-foreground",
  depends_on: "bg-white/[0.04] text-muted-foreground/80",
};

const STATUS_COLORS: Record<string, string> = {
  active: "#fafafa",
  superseded: "#a1a1aa",
  deprecated: "#71717a",
  deleted: "#3f3f46",
};

const STATUS_LABELS: Record<string, string> = {
  active: "Active",
  superseded: "Superseded",
  deprecated: "Deprecated",
  deleted: "Deleted",
};

function truncateText(text: string, max: number) {
  if (text.length <= max) return text;
  return text.slice(0, max) + "...";
}

function useContainerSize() {
  const [size, setSize] = useState({ width: 0, height: 0 });
  const elRef = useRef<HTMLDivElement | null>(null);
  const roRef = useRef<ResizeObserver | null>(null);

  const callbackRef = useCallback((node: HTMLDivElement | null) => {
    if (roRef.current) {
      roRef.current.disconnect();
      roRef.current = null;
    }

    elRef.current = node;
    if (!node) return;

    const measure = () => {
      const w = node.clientWidth;
      const h = node.clientHeight;
      setSize((prev) => {
        if (prev.width === w && prev.height === h) return prev;
        return { width: w, height: h };
      });
    };

    measure();

    roRef.current = new ResizeObserver(measure);
    roRef.current.observe(node);
  }, []);

  return { size, callbackRef, elRef };
}

export function MemoryGraph({ org, repo, onMemoryClick }: MemoryGraphProps) {
  const [nodes, setNodes] = useState<MemoryNode[]>([]);
  const [edges, setEdges] = useState<MemoryEdge[]>([]);
  const [loading, setLoading] = useState(true);
  const [selectedNode, setSelectedNode] = useState<GraphNode | null>(null);
  const [hoveredNode, setHoveredNode] = useState<GraphNode | null>(null);
  const [showOrphans, setShowOrphans] = useState(true);
  
  const [visibleNodeTypes, setVisibleNodeTypes] = useState<Set<string>>(
    new Set(["episodic", "semantic", "procedural"])
  );
  const [visibleLinkTypes, setVisibleLinkTypes] = useState<Set<string>>(
    new Set(["related_to", "derived_from", "contradicts", "depends_on"])
  );
  const [visibleStatuses, setVisibleStatuses] = useState<Set<string>>(
    new Set(["active", "superseded", "deprecated", "deleted"])
  );
  
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const graphRef = useRef<any>(null);

  const { size: { width: canvasWidth, height: canvasHeight }, callbackRef: canvasRef } = useContainerSize();
  const initialFitDone = useRef(false);

  const fetchGraphData = useCallback(async () => {
    setLoading(true);
    try {
      const data = await apiFetch<{
        memories: Array<{
          id: string;
          memoryType: string;
          text: string;
          summary?: string;
          tags?: string[];
          status: string;
          confidence?: number;
          authorId?: string;
          authorName?: string;
          createdAt: string;
        }>;
        links: Array<{
          id: string;
          sourceId: string;
          targetId: string;
          linkType: string;
        }>;
      }>(`/api/repo/${org}/${repo}/graph`);

      const linkCountMap = new Map<string, number>();
      const links = data.links ?? [];
      for (const link of links) {
        linkCountMap.set(
          link.sourceId,
          (linkCountMap.get(link.sourceId) ?? 0) + 1,
        );
        linkCountMap.set(
          link.targetId,
          (linkCountMap.get(link.targetId) ?? 0) + 1,
        );
      }

      const memories = data.memories ?? [];
      const memNodes: MemoryNode[] = memories.map((m) => ({
        id: m.id,
        memoryType: m.memoryType,
        text: m.text,
        summary: m.summary,
        tags: m.tags ?? [],
        status: m.status,
        confidence: m.confidence,
        authorId: m.authorId,
        authorName: m.authorName,
        createdAt: m.createdAt,
        linkCount: linkCountMap.get(m.id) ?? 0,
      }));

      const memEdges: MemoryEdge[] = links.map((l) => ({
        id: l.id,
        source: l.sourceId,
        target: l.targetId,
        linkType: l.linkType,
      }));

      setNodes(memNodes);
      setEdges(memEdges);
      initialFitDone.current = false;
    } catch (err) {
      console.error("Failed to load graph:", err);
    } finally {
      setLoading(false);
    }
  }, [org, repo]);

  useEffect(() => {
    fetchGraphData();
  }, [fetchGraphData]);

  const graphData = useMemo(() => {
    const typeFilteredNodes = nodes.filter(
      (n) => visibleNodeTypes.has(n.memoryType) && visibleStatuses.has(n.status)
    );
    const typeFilteredNodeIds = new Set(typeFilteredNodes.map((n) => n.id));
    
    const filteredEdges = edges.filter(
      (e) =>
        visibleLinkTypes.has(e.linkType) &&
        typeFilteredNodeIds.has(e.source) &&
        typeFilteredNodeIds.has(e.target)
    );
    
    const linkedIds = new Set<string>();
    for (const e of filteredEdges) {
      linkedIds.add(e.source);
      linkedIds.add(e.target);
    }

    const filteredNodes = showOrphans
      ? typeFilteredNodes
      : typeFilteredNodes.filter((n) => linkedIds.has(n.id));

    return {
      nodes: filteredNodes.map((n) => ({ ...n })),
      links: filteredEdges.map((e) => ({ ...e })),
    };
  }, [nodes, edges, showOrphans, visibleNodeTypes, visibleLinkTypes, visibleStatuses]);

  const neighborMap = useMemo(() => {
    const map = new Map<string, Set<string>>();
    for (const e of edges) {
      if (!map.has(e.source)) map.set(e.source, new Set());
      if (!map.has(e.target)) map.set(e.target, new Set());
      map.get(e.source)!.add(e.target);
      map.get(e.target)!.add(e.source);
    }
    return map;
  }, [edges]);

  const selectedNeighbors = useMemo(() => {
    if (!selectedNode) return new Set<string>();
    return neighborMap.get(selectedNode.id) ?? new Set<string>();
  }, [selectedNode, neighborMap]);

  const selectedEdges = useMemo(() => {
    if (!selectedNode) return [];
    return edges.filter(
      (e) => e.source === selectedNode.id || e.target === selectedNode.id,
    );
  }, [selectedNode, edges]);

  const connectedMemories = useMemo(() => {
    if (!selectedNode) return [];
    return selectedEdges.map((e) => {
      const otherId =
        e.source === selectedNode.id ? e.target : e.source;
      const node = nodes.find((n) => n.id === otherId);
      return { edge: e, node };
    });
  }, [selectedNode, selectedEdges, nodes]);

  const handleEngineStop = useCallback(() => {
    if (!initialFitDone.current && graphRef.current) {
      initialFitDone.current = true;
      graphRef.current.zoomToFit(400, 60);
    }
  }, []);

  const handleNodeClick = useCallback(
    (node: GraphNode) => {
      setSelectedNode((prev) => (prev?.id === node.id ? null : node));
    },
    [],
  );

  const handleNodeDoubleClick = useCallback(
    (node: GraphNode) => {
      if (onMemoryClick) {
        onMemoryClick(node);
      }
    },
    [onMemoryClick],
  );

  const handleBackgroundClick = useCallback(() => {
    setSelectedNode(null);
  }, []);

  const handleZoomIn = useCallback(() => {
    const fg = graphRef.current;
    if (fg) fg.zoom(fg.zoom() * 1.5, 300);
  }, []);

  const handleZoomOut = useCallback(() => {
    const fg = graphRef.current;
    if (fg) fg.zoom(fg.zoom() / 1.5, 300);
  }, []);

  const handleFitView = useCallback(() => {
    const fg = graphRef.current;
    if (fg) fg.zoomToFit(400, 60);
  }, []);

  const handleReset = useCallback(() => {
    setSelectedNode(null);
    const fg = graphRef.current;
    if (fg) fg.zoomToFit(400, 60);
  }, []);

  const toggleNodeType = useCallback((type: string) => {
    setVisibleNodeTypes((prev) => {
      const next = new Set(prev);
      if (next.has(type)) {
        if (next.size > 1) next.delete(type);
      } else {
        next.add(type);
      }
      return next;
    });
  }, []);

  const toggleLinkType = useCallback((type: string) => {
    setVisibleLinkTypes((prev) => {
      const next = new Set(prev);
      if (next.has(type)) {
        next.delete(type);
      } else {
        next.add(type);
      }
      return next;
    });
  }, []);

  const toggleStatus = useCallback((status: string) => {
    setVisibleStatuses((prev) => {
      const next = new Set(prev);
      if (next.has(status)) {
        if (next.size > 1) next.delete(status);
      } else {
        next.add(status);
      }
      return next;
    });
  }, []);

  const navigateToNode = useCallback(
    (nodeId: string) => {
      const node = graphData.nodes.find(
        (n) => n.id === nodeId,
      ) as GraphNode | undefined;
      if (node && graphRef.current) {
        setSelectedNode(node);
        graphRef.current.centerAt(node.x, node.y, 500);
        graphRef.current.zoom(3, 500);
      }
    },
    [graphData.nodes],
  );

  const nodeCanvasObject = useCallback(
    (
      node: GraphNode,
      ctx: CanvasRenderingContext2D,
      globalScale: number,
    ) => {
      const isSelected = selectedNode?.id === node.id;
      const isNeighbor = selectedNeighbors.has(node.id);
      const isDimmed = selectedNode && !isSelected && !isNeighbor;
      const isHovered = hoveredNode?.id === node.id;

      const baseRadius = 4 + Math.min(node.linkCount * 1.5, 12);
      const radius = isSelected ? baseRadius + 3 : baseRadius;

      const color = TYPE_COLORS[node.memoryType] ?? "#888";
      const alpha = isDimmed ? 0.12 : 1;

      if (!isDimmed && (isSelected || isHovered)) {
        ctx.beginPath();
        ctx.arc(node.x ?? 0, node.y ?? 0, radius + 6, 0, 2 * Math.PI);
        ctx.fillStyle = color + "15";
        ctx.fill();
      }

      ctx.beginPath();
      ctx.arc(node.x ?? 0, node.y ?? 0, radius, 0, 2 * Math.PI);
      ctx.fillStyle = alpha < 1 ? color + "1f" : color;
      ctx.fill();

      if (isSelected || isHovered) {
        ctx.strokeStyle = isSelected ? "#fff" : color;
        ctx.lineWidth = isSelected ? 2 : 1.5;
        ctx.stroke();
      }

      if (node.status === "deprecated") {
        ctx.beginPath();
        ctx.arc(node.x ?? 0, node.y ?? 0, radius, 0, 2 * Math.PI);
        ctx.fillStyle = "rgba(0,0,0,0.35)";
        ctx.fill();
      }

      if (globalScale > 1.2 && !isDimmed) {
        const label = truncateText(node.summary || node.text, 30);
        const fontSize = Math.max(10 / globalScale, 2.5);
        ctx.font = `500 ${fontSize}px -apple-system, BlinkMacSystemFont, Inter, sans-serif`;
        ctx.textAlign = "center";
        ctx.textBaseline = "top";
        ctx.fillStyle = isDimmed
          ? "rgba(150,150,150,0.3)"
          : "rgba(245,245,247,0.75)";
        ctx.fillText(label, node.x ?? 0, (node.y ?? 0) + radius + 3);
      }

      node.__bckgDimensions = [radius * 2, radius * 2];
    },
    [selectedNode, selectedNeighbors, hoveredNode],
  );

  const linkCanvasObject = useCallback(
    (
      link: GraphLink,
      ctx: CanvasRenderingContext2D,
      _globalScale: number,
    ) => {
      const source = link.source as GraphNode;
      const target = link.target as GraphNode;
      if (!source || !target || source.x == null || target.x == null) return;

      const sourceId =
        typeof link.source === "string" ? link.source : (link.source as GraphNode).id;
      const targetId =
        typeof link.target === "string" ? link.target : (link.target as GraphNode).id;

      const isHighlighted =
        selectedNode &&
        (sourceId === selectedNode.id || targetId === selectedNode.id);

      const isDimmed = selectedNode && !isHighlighted;
      const color = LINK_TYPE_COLORS[link.linkType] ?? "#666";

      ctx.beginPath();
      ctx.moveTo(source.x, source.y!);
      ctx.lineTo(target.x, target.y!);
      ctx.strokeStyle = isDimmed
        ? "rgba(150,150,150,0.04)"
        : isHighlighted
          ? color
          : color + "35";
      ctx.lineWidth = isHighlighted ? 2 : 0.6;
      ctx.stroke();

      if (isHighlighted) {
        const dx = target.x - source.x;
        const dy = target.y! - source.y!;
        const len = Math.sqrt(dx * dx + dy * dy);
        if (len < 1) return;

        const targetRadius =
          4 + Math.min((nodes.find((n) => n.id === targetId)?.linkCount ?? 0) * 1.5, 12);
        const arrowLen = 6;
        const endX = target.x - (dx / len) * (targetRadius + 2);
        const endY = target.y! - (dy / len) * (targetRadius + 2);
        const angle = Math.atan2(dy, dx);

        ctx.beginPath();
        ctx.moveTo(endX, endY);
        ctx.lineTo(
          endX - arrowLen * Math.cos(angle - Math.PI / 7),
          endY - arrowLen * Math.sin(angle - Math.PI / 7),
        );
        ctx.lineTo(
          endX - arrowLen * Math.cos(angle + Math.PI / 7),
          endY - arrowLen * Math.sin(angle + Math.PI / 7),
        );
        ctx.closePath();
        ctx.fillStyle = color;
        ctx.fill();
      }
    },
    [selectedNode, nodes],
  );

  const nodePointerAreaPaint = useCallback(
    (node: GraphNode, color: string, ctx: CanvasRenderingContext2D) => {
      const radius = 4 + Math.min(node.linkCount * 1.5, 12) + 4;
      ctx.beginPath();
      ctx.arc(node.x ?? 0, node.y ?? 0, radius, 0, 2 * Math.PI);
      ctx.fillStyle = color;
      ctx.fill();
    },
    [],
  );

  const formatDate = (d: string) => {
    const date = new Date(d);
    const now = new Date();
    const diffMs = now.getTime() - date.getTime();
    const diffSec = Math.floor(diffMs / 1000);
    const diffMin = Math.floor(diffSec / 60);
    const diffHour = Math.floor(diffMin / 60);
    const diffDay = Math.floor(diffHour / 24);
    const diffMonth = Math.floor(diffDay / 30);
    const diffYear = Math.floor(diffDay / 365);

    if (diffSec < 60) return "just now";
    if (diffMin < 60) return `${diffMin}m ago`;
    if (diffHour < 24) return `${diffHour}h ago`;
    if (diffDay < 30) return `${diffDay}d ago`;
    if (diffMonth < 12) return `${diffMonth}mo ago`;
    return `${diffYear}y ago`;
  };

  const filteredStats = useMemo(() => {
    const linkedIds = new Set<string>();
    for (const link of graphData.links) {
      const sourceId = typeof link.source === "string" ? link.source : (link.source as GraphNode).id;
      const targetId = typeof link.target === "string" ? link.target : (link.target as GraphNode).id;
      linkedIds.add(sourceId);
      linkedIds.add(targetId);
    }
    const linkedCount = linkedIds.size;
    const orphanCount = graphData.nodes.length - linkedCount;
    return { linkedCount, orphanCount, linkCount: graphData.links.length };
  }, [graphData]);

  if (loading) {
    return (
      <div className="flex h-full items-center justify-center">
        <div className="flex flex-col items-center gap-3">
          <div className="h-8 w-8 animate-spin rounded-full border-2 border-foreground border-t-transparent" />
          <span className="text-[13px] text-muted-foreground">
            Loading graph...
          </span>
        </div>
      </div>
    );
  }

  if (nodes.length === 0) {
    return (
      <div className="flex h-full flex-col items-center justify-center gap-3">
        <div className="flex h-16 w-16 items-center justify-center rounded-3xl bg-white/[0.04]">
          <Link2 className="h-8 w-8 text-muted-foreground/30" />
        </div>
        <p className="text-[14px] text-muted-foreground">No memories found</p>
        <p className="text-[12px] text-muted-foreground/50">
          Create memories and link them to see the graph
        </p>
      </div>
    );
  }

  return (
    <div
      style={{ position: "absolute", top: 0, left: 0, right: 0, bottom: 0, display: "flex" }}
    >
      <div ref={canvasRef} style={{ flex: 1, position: "relative", minWidth: 0, minHeight: 0 }}>
        {canvasWidth > 0 && canvasHeight > 0 && (
          <ForceGraph2D
            ref={graphRef as MutableRefObject<undefined>}
            width={canvasWidth}
            height={canvasHeight}
            graphData={graphData}
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            nodeCanvasObject={nodeCanvasObject as any}
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            nodePointerAreaPaint={nodePointerAreaPaint as any}
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            linkCanvasObject={linkCanvasObject as any}
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            onNodeClick={handleNodeClick as any}
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            onNodeRightClick={handleNodeDoubleClick as any}
            onBackgroundClick={handleBackgroundClick}
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            onNodeHover={(node: any) => setHoveredNode(node as GraphNode | null)}
            onEngineStop={handleEngineStop}
            cooldownTicks={120}
            d3AlphaDecay={0.02}
            d3VelocityDecay={0.3}
            d3AlphaMin={0.001}
            enableNodeDrag={true}
            backgroundColor="transparent"
            dagMode={undefined}
            // @ts-expect-error d3Force is a valid prop but types are incomplete
            d3Force={(name, force) => {
              if (name === "charge" && force) {
                force.strength(-120).distanceMax(250);
              }
              if (name === "center" && force) {
                force.strength(0.1);
              }
              if (name === "link" && force) {
                force.distance(80).strength(0.4);
              }
            }}
          />
        )}

        <div className="absolute top-4 left-4 flex items-center gap-1 z-10 rounded-xl border border-border/50 bg-[rgba(28,28,30,0.85)] glass p-1">
          {[
            { icon: ZoomIn, action: handleZoomIn, title: "Zoom in" },
            { icon: ZoomOut, action: handleZoomOut, title: "Zoom out" },
            { icon: Maximize2, action: handleFitView, title: "Fit view" },
            { icon: RotateCcw, action: handleReset, title: "Reset" },
          ].map(({ icon: Icon, action, title }) => (
            <button
              key={title}
              onClick={action}
              className="flex items-center justify-center rounded-lg p-2 transition-colors hover:bg-white/[0.08]"
              title={title}
            >
              <Icon className="h-4 w-4 text-muted-foreground" />
            </button>
          ))}

          <div className="w-px h-5 bg-border/30 mx-1" />

          <button
            onClick={() => setShowOrphans(!showOrphans)}
            className={`flex items-center gap-1.5 rounded-lg px-2.5 py-2 transition-colors ${
              showOrphans
                ? "bg-white/10 text-white"
                : "text-muted-foreground hover:text-foreground hover:bg-white/[0.08]"
            }`}
            title={showOrphans ? "Hide orphan nodes" : "Show orphan nodes"}
          >
            {showOrphans ? (
              <EyeOff className="h-4 w-4" />
            ) : (
              <Eye className="h-4 w-4" />
            )}
            <span className="text-[11px]">{showOrphans ? "Hide orphans" : "Show orphans"}</span>
          </button>
        </div>

        <div className="absolute bottom-4 left-4 rounded-xl border border-border/30 bg-[rgba(28,28,30,0.85)] glass p-3 z-10">
          <p className="mb-2 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60">
            Nodes
          </p>
          <div className="flex flex-wrap gap-x-2 gap-y-1.5 mb-3">
            {Object.entries(TYPE_COLORS).map(([type, color]) => {
              const isActive = visibleNodeTypes.has(type);
              return (
                <button
                  key={type}
                  onClick={() => toggleNodeType(type)}
                  className={`flex items-center gap-1.5 rounded-lg px-2 py-1 transition-all ${
                    isActive
                      ? "bg-white/[0.08] hover:bg-white/[0.12]"
                      : "opacity-40 hover:opacity-70"
                  }`}
                  title={isActive ? `Hide ${type} nodes` : `Show ${type} nodes`}
                >
                  <span
                    className={`inline-block h-2.5 w-2.5 rounded-full transition-all ${
                      isActive ? "ring-2 ring-offset-1 ring-offset-transparent" : ""
                    }`}
                    style={{ 
                      backgroundColor: isActive ? color : "transparent",
                      borderColor: color,
                      borderWidth: 2,
                      borderStyle: "solid",
                    }}
                  />
                  <span className={`text-[11px] capitalize ${isActive ? "text-foreground" : "text-muted-foreground"}`}>
                    {type}
                  </span>
                </button>
              );
            })}
          </div>
          <p className="mb-2 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60">
            Status
          </p>
          <div className="flex flex-wrap gap-x-2 gap-y-1.5 mb-3">
            {Object.entries(STATUS_COLORS).map(([status, color]) => {
              const isActive = visibleStatuses.has(status);
              return (
                <button
                  key={status}
                  onClick={() => toggleStatus(status)}
                  className={`flex items-center gap-1.5 rounded-lg px-2 py-1 transition-all ${
                    isActive
                      ? "bg-white/[0.08] hover:bg-white/[0.12]"
                      : "opacity-40 hover:opacity-70"
                  }`}
                  title={isActive ? `Hide ${STATUS_LABELS[status]} memories` : `Show ${STATUS_LABELS[status]} memories`}
                >
                  <span
                    className={`inline-block h-2 w-2 rounded-sm transition-all`}
                    style={{ 
                      backgroundColor: isActive ? color : "transparent",
                      borderColor: color,
                      borderWidth: 1.5,
                      borderStyle: "solid",
                    }}
                  />
                  <span className={`text-[11px] capitalize ${isActive ? "text-foreground" : "text-muted-foreground"}`}>
                    {STATUS_LABELS[status]}
                  </span>
                </button>
              );
            })}
          </div>
          <p className="mb-2 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/60">
            Links
          </p>
          <div className="flex flex-wrap gap-x-2 gap-y-1.5">
            {Object.entries(LINK_TYPE_COLORS).map(([type, color]) => {
              const isActive = visibleLinkTypes.has(type);
              return (
                <button
                  key={type}
                  onClick={() => toggleLinkType(type)}
                  className={`flex items-center gap-1.5 rounded-lg px-2 py-1 transition-all ${
                    isActive
                      ? "bg-white/[0.08] hover:bg-white/[0.12]"
                      : "opacity-40 hover:opacity-70"
                  }`}
                  title={isActive ? `Hide ${TYPE_LABELS[type]} links` : `Show ${TYPE_LABELS[type]} links`}
                >
                  <span
                    className={`inline-block h-[3px] w-4 rounded transition-all`}
                    style={{ 
                      backgroundColor: isActive ? color : "transparent",
                      borderColor: color,
                      borderWidth: 1,
                      borderStyle: "solid",
                    }}
                  />
                  <span className={`text-[11px] ${isActive ? "text-foreground" : "text-muted-foreground"}`}>
                    {TYPE_LABELS[type] ?? type}
                  </span>
                </button>
              );
            })}
          </div>
        </div>

        <div className="absolute top-4 right-4 rounded-xl border border-border/30 bg-[rgba(28,28,30,0.85)] glass px-3 py-2 z-10">
          <div className="flex items-center gap-4 text-[11px] text-muted-foreground">
            <span>
              <strong className="text-foreground font-semibold">{filteredStats.linkedCount}</strong>{" "}
              linked
            </span>
            <span>
              <strong className="text-foreground font-semibold">
                {filteredStats.orphanCount}
              </strong>{" "}
              orphan
            </span>
            <span>
              <strong className="text-foreground font-semibold">{filteredStats.linkCount}</strong>{" "}
              links
            </span>
          </div>
        </div>
      </div>

      {selectedNode && (
        <div
          style={{ width: 360, flexShrink: 0 }}
          className="border-l border-border/30 bg-[rgba(18,18,18,0.95)] glass-subtle overflow-y-auto animate-fade-in"
        >
          <div className="sticky top-0 z-10 flex items-center justify-between border-b border-border/20 bg-[rgba(18,18,18,0.95)] glass-subtle px-5 py-4">
            <h3 className="text-[14px] font-semibold">Memory Detail</h3>
            <Button
              variant="ghost"
              size="sm"
              onClick={() => setSelectedNode(null)}
              className="h-7 w-7 p-0 rounded-full"
            >
              <X className="h-3.5 w-3.5" />
            </Button>
          </div>

          <div className="space-y-4 p-5">
            <div className="flex flex-wrap gap-1.5">
              <Badge
                className={`${typeColorClasses[selectedNode.memoryType] ?? ""} border-0`}
                variant="secondary"
              >
                {selectedNode.memoryType}
              </Badge>
              <Badge variant="outline">{selectedNode.status}</Badge>
            </div>

            <div className="rounded-xl bg-white/[0.03] p-3.5">
              <p className="whitespace-pre-wrap text-[13px] leading-relaxed">
                {selectedNode.text}
              </p>
            </div>

            {selectedNode.summary && (
              <div>
                <h4 className="mb-1 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/50">
                  Summary
                </h4>
                <p className="text-[12px] text-foreground/70">
                  {selectedNode.summary}
                </p>
              </div>
            )}

            {selectedNode.tags.length > 0 && (
              <div>
                <h4 className="mb-1.5 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/50">
                  Tags
                </h4>
                <div className="flex flex-wrap gap-1">
                  {selectedNode.tags.map((tag) => (
                    <span
                      key={tag}
                      className="rounded-md bg-white/[0.05] px-2 py-0.5 text-[11px] text-muted-foreground"
                    >
                      {tag}
                    </span>
                  ))}
                </div>
              </div>
            )}

            <div className="flex flex-wrap gap-x-4 gap-y-1 text-[11px] text-muted-foreground/70">
              {selectedNode.authorName && (
                <span>By: {selectedNode.authorName}</span>
              )}
              {selectedNode.confidence !== undefined && (
                <span>
                  Confidence: {(selectedNode.confidence * 100).toFixed(0)}%
                </span>
              )}
              <span>{formatDate(selectedNode.createdAt)}</span>
            </div>

            {connectedMemories.length > 0 && (
              <div>
                <h4 className="mb-2 flex items-center gap-1.5 text-[10px] font-medium uppercase tracking-wider text-muted-foreground/50">
                  <Link2 className="h-3 w-3" />
                  Connections ({connectedMemories.length})
                </h4>
                <div className="space-y-1.5">
                  {connectedMemories.map(({ edge, node: connNode }) => {
                    if (!connNode) return null;
                    const direction =
                      edge.source === selectedNode.id ? "\u2192" : "\u2190";
                    return (
                      <button
                        key={edge.id}
                        onClick={() => navigateToNode(connNode.id)}
                        className="w-full rounded-xl border border-border/20 bg-white/[0.02] p-3 text-left transition-all hover:bg-white/[0.05] hover:border-border/40"
                      >
                        <div className="mb-1 flex items-center gap-1.5">
                          <Badge
                            className={`${linkTypeColorClasses[edge.linkType] ?? ""} border-0`}
                            variant="secondary"
                          >
                            {direction} {edge.linkType.replace(/_/g, " ")}
                          </Badge>
                          <Badge
                            className={`${typeColorClasses[connNode.memoryType] ?? ""} border-0`}
                            variant="secondary"
                          >
                            {connNode.memoryType}
                          </Badge>
                        </div>
                        <p className="line-clamp-2 text-[11px] leading-relaxed text-foreground/60">
                          {connNode.text}
                        </p>
                      </button>
                    );
                  })}
                </div>
              </div>
            )}
          </div>

          <div className="border-t border-border/20 p-4 space-y-3">
            <Button
              variant="outline"
              size="sm"
              className="w-full"
              onClick={() => onMemoryClick?.(selectedNode)}
            >
              View Full Details
            </Button>
            <p className="text-center text-[10px] text-muted-foreground/40 font-mono">
              {selectedNode.id.slice(0, 8)}...{selectedNode.id.slice(-4)}
            </p>
          </div>
        </div>
      )}
    </div>
  );
}
