/**
 * Custom hook for drag and drop functionality in the modeler
 * Handles component dragging from the panel and dropping onto the canvas
 */

import { useCallback, useState } from 'react';
import { useReactFlow } from 'reactflow';
import { useStore, Node, Edge } from '../store/store';
import { generateNodeId } from '../utils/clipboardUtils';
import { getComponentTypeFromCategory } from '../utils/modelUtils';
import { getEdgeTypeWithCondition } from '../utils/edgeTypeUtils';
import { findNearestEdge } from '../utils/layoutHelpers';

interface UseDragAndDropProps {
  isLocked: boolean;
  setHasUnsavedChanges: (value: boolean) => void;
}

export function useDragAndDrop({
  isLocked,
  setHasUnsavedChanges
}: UseDragAndDropProps) {
  const { screenToFlowPosition } = useReactFlow();
  const nodes = useStore(state => state.nodes);
  const edges = useStore(state => state.edges);
  const setNodes = useStore(state => state.setNodes);
  const setEdges = useStore(state => state.setEdges);
  // Drag state
  const [isDraggingCondition, setIsDraggingCondition] = useState(false);
  const [hoveredDropEdge, setHoveredDropEdge] = useState<Edge | null>(null);
  const [draggedComponentType, setDraggedComponentType] = useState<any>(null);
  const [isDragActive, setIsDragActive] = useState(false);

  // Wrapper around the extracted findNearestEdge utility for use in callbacks
  const findNearestEdgeForDrop = useCallback((position: { x: number; y: number }, maxDistance = 80, currentEdges?: Edge[], currentNodes?: Node[]): Edge | null => {
    return findNearestEdge(position, currentEdges || edges, currentNodes || nodes, maxDistance);
  }, [edges, nodes]);

  const onDrop = useCallback((event: React.DragEvent) => {
    event.preventDefault();
    
    // Prevent dropping components when canvas is locked
    if (isLocked) {
      return;
    }
    
    const type = event.dataTransfer.getData('application/reactflow');
    const plugin = event.dataTransfer.getData('application/plugin');
    const componentData = JSON.parse(event.dataTransfer.getData('component') || '{}');
    
    if (type && plugin) {
      const position = screenToFlowPosition({
        x: event.clientX,
        y: event.clientY,
      });

      // Special handling for condition/decision components
      if (componentData.category === 'Conditions' || componentData.category === 'Decisions') {
        const nearestEdge = findNearestEdgeForDrop(position);
        
        if (!nearestEdge) {
          // Show error message - no edge nearby
          alert('Decision components can only be attached to existing connections. Please drop near a connection line.');
          setIsDraggingCondition(false);
          setHoveredDropEdge(null);
          return;
        }

        // Check if the edge is locked
        if (nearestEdge.data?.locked) {
          alert('Cannot attach conditions to locked connections. Please unlock the connection first.');
          setIsDraggingCondition(false);
          setHoveredDropEdge(null);
          return;
        }
        
        // Attach condition to the edge
        const conditionLabel = componentData.label || plugin.split('.').pop() || plugin;

        // Deselect all nodes so ReactFlow's internal selection stays in sync.
        setNodes(prev => {
          const hasSelected = prev.some(n => n.selected);
          return hasSelected ? prev.map(n => n.selected ? { ...n, selected: false } : n) : prev;
        });

        // Update the target edge with condition data and mark it as selected,
        // deselecting all other edges.
        // ReactFlow's onSelectionChange will update the store's selection state.
        setEdges(prev => 
          prev.map(edge => {
            if (edge.id === nearestEdge.id) {
              return { 
                ...edge,
                label: conditionLabel,
                selected: true,
                data: { 
                  ...edge.data, 
                  condition: plugin,
                  conditionLabel: conditionLabel
                },
                type: getEdgeTypeWithCondition(plugin, edge.data)
              };
            }
            return edge.selected ? { ...edge, selected: false } : edge;
          })
        );
        
        setHasUnsavedChanges(true);
        setIsDraggingCondition(false);
        setHoveredDropEdge(null);
        return;
      }

      // Regular node creation for non-condition components
      const label = componentData.label || plugin.split('.').pop() || plugin;
      const nodeType = type === 'start' ? 'start' : (type === 'gateway' ? 'gateway' : 'element');
      const componentType = getComponentTypeFromCategory(componentData.category);
      const id = generateNodeId(label, nodeType);
      const newNode = {
        id,
        type: nodeType,
        position,
        selected: true,
        data: {
          plugin,
          label,
          category: componentData.category,
          componentType,
          description: componentData.description,
          documentationUrl: componentData.documentationUrl,
        },
      };

      // Deselect all existing nodes/edges and add the new node as selected.
      // ReactFlow's onSelectionChange will update the store's selection state.
      setNodes(prev => [...prev.map(n => n.selected ? { ...n, selected: false } : n), newNode]);
      setEdges(prev => {
        const hasSelected = prev.some(e => e.selected);
        return hasSelected ? prev.map(e => e.selected ? { ...e, selected: false } : e) : prev;
      });
      
      setHasUnsavedChanges(true);
    }
    
    // Reset drag states
    setIsDraggingCondition(false);
    setHoveredDropEdge(null);
  }, [isLocked, screenToFlowPosition, setNodes, setEdges, findNearestEdgeForDrop, setHasUnsavedChanges, setIsDraggingCondition, setHoveredDropEdge]);

  const onDragOver = useCallback((event: React.DragEvent) => {
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
    
    if (screenToFlowPosition) {
      const position = screenToFlowPosition({
        x: event.clientX,
        y: event.clientY,
      });
      
      // If dragging a condition, provide visual feedback for edges
      if (isDraggingCondition) {
        const nearestEdge = findNearestEdgeForDrop(position);
        
        // Update hovered drop edge state - allow replacement on edges with conditions
        const validDropEdge = nearestEdge && !nearestEdge.data?.locked ? nearestEdge : null;
        if (hoveredDropEdge?.id !== validDropEdge?.id) {
          setHoveredDropEdge(validDropEdge);
        }
      }
    }
  }, [isDraggingCondition, screenToFlowPosition, findNearestEdgeForDrop, hoveredDropEdge, setHoveredDropEdge]);

  // Component panel drag handlers
  const onComponentDrag = useCallback((event: React.DragEvent, component: any) => {
    event.dataTransfer.setData('application/reactflow', component.type || 'element');
    event.dataTransfer.setData('application/plugin', component.plugin);
    event.dataTransfer.setData('component', JSON.stringify(component));
    event.dataTransfer.effectAllowed = 'move';
    setIsDragActive(true);
  }, []);

  const onComponentDragEnd = useCallback(() => {
    setIsDragActive(false);
    setDraggedComponentType(null);
  }, []);

  const onComponentDragStart = useCallback((component: any) => {
    const dragType = {
      type: component.type || 'element',
      category: component.category
    };
    setDraggedComponentType(dragType);
    
    // Handle condition dragging
    if (component.category === 'Conditions' || component.category === 'Decisions') {
      setIsDraggingCondition(true);
    }
  }, []);

  // Condition-specific drag handlers for edge attachments
  const onConditionDragStart = useCallback(() => {
    setIsDraggingCondition(true);
  }, []);

  const onConditionDragEnd = useCallback(() => {
    setIsDraggingCondition(false);
    setHoveredDropEdge(null);
  }, []);

  return {
    // Event handlers
    onDrop,
    onDragOver,
    onComponentDrag,
    onComponentDragEnd,
    onComponentDragStart,
    onConditionDragStart,
    onConditionDragEnd,
    
    // State
    isDraggingCondition,
    hoveredDropEdge,
    draggedComponentType,
    isDragActive,
    
    // Helper functions
    findNearestEdge: findNearestEdgeForDrop
  };
}