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

import useClickDrag from '@/hooks/useClickDrag';

import { EQBand } from '../../studio/types';
import { Q_ADJUSTMENT_SENSITIVITY } from './eqConstants';
import { isModifierKeyPressed } from './eqHelpers';
import {
  positionToFreq,
  positionToGain,
  positionToQ,
  qToPosition,
} from './eqUtils';

// Type definitions for drag events
interface DragEvent {
  event: MouseEvent | TouchEvent;
  clientX: number;
  clientY: number;
}

interface DragHandlers {
  onMouseMove: (event: DragEvent) => void;
  onMouseUp: () => void;
}

interface EQBandDragCallbacks {
  onDragStart: () => void;
  onDragEnd: () => void;
  onUpdate: (updates: Partial<EQBand>) => void;
}

/**
 * Custom hook for EQ band dragging with Q adjustment mode
 */
export function useEQBandDrag(
  band: EQBand,
  containerRef: React.RefObject<HTMLDivElement | null>,
  callbacks: EQBandDragCallbacks
) {
  const isQModeRef = useRef(false);
  const lockedPositionRef = useRef<{
    freq: number;
    gain: number;
  } | null>(null);
  const currentDragPositionRef = useRef<{
    freq: number;
    gain: number;
  } | null>(null);
  const currentQRef = useRef<number>(band.q);

  // Helper: Switch to Q mode
  const enterQMode = useCallback(() => {
    isQModeRef.current = true;
    document.body.requestPointerLock();
  }, []);

  // Helper: Switch to position mode
  const exitQMode = useCallback(() => {
    isQModeRef.current = false;
    lockedPositionRef.current = null;
    document.exitPointerLock();
  }, []);

  // Helper: Handle position (frequency/gain or frequency/Q) adjustment
  const handlePositionAdjustment = useCallback(
    (clientX: number, clientY: number, rect: DOMRect) => {
      const x = (clientX - rect.left) / rect.width;
      const y = 1 - (clientY - rect.top) / rect.height;

      // Clamp to bounds
      const clampedX = Math.max(0, Math.min(1, x));
      const clampedY = Math.max(0, Math.min(1, y));

      const newFreq = positionToFreq(clampedX);

      // For filter types (highpass, lowpass, notch), Y-axis controls Q instead of gain
      const isFilterType =
        band.type === 'highpass' ||
        band.type === 'lowpass' ||
        band.type === 'notch';

      if (isFilterType) {
        const newQ = positionToQ(clampedY);
        currentDragPositionRef.current = {
          freq: Math.round(newFreq),
          gain: band.gain, // Keep current gain
        };
        currentQRef.current = Math.round(newQ * 100) / 100;

        callbacks.onUpdate({
          frequency: currentDragPositionRef.current.freq,
          q: currentQRef.current,
        });
      } else {
        const newGain = positionToGain(clampedY);

        // Update current drag position ref immediately
        currentDragPositionRef.current = {
          freq: Math.round(newFreq),
          gain: Math.round(newGain * 10) / 10,
        };

        callbacks.onUpdate({
          frequency: currentDragPositionRef.current.freq,
          gain: currentDragPositionRef.current.gain,
        });
      }
    },
    [callbacks, band]
  );

  // Helper: Handle Q adjustment
  const handleQAdjustment = useCallback(
    (movementY: number, rect: DOMRect) => {
      // Skip if movement is too large (can happen on pointer lock start)
      if (Math.abs(movementY) > 100) return;

      const locked = lockedPositionRef.current ||
        currentDragPositionRef.current || {
          freq: band.frequency,
          gain: band.gain,
        };

      // Use the current Q from ref, which gets updated on each drag event
      const currentQ = currentQRef.current;
      const currentQPos = qToPosition(currentQ);

      // Convert movement to delta in Q space
      // Negative because moving mouse down should decrease Q
      const deltaQPos = -(movementY / rect.height) * Q_ADJUSTMENT_SENSITIVITY;
      const newQPos = Math.max(0, Math.min(1, currentQPos + deltaQPos));
      const newQ = positionToQ(newQPos);

      // Update the Q ref immediately for next frame
      const roundedQ = Math.round(newQ * 100) / 100;
      currentQRef.current = roundedQ;

      callbacks.onUpdate({
        frequency: locked.freq,
        gain: locked.gain,
        q: roundedQ,
      });
    },
    [callbacks, band]
  );

  const handleDrag = useCallback(
    ({ event }: DragEvent): DragHandlers | undefined => {
      if (!containerRef.current) return;

      // For filter types (highpass, lowpass, notch), ignore modifier keys
      // since Y-axis already controls Q directly
      const isFilterType =
        band.type === 'highpass' ||
        band.type === 'lowpass' ||
        band.type === 'notch';
      const cmdOrCtrl = !isFilterType && isModifierKeyPressed(event);

      // Mark drag as started
      callbacks.onDragStart();

      // Initialize current drag position and Q from current band state
      currentDragPositionRef.current = {
        freq: band.frequency,
        gain: band.gain,
      };
      currentQRef.current = band.q;

      // Initialize Q mode if cmd/ctrl is held (only for gain-based bands)
      if (cmdOrCtrl) {
        lockedPositionRef.current = { ...currentDragPositionRef.current };
        enterQMode();
      } else {
        exitQMode();
      }

      return {
        onMouseMove: ({ event, clientX, clientY }: DragEvent) => {
          if (!containerRef.current) return;

          event.preventDefault();
          const rect = containerRef.current.getBoundingClientRect();

          // For filter types, always ignore modifier keys
          const cmdOrCtrl = !isFilterType && isModifierKeyPressed(event);

          // Handle mode switching during drag (only for gain-based bands)
          if (!isFilterType) {
            if (cmdOrCtrl && !isQModeRef.current) {
              // Switching to Q mode - lock current dragged position
              lockedPositionRef.current = currentDragPositionRef.current
                ? { ...currentDragPositionRef.current }
                : {
                    freq: band.frequency,
                    gain: band.gain,
                  };
              enterQMode();
            } else if (!cmdOrCtrl && isQModeRef.current) {
              // Switching back to position mode
              exitQMode();
            }
          }

          if (cmdOrCtrl && event instanceof MouseEvent) {
            // Q adjustment mode - use movementY from pointer lock (gain-based bands only)
            handleQAdjustment(event.movementY, rect);
          } else {
            // Normal position adjustment mode
            handlePositionAdjustment(clientX, clientY, rect);
          }
        },
        onMouseUp: () => {
          exitQMode();
          currentDragPositionRef.current = null;

          // Commit changes to permanent state
          callbacks.onDragEnd();
        },
      };
    },
    [
      band,
      containerRef,
      callbacks,
      handlePositionAdjustment,
      handleQAdjustment,
      enterQMode,
      exitQMode,
    ]
  );

  const dragRef = useClickDrag(handleDrag);

  return { dragRef, exitQMode, isQModeRef };
}
