import { useEffect, useRef } from 'react';
import { useSchemaStore } from '../../store/schemaStore.ts';
import { initGraph } from './graph/initGraph.ts';
import { registerAllNodes } from './graph/registerNodes.ts';
import { buildGraphData } from './helpers/dataMapper.ts';
import { mockData } from '../../mock/schemaData.ts';
import { DeviceGroup } from '../../types/index.ts';

let nodesRegistered = false;

export function SchemaCanvas() {
  const containerRef = useRef<HTMLDivElement>(null);
  const minimapRef = useRef<HTMLDivElement>(null);
  const setGraph = useSchemaStore((state) => state.setGraph);
  const setContextMenu = useSchemaStore((state) => state.setContextMenu);
  const setRightPanelData = useSchemaStore((state) => state.setRightPanelData);
  const setConnectionsPanelData = useSchemaStore(
    (state) => state.setConnectionsPanelData,
  );
  const setConnectionsPanelVisible = useSchemaStore(
    (state) => state.setConnectionsPanelVisible,
  );
  const displaySettings = useSchemaStore((state) => state.displaySettings);

  useEffect(() => {
    if (!containerRef.current) return;

    if (!nodesRegistered) {
      registerAllNodes();
      nodesRegistered = true;
    }

    const graph = initGraph(containerRef.current, minimapRef.current);
    setGraph(graph);

    const { nodes, edges } = buildGraphData(mockData, displaySettings.lineType);

    // Add nodes first (sites, then devices, then cards)
    const siteNodes = nodes.filter((n) => n.shape === 'site-node');
    const deviceNodes = nodes.filter(
      (n) => n.shape !== 'site-node' && n.shape !== 'card-node',
    );
    const cardNodes = nodes.filter((n) => n.shape === 'card-node');

    for (const node of siteNodes) {
      graph.addNode(node);
    }
    for (const node of deviceNodes) {
      const graphNode = graph.addNode(node);
      // Set parent (embed in site)
      if (node.parent) {
        const parentNode = graph.getCellById(node.parent);
        if (parentNode) {
          parentNode.addChild(graphNode);
        }
      }
    }
    for (const node of cardNodes) {
      const graphNode = graph.addNode(node);
      if (node.parent) {
        const parentNode = graph.getCellById(node.parent);
        if (parentNode) {
          parentNode.addChild(graphNode);
        }
      }
    }

    // Add edges
    for (const edge of edges) {
      graph.addEdge(edge);
    }

    // Center content
    graph.centerContent();

    // Event handlers
    graph.on('node:click', ({ node }) => {
      const data = node.getData() as Record<string, unknown>;
      setRightPanelData(data);
    });

    graph.on('edge:click', ({ edge }) => {
      const data = edge.getData() as Record<string, unknown>;
      const line = mockData.lines.find((l) => l.id === edge.id);
      if (line) {
        setRightPanelData({
          entityType: 'line',
          entityId: line.id,
          name: line.name,
          status: line.status,
          medium: line.medium,
          lineStyle: line.lineStyle,
          type: line.type,
        });
      } else if (data) {
        setRightPanelData(data);
      }
    });

    graph.on('node:contextmenu', ({ e, node }) => {
      e.preventDefault();
      const data = node.getData() as Record<string, unknown>;
      const entityType = data.entityType as string;

      let menuType: 'site' | 'active-device' | 'passive-device' = 'active-device';
      if (entityType === 'site') {
        menuType = 'site';
      } else if (entityType === 'device') {
        const group = data.group as string;
        menuType = group === DeviceGroup.Passive ? 'passive-device' : 'active-device';
      }

      setContextMenu({
        visible: true,
        x: e.clientX,
        y: e.clientY,
        type: menuType,
        data: data,
      });
    });

    graph.on('edge:contextmenu', ({ e, edge }) => {
      e.preventDefault();
      const line = mockData.lines.find((l) => l.id === edge.id);
      setContextMenu({
        visible: true,
        x: e.clientX,
        y: e.clientY,
        type: 'line',
        data: line
          ? {
              entityType: 'line',
              entityId: line.id,
              name: line.name,
              status: line.status,
            }
          : {},
      });
    });

    graph.on('blank:contextmenu', ({ e }) => {
      e.preventDefault();
      setContextMenu({
        visible: true,
        x: e.clientX,
        y: e.clientY,
        type: 'blank',
        data: {},
      });
    });

    graph.on('blank:click', () => {
      setContextMenu(null);
      setRightPanelData(null);
    });

    // Port side recalculation on node move
    graph.on('node:moved', () => {
      // In a full implementation, we'd recalculate port sides here
    });

    // Show connections panel on edge double click
    graph.on('edge:dblclick', ({ edge }) => {
      const line = mockData.lines.find((l) => l.id === edge.id);
      if (line) {
        const portA = mockData.ports.find((p) => p.id === line.portAId);
        const portZ = mockData.ports.find((p) => p.id === line.portZId);
        const devA = portA
          ? mockData.devices.find((d) => d.id === portA.deviceId)
          : null;
        const devZ = portZ
          ? mockData.devices.find((d) => d.id === portZ.deviceId)
          : null;

        setConnectionsPanelData({
          line,
          portA,
          portZ,
          deviceA: devA,
          deviceZ: devZ,
        });
        setConnectionsPanelVisible(true);
      }
    });

    return () => {
      graph.dispose();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Sync display settings: grid
  useEffect(() => {
    const graph = useSchemaStore.getState().graph;
    if (!graph) return;

    if (displaySettings.showGrid) {
      graph.showGrid();
    } else {
      graph.hideGrid();
    }
  }, [displaySettings.showGrid]);

  // Sync display settings: labels
  useEffect(() => {
    const graph = useSchemaStore.getState().graph;
    if (!graph) return;

    const show = displaySettings.showLabels;

    // Toggle edge labels
    for (const edge of graph.getEdges()) {
      const labels = edge.getLabels();
      if (labels.length > 0) {
        edge.setLabels(
          labels.map((label) => ({
            ...label,
            attrs: {
              ...label.attrs,
              label: {
                ...(label.attrs?.label as Record<string, unknown>),
                visibility: show ? 'visible' : 'hidden',
              },
              rect: {
                ...(label.attrs?.rect as Record<string, unknown>),
                visibility: show ? 'visible' : 'hidden',
              },
            },
          })),
        );
      }
    }

    // Toggle port labels
    for (const node of graph.getNodes()) {
      const ports = node.getPorts();
      for (const port of ports) {
        node.setPortProp(port.id!, 'attrs/text/visibility', show ? 'visible' : 'hidden');
      }
    }
  }, [displaySettings.showLabels]);

  return (
    <div style={{ position: 'relative', width: '100%', height: '100%' }}>
      <div
        ref={containerRef}
        style={{ width: '100%', height: '100%' }}
      />
      <div
        ref={minimapRef}
        style={{
          position: 'absolute',
          bottom: 16,
          right: 16,
          width: 200,
          height: 150,
          border: '1px solid #d9d9d9',
          background: '#fff',
          borderRadius: 4,
          overflow: 'hidden',
          display: displaySettings.showMinimap ? 'block' : 'none',
          boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
        }}
      />
    </div>
  );
}
