import { useEffect, useCallback, useRef } from 'react';

interface KeyboardShortcutCallbacks {
  onDelete?: () => void;
  onCopy?: () => void;
  onPaste?: () => void;
  onToggleSearch?: () => void;
  onEscape?: () => void;
}

interface ModifierStates {
  isShiftPressed: boolean;
  setIsShiftPressed: (pressed: boolean) => void;
  isCtrlPressed: boolean;
  setIsCtrlPressed: (pressed: boolean) => void;
  isAltPressed: boolean;
  setIsAltPressed: (pressed: boolean) => void;
}

interface KeyboardCapabilities {
  canDelete: boolean;
  canCopy: boolean;
  canPaste: boolean;
  canSearch: boolean;
  canEscape: boolean;
}

interface UseKeyboardShortcutsProps {
  callbacks: KeyboardShortcutCallbacks;
  modifiers: ModifierStates;
  capabilities: KeyboardCapabilities;
  isModelerFocused: boolean;
  getViewport?: () => any;
  setViewport?: (viewport: any) => void;
  enabled?: boolean; // Allow disabling shortcuts (e.g., during replay)
}

// ============ Extracted Helpers ============

/** Check if user is interacting with form elements */
function isInFormField(event: KeyboardEvent): boolean {
  const target = event.target as HTMLElement;
  return target.tagName === 'INPUT' || 
         target.tagName === 'TEXTAREA' || 
         target.contentEditable === 'true' ||
         target.isContentEditable;
}

/** Check if there is a text selection on the page */
function hasTextSelection(): boolean {
  return !!(window.getSelection &&
            (window.getSelection()?.toString().length ?? 0) > 0);
}

/**
 * Check if user is in a context where shortcuts should be suppressed.
 * Form fields block all shortcuts. Text selection only blocks copy
 * (to allow native text copy) but not paste, search, delete, etc.
 */
function isInputContext(event: KeyboardEvent): boolean {
  if (isInFormField(event)) return true;

  // Text selection only blocks Ctrl+C so the user can copy text natively.
  // Other shortcuts (paste, search, etc.) should still work.
  const ctrlOrMeta = event.ctrlKey || event.metaKey;
  if (hasTextSelection() && ctrlOrMeta && event.key === 'c') return true;

  return false;
}

/**
 * Handle mouse wheel events for canvas navigation.
 * Extracted as a pure function factory for testability.
 */
function createWheelHandler(
  getViewport: () => any,
  setViewport: (viewport: any) => void
) {
  return (e: WheelEvent) => {
    // Only handle wheel events on the ReactFlow canvas area
    const reactFlowWrapper = (e.target as HTMLElement).closest('.react-flow');
    if (!reactFlowWrapper) return;

    // Check actual key states from the event, not React state
    const ctrlPressed = e.ctrlKey || e.metaKey;
    const altPressed = e.altKey;
    const shiftPressed = e.shiftKey;

    if (ctrlPressed && !shiftPressed && !altPressed) {
      // Ctrl+wheel = vertical panning
      e.preventDefault();
      e.stopPropagation();
      const viewport = getViewport();
      setViewport({ ...viewport, y: viewport.y - e.deltaY * 0.5 });
      return;
    }
    
    if (ctrlPressed && altPressed && !shiftPressed) {
      // Ctrl+Alt+wheel = horizontal panning
      e.preventDefault();
      e.stopPropagation();
      const viewport = getViewport();
      setViewport({ ...viewport, x: viewport.x - e.deltaY * 0.5 });
      return;
    }

    // Normal wheel (no modifiers) = zoom toward cursor
    if (!ctrlPressed && !altPressed && !shiftPressed) {
      e.preventDefault();
      e.stopPropagation();
      const viewport = getViewport();
      const zoomSpeed = 0.1;
      const zoomFactor = e.deltaY > 0 ? 1 - zoomSpeed : 1 + zoomSpeed;
      const newZoom = Math.max(0.1, Math.min(2, viewport.zoom * zoomFactor));
      
      // Calculate zoom toward cursor position
      const rect = reactFlowWrapper.getBoundingClientRect();
      const mouseX = e.clientX - rect.left;
      const mouseY = e.clientY - rect.top;
      
      // Convert mouse position to flow coordinates
      const flowX = (mouseX - viewport.x) / viewport.zoom;
      const flowY = (mouseY - viewport.y) / viewport.zoom;
      
      // Calculate new viewport position to keep the point under the cursor
      const newX = viewport.x - (flowX * newZoom - flowX * viewport.zoom);
      const newY = viewport.y - (flowY * newZoom - flowY * viewport.zoom);
      
      setViewport({ x: newX, y: newY, zoom: newZoom });
      return;
    }
  };
}

/**
 * Check whether the keyboard event matches a registered shortcut.
 * Returns true if the event corresponds to a known shortcut key combination.
 */
function isShortcutKey(
  event: KeyboardEvent,
  capabilities: KeyboardCapabilities
): boolean {
  const ctrlOrMeta = event.ctrlKey || event.metaKey;
  if (event.key === 'Delete' && capabilities.canDelete) return true;
  if (ctrlOrMeta && event.key === 'c' && capabilities.canCopy) return true;
  if (ctrlOrMeta && event.key === 'v' && capabilities.canPaste) return true;
  if (ctrlOrMeta && event.key === 'f' && capabilities.canSearch) return true;
  if (event.key === 'Escape' && capabilities.canEscape) return true;
  return false;
}

/**
 * Create a keyboard shortcut handler that processes all shortcut keys.
 * Only executes callbacks on keydown; keypress/keyup only preventDefault.
 */
function createShortcutHandler(
  isModelerContext: (event: KeyboardEvent) => boolean,
  capabilities: KeyboardCapabilities,
  callbacks: KeyboardShortcutCallbacks
) {
  return (event: KeyboardEvent) => {
    if (!isModelerContext(event)) return;
    if (isInputContext(event)) return;
    
    // For keypress/keyup events, only prevent default to block browser behavior
    // (e.g. Firefox may fire keypress for Ctrl+F). Do NOT execute callbacks again.
    if (event.type !== 'keydown') {
      if (isShortcutKey(event, capabilities)) {
        event.preventDefault();
        event.stopPropagation();
      }
      return;
    }

    const { canDelete, canCopy, canPaste, canSearch, canEscape } = capabilities;
    const { onDelete, onCopy, onPaste, onToggleSearch, onEscape } = callbacks;
    const ctrlOrMeta = event.ctrlKey || event.metaKey;
    
    if (event.key === 'Delete' && canDelete && onDelete) {
      event.preventDefault();
      event.stopPropagation();
      onDelete();
      return false;
    }
    
    if (ctrlOrMeta && event.key === 'c' && canCopy && onCopy) {
      event.preventDefault();
      event.stopPropagation();
      onCopy();
      return false;
    }
    
    if (ctrlOrMeta && event.key === 'v' && canPaste && onPaste) {
      event.preventDefault();
      event.stopPropagation();
      onPaste();
      return false;
    }
    
    if (ctrlOrMeta && event.key === 'f' && canSearch && onToggleSearch) {
      event.preventDefault();
      event.stopPropagation();
      onToggleSearch();
      return false;
    }
    
    if (event.key === 'Escape' && canEscape && onEscape) {
      event.preventDefault();
      event.stopPropagation();
      onEscape();
      return false;
    }
  };
}

// ============ Main Hook ============

/**
 * Custom hook for managing keyboard shortcuts and modifiers in the modeler
 * 
 * Handles:
 * - Keyboard shortcuts (Ctrl+C, Ctrl+V, Ctrl+F, Delete, Escape)
 * - Modifier key tracking (Shift, Ctrl, Alt)
 * - Mouse wheel + modifier combinations for canvas navigation
 * - Context-aware event handling (avoid conflicts with form inputs)
 * - Firefox-specific event handling
 */
export const useKeyboardShortcuts = ({
  callbacks,
  modifiers,
  capabilities,
  isModelerFocused,
  getViewport,
  setViewport,
  enabled = true
}: UseKeyboardShortcutsProps) => {
  
  const { 
    isShiftPressed, setIsShiftPressed, 
    isCtrlPressed, setIsCtrlPressed,
    isAltPressed, setIsAltPressed 
  } = modifiers;

  // Check if event is within modeler context
  const isModelerContext = useCallback((event: KeyboardEvent): boolean => {
    if (isModelerFocused) return true;
    const target = event.target as HTMLElement;
    return target.closest('.workflow-modeler, .modeler') !== null;
  }, [isModelerFocused]);

  // Use refs to track current modifier state so event handlers always have
  // the latest values without needing to re-register on every state change.
  // This prevents missed keyup events during listener teardown/re-registration.
  const shiftRef = useRef(isShiftPressed);
  const ctrlRef = useRef(isCtrlPressed);
  const altRef = useRef(isAltPressed);

  // Keep refs in sync with props
  useEffect(() => { shiftRef.current = isShiftPressed; }, [isShiftPressed]);
  useEffect(() => { ctrlRef.current = isCtrlPressed; }, [isCtrlPressed]);
  useEffect(() => { altRef.current = isAltPressed; }, [isAltPressed]);

  // Handle keyboard modifiers and mouse wheel combinations
  useEffect(() => {
    if (!enabled) return;
    
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.shiftKey && !shiftRef.current) { shiftRef.current = true; setIsShiftPressed(true); }
      if ((e.ctrlKey || e.metaKey) && !ctrlRef.current) { ctrlRef.current = true; setIsCtrlPressed(true); }
      if (e.altKey && !altRef.current) { altRef.current = true; setIsAltPressed(true); }
    };

    const handleKeyUp = (e: KeyboardEvent) => {
      if (!e.shiftKey && shiftRef.current) { shiftRef.current = false; setIsShiftPressed(false); }
      if (!e.ctrlKey && !e.metaKey && ctrlRef.current) { ctrlRef.current = false; setIsCtrlPressed(false); }
      if (!e.altKey && altRef.current) { altRef.current = false; setIsAltPressed(false); }
    };

    window.addEventListener('keydown', handleKeyDown);
    window.addEventListener('keyup', handleKeyUp);
    
    let handleWheel: ((e: WheelEvent) => void) | undefined;
    if (getViewport && setViewport) {
      handleWheel = createWheelHandler(getViewport, setViewport);
      document.addEventListener('wheel', handleWheel, { passive: false, capture: true });
    }

    return () => {
      window.removeEventListener('keydown', handleKeyDown);
      window.removeEventListener('keyup', handleKeyUp);
      if (handleWheel) {
        document.removeEventListener('wheel', handleWheel, { capture: true });
      }
    };
  }, [
    enabled, 
    setIsShiftPressed, setIsCtrlPressed, setIsAltPressed, 
    getViewport, setViewport
  ]);

  // Handle keyboard shortcuts (single handler for keydown + Firefox keypress/keyup)
  useEffect(() => {
    if (!enabled) return;
    
    const handleShortcut = createShortcutHandler(isModelerContext, capabilities, callbacks);

    // Add multiple event listeners with capture to ensure we intercept before other handlers
    document.addEventListener('keydown', handleShortcut, { capture: true });
    document.addEventListener('keypress', handleShortcut, { capture: true });
    document.addEventListener('keyup', handleShortcut, { capture: true });
    
    // Additional Firefox-specific handling on the modeler element
    const modelerElement = document.querySelector('.workflow-modeler, .modeler');
    if (modelerElement) {
      modelerElement.addEventListener('keydown', handleShortcut as EventListener, { capture: true });
      modelerElement.addEventListener('keypress', handleShortcut as EventListener, { capture: true });
      modelerElement.addEventListener('keyup', handleShortcut as EventListener, { capture: true });
    }

    return () => {
      document.removeEventListener('keydown', handleShortcut, { capture: true });
      document.removeEventListener('keypress', handleShortcut, { capture: true });
      document.removeEventListener('keyup', handleShortcut, { capture: true });

      if (modelerElement) {
        modelerElement.removeEventListener('keydown', handleShortcut as EventListener, { capture: true });
        modelerElement.removeEventListener('keypress', handleShortcut as EventListener, { capture: true });
        modelerElement.removeEventListener('keyup', handleShortcut as EventListener, { capture: true });
      }
    };
  }, [
    enabled,
    capabilities,
    isModelerContext,
    callbacks
  ]);
};
