import React, { useMemo } from 'react';
import ReactFlow, {
  Node,
  Edge,
  Connection,
  NodeChange,
  EdgeChange,
  ConnectionLineType,
  MarkerType,
  SelectionMode,
  OnSelectionChangeParams,
  OnConnectStartParams,
  MiniMap,
  Viewport,
  PanOnScrollMode,
  EdgeLabelRenderer,
} from 'reactflow';
import { ReplayStep } from '../hooks/useSimpleReplaySync';
import { useEdgeOrdering } from '../hooks/useEdgeOrdering';

// Node Components
import CustomNode from './nodes/CustomNode';
import StartNode from './nodes/StartNode';
import GatewayNode from './nodes/GatewayNode';
import SubprocessNode from './nodes/SubprocessNode';

// Edge Components
import DefaultEdge from './edges/DefaultEdge';
import ConditionEdge from './edges/ConditionEdge';
import AnnotationEdge from './edges/AnnotationEdge';

/** ReactFlow event handlers passed through to the canvas. */
interface FlowCanvasEventHandlers {
  onNodesChange: (changes: NodeChange[]) => void;
  onEdgesChange: (changes: EdgeChange[]) => void;
  onConnect: (connection: Connection) => void;
  onSelectionChange: (params: OnSelectionChangeParams) => void;
  onConnectStart: (event: React.MouseEvent | React.TouchEvent, params: OnConnectStartParams) => void;
  onConnectEnd: (event: MouseEvent | TouchEvent) => void;
  onDrop: (event: React.DragEvent) => void;
  onDragOver: (event: React.DragEvent) => void;
  onDragEnter: (event: React.DragEvent) => void;
  onDragLeave: (event: React.DragEvent) => void;
  onNodeClick: (event: React.MouseEvent, node: Node) => void;
  onEdgeClick: (event: React.MouseEvent, edge: Edge) => void;
  onPaneClick: (event: React.MouseEvent) => void;
  onNodeDragStart: (event: React.MouseEvent, node: Node) => void;
  onNodeDragStop: (event: React.MouseEvent, node: Node) => void;
  onInit: (instance: any) => void;
}

/** Callbacks for updating individual nodes/edges. */
interface FlowCanvasElementCallbacks {
  onEdgeUpdate?: (edgeId: string, updates: any) => void;
  onNodeUpdate?: (nodeId: string, newData: any) => void;
  onDeleteNode?: (nodeId: string) => void;
}

/** Keyboard modifier key state. */
interface FlowCanvasModifierKeys {
  isShiftPressed: boolean;
  isCtrlPressed: boolean;
  isAltPressed: boolean;
}

/** Toggle flags controlling canvas UI features. */
interface FlowCanvasUIState {
  isDragActive: boolean;
  isLocked: boolean;
  showMinimap: boolean;
  showEdgeOrderNumbers: boolean;
  showAllAnnotations: boolean;
}

/** Search-related state. */
interface FlowCanvasSearchState {
  searchTerm: string;
  highlightedSearchResult: any;
}

/** Replay visualization state. */
interface FlowCanvasReplayState {
  replayData: ReplayStep[];
  currentReplayStep: number;
  isReplayMode: boolean;
  replayIndicators: Array<{
    id: string;
    x: number;
    y: number;
    color: string;
  }>;
}

/** Quick-add node/condition callbacks. */
interface FlowCanvasQuickAddProps {
  onQuickAdd?: (component: any, sourceNodeId: string) => void;
  onAddCondition?: (edgeId: string, component: any) => void;
}

interface FlowCanvasProps {
  // Data
  nodes: Node[];
  edges: Edge[];

  // ReactFlow event handlers
  eventHandlers: FlowCanvasEventHandlers;

  // Element update callbacks
  elementCallbacks: FlowCanvasElementCallbacks;

  // Viewport
  viewport: Viewport;
  onViewportChange: (viewport: Viewport) => void;

  // Modifier keys
  modifierKeys: FlowCanvasModifierKeys;

  // UI state
  uiState: FlowCanvasUIState;

  // Search
  search: FlowCanvasSearchState;

  // Replay
  replay: FlowCanvasReplayState;

  // Edge ordering
  setEdges: (updater: (edges: Edge[]) => Edge[]) => void;
  setHasUnsavedChanges: (value: boolean) => void;

  // Quick add
  quickAdd: FlowCanvasQuickAddProps;
}

// Node types configuration
const nodeTypes = {
  customNode: CustomNode,
  element: CustomNode,  // 'element' type uses CustomNode component
  start: StartNode,
  gateway: GatewayNode,
  subprocess: SubprocessNode,
};

// Edge types configuration
const edgeTypes = {
  default: DefaultEdge,
  condition: ConditionEdge,
  annotation: AnnotationEdge,
};

const FlowCanvas: React.FC<FlowCanvasProps> = ({
  nodes,
  edges,
  eventHandlers,
  elementCallbacks,
  viewport: _viewport,
  onViewportChange,
  modifierKeys,
  uiState,
  search,
  replay,
  setEdges,
  setHasUnsavedChanges,
  quickAdd,
}) => {
  // Destructure grouped props for use in the component body
  const {
    onNodesChange, onEdgesChange, onConnect, onSelectionChange,
    onConnectStart, onConnectEnd, onDrop, onDragOver, onDragEnter,
    onDragLeave, onNodeClick, onEdgeClick, onPaneClick, onNodeDragStart, onNodeDragStop, onInit,
  } = eventHandlers;
  const { onEdgeUpdate, onNodeUpdate, onDeleteNode } = elementCallbacks;
  const { isShiftPressed, isCtrlPressed, isAltPressed } = modifierKeys;
  const { isDragActive, isLocked, showMinimap, showEdgeOrderNumbers, showAllAnnotations } = uiState;
  const { searchTerm, highlightedSearchResult } = search;
  const { replayData, currentReplayStep, isReplayMode, replayIndicators } = replay;
  const { onQuickAdd, onAddCondition } = quickAdd;
  // Edge ordering hook - handles drag/drop reordering and order info calculation
  const {
    handleDragStart,
    handleDragEnd,
    handleEdgeOrderDrop,
    handleReorderEdge,
    getEdgeOrderInfo,
  } = useEdgeOrdering({ edges, nodes, setEdges, setHasUnsavedChanges });

  // Configure selection mode based on modifier keys
  const selectionMode = useMemo((): SelectionMode => {
    return isShiftPressed ? SelectionMode.Partial : SelectionMode.Full;
  }, [isShiftPressed]);

  // Set cursor classes based on modifier state
  const reactFlowClasses = useMemo(() => {
    const classes = [];
    if (isShiftPressed) {
      classes.push('shift-pressed');
    } else if (isCtrlPressed && isAltPressed) {
      classes.push('ctrl-alt-pressed');
    } else if (isCtrlPressed) {
      classes.push('ctrl-pressed');
    }
    return classes.join(' ');
  }, [isShiftPressed, isCtrlPressed, isAltPressed]);

  // Enhanced edges with order numbers and drag handlers
  const enhancedEdges = useMemo(() => {
    return edges.map((edge, _index) => {
      const edgeOrderInfo = getEdgeOrderInfo(edge, edges);
      const hasCondition = edge.data?.condition;
      
      // Note: Condition result indicators are now handled via separate indicator objects
      
      return {
        ...edge,
        data: {
          ...edge.data,
          // Global lock state (canvas locked / read-only / standalone).
          // Distinct from per-edge isLocked/locked which tracks individual
          // element locks.  Used by edge components to disable interactions
          // (e.g. edge order reorder) without hiding visual indicators.
          globalLocked: isLocked,
          edgeOrdersVisible: showEdgeOrderNumbers,
          edgeOrderInfo: edgeOrderInfo,
          showOrderNumbers: showEdgeOrderNumbers,
          showAnnotations: showAllAnnotations,
          // Individual annotation visibility (stored in edge data)
          isAnnotationVisible: showAllAnnotations || edge.data?.isAnnotationVisible || false,
          onToggleAnnotation: () => {
            // Toggle individual annotation visibility
            if (onEdgeUpdate) {
              const newAnnotationVisible = !edge.data?.isAnnotationVisible;
              onEdgeUpdate(edge.id, {
                ...edge.data,
                isAnnotationVisible: newAnnotationVisible
              });
            }
          },
          onDragStart: handleDragStart,
          onDragEnd: handleDragEnd,
          onDrop: handleEdgeOrderDrop,
          onReorderEdge: handleReorderEdge,
          onEdgeUpdate: onEdgeUpdate,
          // Only show quick add condition button on edges without conditions
          onAddCondition: !hasCondition && onAddCondition && !isLocked ? onAddCondition : undefined,
          searchTerm,
          isHighlighted: highlightedSearchResult?.id === edge.id,
          // Replay state
          replayData,
          currentReplayStep,
          isReplayMode,
        },
      };
    });
  }, [
    edges,
    getEdgeOrderInfo,
    showEdgeOrderNumbers,
    showAllAnnotations,
    handleDragStart,
    handleDragEnd,
    handleEdgeOrderDrop,
    handleReorderEdge,
    onEdgeUpdate,
    onAddCondition,
    isLocked,
    searchTerm,
    highlightedSearchResult,
    replayData,
    currentReplayStep,
    isReplayMode
  ]);

  // Enhanced nodes with search highlighting and replay state
  const enhancedNodes = useMemo(() => {
    return nodes.map(node => ({
      ...node,
      data: {
        ...node.data,
        showAnnotations: showAllAnnotations,
        // Individual annotation visibility (stored in node data)
        isAnnotationVisible: showAllAnnotations || node.data?.isAnnotationVisible || false,
        onToggleAnnotation: () => {
          // Toggle individual annotation visibility
          if (onNodeUpdate) {
            const newAnnotationVisible = !node.data?.isAnnotationVisible;
            onNodeUpdate(node.id, {
              ...node.data,
              isAnnotationVisible: newAnnotationVisible
            });
          }
        },
        searchTerm,
        isHighlighted: highlightedSearchResult?.id === node.id,
        // Replay state
        replayData,
        currentReplayStep,
        isReplayMode,
        // Node operations
        onDelete: onDeleteNode ? () => onDeleteNode(node.id) : undefined,
        onQuickAdd: onQuickAdd && !isLocked ? (component: any) => onQuickAdd(component, node.id) : undefined,
        isLocked,
      },
    }));
  }, [
    nodes, 
    showAllAnnotations, 
    onNodeUpdate,
    searchTerm, 
    highlightedSearchResult,
    replayData,
    currentReplayStep,
    isReplayMode,
    onDeleteNode,
    onQuickAdd,
    isLocked
  ]);

  return (
    <div 
      className={`reactflow-wrapper ${isDragActive ? 'drag-active' : ''}`}
    >
      <ReactFlow
        nodes={enhancedNodes}
        edges={enhancedEdges}
        onNodesChange={onNodesChange}
        onEdgesChange={onEdgesChange}
        onConnect={onConnect}
        onSelectionChange={onSelectionChange}
        onConnectStart={onConnectStart}
        onConnectEnd={onConnectEnd}
        onDrop={onDrop}
        onDragOver={onDragOver}
        onDragEnter={onDragEnter}
        onDragLeave={onDragLeave}
        onNodeClick={onNodeClick}
        onEdgeClick={onEdgeClick}
        onPaneClick={onPaneClick}
        onNodeDragStart={onNodeDragStart}
        onNodeDragStop={onNodeDragStop}
        onInit={onInit}
        onMove={(event, viewport) => onViewportChange(viewport)}
        nodeTypes={nodeTypes}
        edgeTypes={edgeTypes}
        connectionLineType={ConnectionLineType.SmoothStep}
        selectionMode={selectionMode}
        defaultMarkerColor="var(--modeler-color-edge-locked)"
        defaultEdgeOptions={{
          type: 'default',
          markerEnd: {
            type: MarkerType.Arrow,
            strokeWidth: 1.5,
            color: 'var(--modeler-color-edge-locked)',
          },
        }}
        nodesDraggable={!isLocked}
        nodesConnectable={!isLocked}
        elementsSelectable={true}  // Allow selection even when locked for viewing properties
        selectNodesOnDrag={false}  // Don't select on drag when locked
        panOnDrag={!isShiftPressed && !isCtrlPressed}
        zoomOnScroll={!isCtrlPressed}
        panOnScroll={false}
        zoomOnPinch
        panOnScrollMode={PanOnScrollMode.Vertical}
        selectionOnDrag={isShiftPressed && !isLocked}
        multiSelectionKeyCode="Shift"
        deleteKeyCode={null} // Handled by useKeyboardShortcuts
        fitView={false}
        attributionPosition="bottom-left"
        className={reactFlowClasses}
      >
        {showMinimap && (
          <MiniMap
            style={{
              backgroundColor: 'var(--modeler-color-minimap-bg)',
              border: '1px solid var(--modeler-color-minimap-border)',
            }}
            nodeColor={(node: Node) => {
              if (node.selected) return 'var(--modeler-color-minimap-selected)';
              if (node.data?.locked) return 'var(--modeler-color-minimap-locked)';
              return 'var(--modeler-color-minimap-node)';
            }}
            maskColor="var(--modeler-color-minimap-mask)"
          />
        )}
        {/* Replay condition indicators rendered inside EdgeLabelRenderer so
            they automatically follow canvas pan/zoom transforms. */}
        {replayIndicators.length > 0 && (
          <EdgeLabelRenderer>
            {replayIndicators.map(indicator => (
              <div
                key={indicator.id}
                className="replay-indicator"
                style={{
                  transform: `translate(-50%, -50%) translate(${indicator.x}px, ${indicator.y}px)`,
                  backgroundColor: indicator.color,
                }}
              />
            ))}
          </EdgeLabelRenderer>
        )}
      </ReactFlow>

    </div>
  );
};

export default FlowCanvas;