/**
 * Mobile Gesture Utilities
 * Provides touch gesture detection and handling
 */

export interface GestureEvent {
  type: 'swipe' | 'pinch' | 'rotate' | 'tap' | 'longpress' | 'doubletap';
  direction?: 'up' | 'down' | 'left' | 'right';
  scale?: number;
  rotation?: number;
  center?: { x: number; y: number };
  velocity?: { x: number; y: number };
}

export interface GestureHandlers {
  onSwipe?: (direction: 'up' | 'down' | 'left' | 'right', velocity: number) => void;
  onPinch?: (scale: number) => void;
  onRotate?: (angle: number) => void;
  onTap?: (x: number, y: number) => void;
  onLongPress?: (x: number, y: number) => void;
  onDoubleTap?: (x: number, y: number) => void;
}

export interface GestureConfig {
  swipeThreshold?: number;
  swipeVelocityThreshold?: number;
  longPressDelay?: number;
  doubleTapDelay?: number;
  pinchThreshold?: number;
}

const defaultConfig: Required<GestureConfig> = {
  swipeThreshold: 50,
  swipeVelocityThreshold: 0.3,
  longPressDelay: 500,
  doubleTapDelay: 300,
  pinchThreshold: 0.1,
};

export function createGestureHandler(
  element: HTMLElement,
  handlers: GestureHandlers,
  config: GestureConfig = {}
): () => void {
  const cfg = { ...defaultConfig, ...config };

  let startX = 0;
  let startY = 0;
  let startTime = 0;
  let longPressTimer: NodeJS.Timeout | null = null;
  let lastTapTime = 0;
  let initialDistance = 0;
  let initialAngle = 0;

  const getDistance = (touches: TouchList): number => {
    if (touches.length < 2) return 0;
    const dx = touches[1].clientX - touches[0].clientX;
    const dy = touches[1].clientY - touches[0].clientY;
    return Math.sqrt(dx * dx + dy * dy);
  };

  const getAngle = (touches: TouchList): number => {
    if (touches.length < 2) return 0;
    const dx = touches[1].clientX - touches[0].clientX;
    const dy = touches[1].clientY - touches[0].clientY;
    return (Math.atan2(dy, dx) * 180) / Math.PI;
  };

  const handleTouchStart = (e: TouchEvent) => {
    const touch = e.touches[0];
    startX = touch.clientX;
    startY = touch.clientY;
    startTime = Date.now();

    // Long press detection
    if (handlers.onLongPress) {
      longPressTimer = setTimeout(() => {
        handlers.onLongPress!(startX, startY);
        // Vibrate on long press if available
        if ('vibrate' in navigator) {
          navigator.vibrate(50);
        }
      }, cfg.longPressDelay);
    }

    // Pinch/rotate initialization
    if (e.touches.length === 2) {
      initialDistance = getDistance(e.touches);
      initialAngle = getAngle(e.touches);
    }
  };

  const handleTouchMove = (e: TouchEvent) => {
    // Cancel long press on move
    if (longPressTimer) {
      clearTimeout(longPressTimer);
      longPressTimer = null;
    }

    // Pinch detection
    if (e.touches.length === 2 && (handlers.onPinch || handlers.onRotate)) {
      const currentDistance = getDistance(e.touches);
      const currentAngle = getAngle(e.touches);

      if (handlers.onPinch && initialDistance > 0) {
        const scale = currentDistance / initialDistance;
        if (Math.abs(scale - 1) > cfg.pinchThreshold) {
          handlers.onPinch(scale);
        }
      }

      if (handlers.onRotate) {
        const rotation = currentAngle - initialAngle;
        handlers.onRotate(rotation);
      }
    }
  };

  const handleTouchEnd = (e: TouchEvent) => {
    // Clear long press timer
    if (longPressTimer) {
      clearTimeout(longPressTimer);
      longPressTimer = null;
    }

    const touch = e.changedTouches[0];
    const endX = touch.clientX;
    const endY = touch.clientY;
    const endTime = Date.now();

    const deltaX = endX - startX;
    const deltaY = endY - startY;
    const deltaTime = endTime - startTime;

    // Swipe detection
    if (handlers.onSwipe) {
      const velocity = Math.sqrt(deltaX * deltaX + deltaY * deltaY) / deltaTime;

      if (velocity > cfg.swipeVelocityThreshold) {
        if (Math.abs(deltaX) > Math.abs(deltaY)) {
          if (Math.abs(deltaX) > cfg.swipeThreshold) {
            handlers.onSwipe(deltaX > 0 ? 'right' : 'left', velocity);
          }
        } else {
          if (Math.abs(deltaY) > cfg.swipeThreshold) {
            handlers.onSwipe(deltaY > 0 ? 'down' : 'up', velocity);
          }
        }
      }
    }

    // Tap / Double tap detection
    if (Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10 && deltaTime < 300) {
      const now = Date.now();

      if (handlers.onDoubleTap && now - lastTapTime < cfg.doubleTapDelay) {
        handlers.onDoubleTap(endX, endY);
        lastTapTime = 0;
      } else if (handlers.onTap) {
        lastTapTime = now;
        // Delay tap to wait for potential double tap
        if (handlers.onDoubleTap) {
          setTimeout(() => {
            if (lastTapTime === now) {
              handlers.onTap!(endX, endY);
            }
          }, cfg.doubleTapDelay);
        } else {
          handlers.onTap(endX, endY);
        }
      }
    }
  };

  const handleTouchCancel = () => {
    if (longPressTimer) {
      clearTimeout(longPressTimer);
      longPressTimer = null;
    }
  };

  // Add event listeners
  element.addEventListener('touchstart', handleTouchStart, { passive: true });
  element.addEventListener('touchmove', handleTouchMove, { passive: true });
  element.addEventListener('touchend', handleTouchEnd, { passive: true });
  element.addEventListener('touchcancel', handleTouchCancel, { passive: true });

  // Return cleanup function
  return () => {
    element.removeEventListener('touchstart', handleTouchStart);
    element.removeEventListener('touchmove', handleTouchMove);
    element.removeEventListener('touchend', handleTouchEnd);
    element.removeEventListener('touchcancel', handleTouchCancel);

    if (longPressTimer) {
      clearTimeout(longPressTimer);
    }
  };
}

// React hook for gestures
export function useGestures(
  ref: React.RefObject<HTMLElement>,
  handlers: GestureHandlers,
  config?: GestureConfig
): void {
  if (typeof window === 'undefined') return;

  const handlersRef = { current: handlers };
  handlersRef.current = handlers;

  const element = ref.current;
  if (!element) return;

  // Note: In actual React usage, this should be inside a useEffect
  createGestureHandler(element, handlersRef.current, config);
}

// Haptic feedback utility
export function triggerHaptic(type: 'light' | 'medium' | 'heavy' = 'light'): void {
  if (!('vibrate' in navigator)) return;

  const patterns: Record<typeof type, number | number[]> = {
    light: 10,
    medium: 25,
    heavy: [50, 50, 50],
  };

  navigator.vibrate(patterns[type]);
}

// Prevent pull-to-refresh on specific elements
export function preventPullToRefresh(element: HTMLElement): () => void {
  let startY = 0;

  const handleTouchStart = (e: TouchEvent) => {
    startY = e.touches[0].clientY;
  };

  const handleTouchMove = (e: TouchEvent) => {
    const currentY = e.touches[0].clientY;
    const scrollTop = element.scrollTop;

    // Prevent if at top and pulling down
    if (scrollTop === 0 && currentY > startY) {
      e.preventDefault();
    }
  };

  element.addEventListener('touchstart', handleTouchStart, { passive: true });
  element.addEventListener('touchmove', handleTouchMove, { passive: false });

  return () => {
    element.removeEventListener('touchstart', handleTouchStart);
    element.removeEventListener('touchmove', handleTouchMove);
  };
}
