import { useEffect, useCallback, useState, type FC } from 'react';
import Graph from 'graphology';
import forceAtlas2 from 'graphology-layout-forceatlas2';
import { SigmaContainer, useLoadGraph, useRegisterEvents, useSigma } from '@react-sigma/core';
import { LAYOUT_SETTINGS } from './graphConfig';

/* ------------------------------------------------------------------ */
/*  Types                                                              */
/* ------------------------------------------------------------------ */

export interface SigmaGraphNode {
  id: string;
  label: string;
  size?: number;
  color?: string;
  x?: number;
  y?: number;
}

export interface SigmaGraphEdge {
  source: string;
  target: string;
  weight?: number;
  color?: string;
}

export interface SigmaGraphProps {
  nodes: SigmaGraphNode[];
  edges: SigmaGraphEdge[];
  onNodeClick?: (nodeId: string) => void;
  onNodeHover?: (nodeId: string | null) => void;
  searchQuery?: string;
  height?: string;
  className?: string;
}

/* ------------------------------------------------------------------ */
/*  Inner component: loads graph + wires events                        */
/* ------------------------------------------------------------------ */

interface GraphLoaderProps {
  nodes: SigmaGraphNode[];
  edges: SigmaGraphEdge[];
  onNodeClick?: (nodeId: string) => void;
  onNodeHover?: (nodeId: string | null) => void;
  searchQuery?: string;
}

const GraphLoader: FC<GraphLoaderProps> = ({ nodes, edges, onNodeClick, onNodeHover, searchQuery }) => {
  const loadGraph = useLoadGraph();
  const sigma = useSigma();
  const registerEvents = useRegisterEvents();
  const [hoveredNode, setHoveredNode] = useState<string | null>(null);

  // Build and load graph whenever data changes
  useEffect(() => {
    const graph = new Graph();

    nodes.forEach((n) => {
      if (!graph.hasNode(n.id)) {
        graph.addNode(n.id, {
          label: String(n.label || n.id || ''),
          size: n.size ?? 5,
          color: n.color && n.color.length > 0 ? n.color : '#3b82f6',
          x: n.x ?? Math.random() * 100,
          y: n.y ?? Math.random() * 100,
        });
      }
    });

    edges.forEach((e) => {
      if (graph.hasNode(e.source) && graph.hasNode(e.target) && !graph.hasEdge(e.source, e.target)) {
        graph.addEdge(e.source, e.target, {
          weight: e.weight ?? 1,
          color: e.color && e.color.length > 0 ? e.color : '#1e293b',
        });
      }
    });

    // Apply ForceAtlas2 layout synchronously
    if (graph.order > 0) {
      forceAtlas2.assign(graph, {
        iterations: LAYOUT_SETTINGS.iterations,
        settings: LAYOUT_SETTINGS.settings,
      });
    }

    loadGraph(graph);

    return () => {
      graph.clear();
    };
  }, [nodes, edges, loadGraph]);

  // Register events
  useEffect(() => {
    registerEvents({
      clickNode: (event) => {
        onNodeClick?.(event.node);
      },
      enterNode: (event) => {
        setHoveredNode(event.node);
        onNodeHover?.(event.node);
      },
      leaveNode: () => {
        setHoveredNode(null);
        onNodeHover?.(null);
      },
    });
  }, [registerEvents, onNodeClick, onNodeHover]);

  // Apply node/edge reducer for search highlight + hover highlight
  useEffect(() => {
    const graph = sigma.getGraph();

    sigma.setSetting('nodeReducer', (node, data) => {
      const res = { ...data };

      // Hover highlight: dim non-neighbors
      if (hoveredNode && hoveredNode !== node) {
        const isNeighbor = graph.hasNode(hoveredNode) && graph.areNeighbors(node, hoveredNode);
        if (!isNeighbor) {
          res.color = '#1e293b';
          res.size = (data.size ?? 5) * 0.5;
          res.label = '';
        }
      }

      // Search highlight
      if (searchQuery) {
        const q = searchQuery.toLowerCase();
        const label = (data.label ?? '').toLowerCase();
        if (!label.includes(q)) {
          res.color = '#1e293b';
          res.size = (data.size ?? 5) * 0.5;
          res.label = '';
        } else {
          res.highlighted = true;
        }
      }

      return res;
    });

    sigma.setSetting('edgeReducer', (_edge, data) => {
      const res = { ...data };
      if (hoveredNode || searchQuery) {
        res.color = '#0f172a';
      }
      return res;
    });
  }, [sigma, hoveredNode, searchQuery]);

  return null;
};

/* ------------------------------------------------------------------ */
/*  Main SigmaGraph component                                          */
/* ------------------------------------------------------------------ */

const SigmaGraph: FC<SigmaGraphProps> = ({
  nodes,
  edges,
  onNodeClick,
  onNodeHover,
  searchQuery,
  height = '100%',
  className = '',
}) => {
  // Memoize a stable callback ref for the child
  const handleNodeClick = useCallback(
    (id: string) => onNodeClick?.(id),
    [onNodeClick],
  );
  const handleNodeHover = useCallback(
    (id: string | null) => onNodeHover?.(id),
    [onNodeHover],
  );

  return (
    <div
      className={`bg-surface-elevated rounded-lg overflow-hidden ${className}`}
      style={{ height, position: 'relative' }}
    >
      <SigmaContainer
        style={{ width: '100%', height: '100%' }}
        settings={{
          renderEdgeLabels: false,
          defaultNodeColor: '#3b82f6',
          defaultEdgeColor: '#1e293b',
          labelColor: { color: '#94a3b8' },
          labelRenderedSizeThreshold: 8,
          allowInvalidContainer: true,
        }}
      >
        <GraphLoader
          nodes={nodes}
          edges={edges}
          onNodeClick={handleNodeClick}
          onNodeHover={handleNodeHover}
          searchQuery={searchQuery}
        />
      </SigmaContainer>
    </div>
  );
};

export default SigmaGraph;
