import { useState, useRef } from 'react';
import { DEBUG_MODE, PIANO_ROLL_CONSTANTS } from '../constants';
import { KGMidiRegion } from '../core/region/KGMidiRegion';
import { KGMidiNote } from '../core/midi/KGMidiNote';
import { KGTrack } from '../core/track/KGTrack';
import { KGCore } from '../core/KGCore';
import { KGPianoRollState } from '../core/state/KGPianoRollState';
import { useProjectStore } from '../stores/projectStore';

interface UseNoteSelectionProps {
  activeRegion: KGMidiRegion | null;
  updateTrack: (track: KGTrack) => void;
  tracks: KGTrack[];
}

export const useNoteSelection = ({
  activeRegion,
  updateTrack,
  tracks
}: UseNoteSelectionProps) => {
  // Get store actions
  const { clearAllSelections } = useProjectStore();
  
  // State for note selection
  const [selectedNoteIds, setSelectedNoteIds] = useState<Set<string>>(new Set());
  
  // Use refs for box selection to avoid async state update issues
  const isBoxSelectingRef = useRef<boolean>(false);
  const selectionBoxRef = useRef<{ startX: number, startY: number, endX: number, endY: number }>({
    startX: 0, startY: 0, endX: 0, endY: 0
  });
  
  // We still need a state for the selection box to trigger re-renders
  const [selectionBoxRender, setSelectionBoxRender] = useState<number>(0);
  
  // Flag to prevent deselection after box selection
  const preventDeselectRef = useRef<boolean>(false);
  
  // Track shift key state for box selection
  const isShiftKeyPressedRef = useRef<boolean>(false);
  
  // Get KGCore instance
  const core = KGCore.instance();

  const deselectAutomationEvents = () => {
    if (!activeRegion) return;

    activeRegion.getPitchBends().forEach(pitchBend => {
      pitchBend.deselect();
    });
    activeRegion.getControllerEventsByType().forEach(events => {
      events.forEach(controllerEvent => {
        controllerEvent.deselect();
      });
    });
  };
  
  // Handle note click for selection
  const handleNoteClick = (noteId: string, e: React.MouseEvent) => {
    if (!activeRegion) return;
    
    // Disable note selection in pencil mode
    if (KGPianoRollState.instance().getActiveTool() === 'pencil') {
      return;
    }
    
    // Find the note
    const note = activeRegion.getNotes().find(n => n.getId() === noteId);
    if (!note) return;
    
    if (DEBUG_MODE.PIANO_ROLL) {
      console.log(`NOTE CLICKED: noteId=${noteId}, shift key: ${e.shiftKey}`);
    }

    deselectAutomationEvents();
    
    // Create a new Set for selected note IDs
    let newSelectedNoteIds: Set<string>;
    
    // Check if shift key is pressed for multi-selection
    if (e.shiftKey) {
      // Shift key pressed - add/remove to existing selection
      newSelectedNoteIds = new Set(selectedNoteIds);
      
      if (newSelectedNoteIds.has(noteId)) {
        // Deselect the note
        newSelectedNoteIds.delete(noteId);
        
        // Update the note's selection state
        note.deselect();
        
        // Remove from KGCore selection
        core.removeSelectedItem(note);
        
        if (DEBUG_MODE.PIANO_ROLL) {
          console.log(`Deselected note: ${noteId}`);
        }
      } else {
        // Add to selection
        newSelectedNoteIds.add(noteId);
        
        // Update the note's selection state
        note.select();
        
        // Add to KGCore selection
        core.addSelectedItem(note);
        
        // Update last edited note length
        const noteLength = note.getEndBeat() - note.getStartBeat();
        KGPianoRollState.instance().setLastEditedNoteLength(noteLength);
        
        if (DEBUG_MODE.PIANO_ROLL) {
          console.log(`Added note to selection: ${noteId}`);
        }
      }
    } else {
      // Shift key not pressed - replace selection
      
      // First deselect all currently selected notes
      activeRegion.getNotes().forEach(n => {
        if (n.isSelected()) {
          n.deselect();
        }
      });
      
      // Clear KGCore selection using store method
      clearAllSelections();
      deselectAutomationEvents();
      
      // Create new selection with only this note
      newSelectedNoteIds = new Set([noteId]);
      
      // Select the note
      note.select();
      
      // Add to KGCore selection
      core.addSelectedItem(note);
      
      // Update last edited note length
      const noteLength = note.getEndBeat() - note.getStartBeat();
      KGPianoRollState.instance().setLastEditedNoteLength(noteLength);
      
      if (DEBUG_MODE.PIANO_ROLL) {
        console.log(`Selected note (replacing previous selection): ${noteId}`);
      }
    }
    
    // Update selection state
    setSelectedNoteIds(newSelectedNoteIds);
    
    // Update the track to persist the selection state
    const track = tracks.find(t => t.getId().toString() === activeRegion.getTrackId());
    if (track) {
      updateTrack(track);
    }
  };
  
  // Handle click on piano grid (background) to start box selection or deselect all notes
  const handleBackgroundClick = (e: React.MouseEvent) => {
    // Only handle direct clicks on the piano grid, not on notes
    if (e.target === e.currentTarget) {
      if (DEBUG_MODE.PIANO_ROLL) {
        console.log('Piano grid background clicked');
      }
      
      // Check if we should prevent deselection (right after box selection)
      if (preventDeselectRef.current) {
        if (DEBUG_MODE.PIANO_ROLL) {
          console.log('Preventing deselection after box selection');
        }
        preventDeselectRef.current = false;
        return;
      }
      
      // If we're not starting a box selection, deselect all notes
      if (!isBoxSelectingRef.current) {
        if (activeRegion) {
          // Deselect all notes in the region
          activeRegion.getNotes().forEach(note => {
            note.deselect();
          });
          
          // Update the track to persist the selection state
          const track = tracks.find(t => t.getId().toString() === activeRegion.getTrackId());
          if (track) {
            updateTrack(track);
          }
        }
        
        // Clear local selection
        setSelectedNoteIds(new Set());
        
        // Clear KGCore selection using store method
        clearAllSelections();
      }
    }
  };

  // Handle mouse down on piano grid for box selection
  const handleBackgroundMouseDown = (e: React.MouseEvent) => {
    // Only handle direct clicks on the piano grid, not on notes
    if (e.target === e.currentTarget) {
      // Disable box selection in pencil mode
      if (KGPianoRollState.instance().getActiveTool() === 'pencil') {
        return;
      }
      if (DEBUG_MODE.PIANO_ROLL) {
        console.log('Starting box selection');
      }

      deselectAutomationEvents();
      
      // Store shift key state for box selection
      isShiftKeyPressedRef.current = e.shiftKey;
      
      if (DEBUG_MODE.PIANO_ROLL) {
        console.log(`Box selection with shift key: ${isShiftKeyPressedRef.current}`);
      }
      
      // Get the grid element's bounding rectangle
      const rect = e.currentTarget.getBoundingClientRect();
      if (!rect) return;
      
      // Calculate relative position within the grid
      const startX = e.clientX - rect.left;
      const startY = e.clientY - rect.top;
      
      // Initialize selection box using ref for immediate update
      selectionBoxRef.current = {
        startX,
        startY,
        endX: startX,
        endY: startY
      };
      
      // Set box selection mode
      isBoxSelectingRef.current = true;
      
      // Force a re-render to show the initial selection box
      setSelectionBoxRender(prev => prev + 1);
      
      // Add global event listeners for mouse move and up
      document.addEventListener('mousemove', handleBoxSelectionMouseMove);
      document.addEventListener('mouseup', handleBoxSelectionMouseUp);
    }
  };
  
  // Handle mouse move during box selection
  const handleBoxSelectionMouseMove = (e: MouseEvent) => {
    if (!isBoxSelectingRef.current) return;
    
    // Get the grid element's bounding rectangle
    const gridElement = document.querySelector('.piano-grid');
    if (!gridElement) return;
    
    const rect = gridElement.getBoundingClientRect();
    
    // Calculate relative position within the grid
    const endX = e.clientX - rect.left;
    const endY = e.clientY - rect.top;
    
    // Update selection box using ref for immediate update
    selectionBoxRef.current = {
      ...selectionBoxRef.current,
      endX,
      endY
    };
    
    // Force a re-render to update the selection box
    setSelectionBoxRender(prev => prev + 1);
  };
  
  // Handle mouse up to end box selection
  const handleBoxSelectionMouseUp = (e: MouseEvent) => {
    if (!isBoxSelectingRef.current || !activeRegion) return;
    
    // Calculate the normalized coordinates (top-left to bottom-right)
    const { startX, startY, endX, endY } = selectionBoxRef.current;
    const left = Math.min(startX, endX);
    const top = Math.min(startY, endY);
    const right = Math.max(startX, endX);
    const bottom = Math.max(startY, endY);

    const mouseReleaseX = endX;
    const mouseReleaseY = endY;
    
    if (DEBUG_MODE.PIANO_ROLL) {
      console.log('Box selection ended');
      console.log(`Selection box coordinates: left=${left}, top=${top}, right=${right}, bottom=${bottom}`);
      console.log(`Width: ${right - left}, Height: ${bottom - top}`);
      console.log(`Shift key was pressed: ${isShiftKeyPressedRef.current}`);
    }
    
    // Check if this is a click (very small box) or an actual selection
    const isClick = (right - left < PIANO_ROLL_CONSTANTS.DRAG_THRESHOLD) && (bottom - top < PIANO_ROLL_CONSTANTS.DRAG_THRESHOLD);
    
    // Only proceed with selection if this is not just a click
    if (!isClick) {
      // Get all notes in the region
      const notes = activeRegion.getNotes();
      
      // Create a new set for selected note IDs
      let newSelectedNoteIds: Set<string>;
      
      // If shift key is pressed, start with current selection
      if (isShiftKeyPressedRef.current) {
        newSelectedNoteIds = new Set(selectedNoteIds);
      } else {
        // If shift key is not pressed, deselect all notes first
        notes.forEach(note => {
          note.deselect();
        });
        
        // Clear KGCore selection using store method
        clearAllSelections();
        deselectAutomationEvents();
        
        // Start with empty selection
        newSelectedNoteIds = new Set<string>();
      }
      
      // Get the beat width and note height for position calculations
      const beatWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')) || 40;
      const noteHeight = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--region-piano-key-height')) || 20;
      const regionStartBeat = activeRegion.getStartFromBeat();
      
      // Track notes that are in the box selection
      const notesInBox = new Set<string>();
      
      // Track notes with their distances to mouse release position for finding closest
      const notesWithDistances: { note: KGMidiNote, distance: number }[] = [];
      
      // Check each note to see if any part of it is inside the selection box
      notes.forEach(note => {
        // Calculate position and size
        const startBeat = note.getStartBeat() + regionStartBeat; // Absolute beat position
        const endBeat = note.getEndBeat() + regionStartBeat; // Absolute beat position
        const pitch = note.getPitch();
        
        // Convert pitch to y position (higher notes have lower y values)
        const pitchIndex = 107 - pitch; // Reverse the pitch to get the index (B7 is 107)
        
        // Calculate position and dimensions of the note
        const noteLeft = startBeat * beatWidth;
        const noteTop = pitchIndex * noteHeight;
        const noteRight = endBeat * beatWidth;
        const noteBottom = noteTop + noteHeight;
        
        // Check if any part of the note is inside the selection box
        // This checks if any of the corners or edges of the note are inside the box
        const isIntersecting = !(
          noteRight < left || // Note is completely to the left of the box
          noteLeft > right || // Note is completely to the right of the box
          noteBottom < top || // Note is completely above the box
          noteTop > bottom    // Note is completely below the box
        );
        
        if (isIntersecting) {
          // Add to the set of notes in the box
          notesInBox.add(note.getId());
          
          // Calculate distance from mouse release position to note center
          const noteCenterX = (noteLeft + noteRight) / 2;
          const noteCenterY = (noteTop + noteBottom) / 2;
          const distance = Math.sqrt(
            Math.pow(mouseReleaseX - noteCenterX, 2) + 
            Math.pow(mouseReleaseY - noteCenterY, 2)
          );
          notesWithDistances.push({ note, distance });
          
          if (isShiftKeyPressedRef.current) {
            // If shift is pressed, toggle the selection
            if (newSelectedNoteIds.has(note.getId())) {
              // Deselect the note
              newSelectedNoteIds.delete(note.getId());
              note.deselect();
              core.removeSelectedItem(note);
              
              if (DEBUG_MODE.PIANO_ROLL) {
                console.log(`Deselected note by shift+box selection: ${note.getId()}`);
              }
            } else {
              // Select the note
              newSelectedNoteIds.add(note.getId());
              note.select();
              core.addSelectedItem(note);
              
              if (DEBUG_MODE.PIANO_ROLL) {
                console.log(`Selected note by shift+box selection: ${note.getId()}`);
              }
            }
          } else {
            // Regular box selection - always select
            newSelectedNoteIds.add(note.getId());
            note.select();
            core.addSelectedItem(note);
            
            if (DEBUG_MODE.PIANO_ROLL) {
              console.log(`Selected note by box selection: ${note.getId()}`);
            }
          }
        } else if (!isShiftKeyPressedRef.current) {
          // For non-shift selection, make sure notes outside the box are deselected
          note.deselect();
        }
      });
      
      // Update selection state
      setSelectedNoteIds(newSelectedNoteIds);
      
      // Find the closest note to mouse release position and update last edited note length
      if (notesWithDistances.length > 0) {
        const closestNote = notesWithDistances.reduce((closest, current) => 
          current.distance < closest.distance ? current : closest
        ).note;
        
        const noteLength = closestNote.getEndBeat() - closestNote.getStartBeat();
        KGPianoRollState.instance().setLastEditedNoteLength(noteLength);
        
        if (DEBUG_MODE.PIANO_ROLL) {
          console.log(`Updated last edited note length to ${noteLength} from closest note: ${closestNote.getId()}`);
        }
      }
      
      if (DEBUG_MODE.PIANO_ROLL) {
        console.log(`Selected ${newSelectedNoteIds.size} notes with box selection`);
      }
      
      // Update the track to persist the selection state
      const track = tracks.find(t => t.getId().toString() === activeRegion.getTrackId());
      if (track) {
        updateTrack(track);
      }
      
      // Set the flag to prevent immediate deselection
      preventDeselectRef.current = true;
    }
    
    // End box selection
    isBoxSelectingRef.current = false;
    
    // Force a re-render to hide the selection box
    setSelectionBoxRender(prev => prev + 1);
    
    // Remove global event listeners
    document.removeEventListener('mousemove', handleBoxSelectionMouseMove);
    document.removeEventListener('mouseup', handleBoxSelectionMouseUp);
  };
  
  // Clean up function to remove event listeners
  const cleanupSelectionListeners = () => {
    document.removeEventListener('mousemove', handleBoxSelectionMouseMove);
    document.removeEventListener('mouseup', handleBoxSelectionMouseUp);
  };
  
  return {
    selectedNoteIds,
    isBoxSelectingRef,
    selectionBoxRef,
    selectionBoxRender,
    handleNoteClick,
    handleBackgroundClick,
    handleBackgroundMouseDown,
    cleanupSelectionListeners
  };
}; 
