/**
 * Custom hook for ReactFlow event handlers
 * Handles node/edge interactions, connections, selections, and canvas events
 */

import { useCallback, useRef } from 'react';
import { useStore, Node, Edge } from '../store/store';
import { generateUniqueEdgeId } from '../utils/clipboardUtils';
import { t } from '../utils/translation';

interface UseFlowEventHandlersProps {
  handleCanvasNodeClick: (node: Node) => void;
  handleCanvasEdgeClick: (edge: Edge) => void;
  isNodeLocked: (nodeId: string) => boolean;
  isEdgeLocked: (edgeId: string) => boolean;
  setHasUnsavedChanges: (value: boolean) => void;
  isSyncing: boolean;
  hasReplayData: boolean;
  isReplayMode: boolean;
  currentReplayStep: number;
  autoSyncToReplay: (node: Node | null) => void;
  /** Screen reader announcement callback */
  announce?: (text: string) => void;
}

export function useFlowEventHandlers({
  handleCanvasNodeClick,
  handleCanvasEdgeClick,
  isNodeLocked,
  isEdgeLocked,
  setHasUnsavedChanges,
  isSyncing,
  hasReplayData,
  isReplayMode,
  currentReplayStep,
  autoSyncToReplay,
  announce
}: UseFlowEventHandlersProps) {
  
  // Store state
  const nodes = useStore(state => state.nodes);
  const edges = useStore(state => state.edges);
  const setNodes = useStore(state => state.setNodes);
  const setEdges = useStore(state => state.setEdges);
  const applyNodeChangesToStore = useStore(state => state.applyNodeChanges);
  const applyEdgeChangesToStore = useStore(state => state.applyEdgeChanges);
  const selectedNode = useStore(state => state.selectedNode);
  const _selectedEdge = useStore(state => state.selectedEdge);
  const setSelectedNode = useStore(state => state.setSelectedNode);
  const setSelectedEdge = useStore(state => state.setSelectedEdge);
  const setSelectedNodes = useStore(state => state.setSelectedNodes);
  const setSelectedEdges = useStore(state => state.setSelectedEdges);

  // Guard against stale onSelectionChange events after a pane click.
  // ReactFlow may fire onSelectionChange with the previously selected
  // node/edge *after* onPaneClick has already cleared the selection.
  // The ref is set by onPaneClick and consumed (reset) by the next
  // onSelectionChange that carries an empty selection.
  const paneClickedRef = useRef(false);

  // Snapshot node position at drag start so onNodeDragStop can detect
  // whether the node actually moved.  ReactFlow fires onNodeDragStop even
  // on simple clicks (zero-movement "drags"), so we need this comparison.
  const dragStartPositions = useRef<Record<string, { x: number; y: number }>>({});

  // ReactFlow change handlers
  const onNodesChange = useCallback((changes: any[]) => {
    applyNodeChangesToStore(changes);
  }, [applyNodeChangesToStore]);

  const onEdgesChange = useCallback((changes: any[]) => {
    applyEdgeChangesToStore(changes);
  }, [applyEdgeChangesToStore]);

  // Handle selection changes from canvas
  const onSelectionChange = useCallback(({ nodes: selectedNodes, edges: selectedEdges }: { nodes: Node[]; edges: Edge[] }) => {
    const isEmpty = selectedNodes.length === 0 && selectedEdges.length === 0;

    if (paneClickedRef.current) {
      if (isEmpty) {
        // This is the confirming empty event after the pane click — consume
        // the flag and proceed normally to clear selection state.
        paneClickedRef.current = false;
      } else {
        // Stale event carrying the previous selection — ignore it.
        return;
      }
    }

    // Handle single selection for primary property display
    const newSelectedNode = selectedNodes[0] || null;
    const newSelectedEdge = selectedEdges[0] || null;
    
    setSelectedNode(newSelectedNode);
    setSelectedEdge(newSelectedEdge);
    
    // Handle multi-selection for property panel
    const nodeIds = selectedNodes.map((node: Node) => node.id);
    const edgeIds = selectedEdges.map((edge: Edge) => edge.id);
    
    setSelectedNodes(nodeIds);
    setSelectedEdges(edgeIds);
    
    // Auto-sync to replay if not already syncing
    if (!isSyncing && hasReplayData && (!isReplayMode || currentReplayStep === -1)) {
      autoSyncToReplay(newSelectedNode);
    }
  }, [
    setSelectedNode,
    setSelectedEdge,
    setSelectedNodes,
    setSelectedEdges,
    isSyncing,
    hasReplayData,
    isReplayMode,
    currentReplayStep,
    autoSyncToReplay
  ]);

  // Handle node clicks
  const onNodeClick = useCallback((event: React.MouseEvent, node: Node) => {
    handleCanvasNodeClick(node);
  }, [handleCanvasNodeClick]);

  // Handle edge clicks  
  const onEdgeClick = useCallback((event: React.MouseEvent, edge: Edge) => {
    handleCanvasEdgeClick(edge);
  }, [handleCanvasEdgeClick]);

  // Handle individual node deletion (from node component)
  const onDeleteNode = useCallback((nodeId: string) => {
    // Remove the node
    setNodes((nds) => nds.filter(n => n.id !== nodeId));
    // Remove all connected edges
    setEdges((eds) => eds.filter(e => e.source !== nodeId && e.target !== nodeId));
    // Clear selection if deleted node was selected
    if (selectedNode?.id === nodeId) {
      setSelectedNode(null);
    }
    setHasUnsavedChanges(true);
    if (announce) {
      announce(t('Element deleted.'));
    }
  }, [selectedNode, setSelectedNode, setNodes, setEdges, setHasUnsavedChanges, announce]);

  // Handle delete selected elements
  const handleDeleteSelected = useCallback(() => {
    const selectedNodes = nodes.filter(node => node.selected && !isNodeLocked(node.id));
    const selectedEdges = edges.filter(edge => edge.selected && !isEdgeLocked(edge.id));
    
    if (selectedNodes.length === 0 && selectedEdges.length === 0) return;
    
    // Delete selected nodes
    if (selectedNodes.length > 0) {
      const nodeIdsToDelete = selectedNodes.map(n => n.id);
      setNodes(prevNodes => prevNodes.filter(n => !nodeIdsToDelete.includes(n.id)));
      
      // Also delete any edges connected to these nodes
      setEdges(prevEdges => prevEdges.filter(e => 
        !nodeIdsToDelete.includes(e.source) && !nodeIdsToDelete.includes(e.target)
      ));
    }
    
    // Delete selected edges (that weren't already deleted by node deletion)
    if (selectedEdges.length > 0) {
      const edgeIdsToDelete = selectedEdges.map(e => e.id);
      setEdges(prevEdges => prevEdges.filter(e => !edgeIdsToDelete.includes(e.id)));
    }
    
    // Clear selections
    setSelectedNode(null);
    setSelectedEdge(null);
    setHasUnsavedChanges(true);

    // Announce deletion count to screen readers
    if (announce) {
      const totalDeleted = selectedNodes.length + selectedEdges.length;
      announce(t('@count elements deleted.', { '@count': String(totalDeleted) }));
    }
  }, [
    nodes,
    edges,
    isNodeLocked,
    isEdgeLocked,
    setNodes,
    setEdges,
    setSelectedNode,
    setSelectedEdge,
    setHasUnsavedChanges,
    announce
  ]);

  // Handle new connections between nodes
  const onConnect = useCallback((connection: { source: string | null; target: string | null; sourceHandle?: string | null; targetHandle?: string | null }) => {
    if (!connection.source || !connection.target) return;
    const id = generateUniqueEdgeId();
    const newEdge = {
      id,
      source: connection.source,
      target: connection.target,
      type: 'default',
    };
    setEdges(prev => [...prev, newEdge]);
    setHasUnsavedChanges(true);
  }, [setEdges, setHasUnsavedChanges]);

  // Handle canvas pane clicks (deselect all)
  const onPaneClick = useCallback(() => {
    paneClickedRef.current = true;
    setSelectedNode(null);
    setSelectedEdge(null);
  }, [setSelectedNode, setSelectedEdge]);

  // Handle node drag start — snapshot the position so we can detect actual movement
  const onNodeDragStart = useCallback((_event: React.MouseEvent, node: Node) => {
    dragStartPositions.current[node.id] = { x: node.position.x, y: node.position.y };
  }, []);

  // Handle node drag stop — mark dirty only if the node actually moved
  const onNodeDragStop = useCallback((_event: React.MouseEvent, node: Node) => {
    const startPos = dragStartPositions.current[node.id];
    delete dragStartPositions.current[node.id];
    if (startPos && (startPos.x !== node.position.x || startPos.y !== node.position.y)) {
      setHasUnsavedChanges(true);
    }
  }, [setHasUnsavedChanges]);

  // Handle viewport changes
  const onViewportChange = useCallback((_viewport: { x: number; y: number; zoom: number }) => {
    // Update viewport state if needed
  }, []);

  return {
    onNodesChange,
    onEdgesChange,
    onSelectionChange,
    onNodeClick,
    onEdgeClick,
    onDeleteNode,
    handleDeleteSelected,
    onConnect,
    onPaneClick,
    onNodeDragStart,
    onNodeDragStop,
    onViewportChange
  };
}