import { addSecondsToBeats } from '@suno/studiokit/timeMapping';
import { RefObject, useEffect, useRef } from 'react';

import snap from '@/utils/snap';

import { getDerivedTiming } from './selectors';
import { AFTER_LAST_TRACK, StudioProjectState } from './types';
import useStudioTimelineController from './useStudioTimelineController';

export default function useFileDropReceiver(
  stateRef: RefObject<StudioProjectState>,
  gridSizeRef: RefObject<number>,
  arrangeFile: (
    trackId: string | null,
    clipSettings: {
      startBeats: number;
      endBeats: number;
      downbeats?: [number, number][];
    },
    file: File,
    audioBuffer: AudioBuffer
  ) => void,
  timelineController: ReturnType<typeof useStudioTimelineController>
) {
  // Drag and drop state
  const draggedFilePositionRef = useRef<{
    beats: number;
    trackId: string;
  } | null>(null);

  useEffect(() => {
    const wrapper = timelineController.timelineAndTrackHeadersContainer;
    if (!wrapper) return;
    // Drag and drop event listeners
    const handleDragOver = (e: DragEvent) => {
      e.preventDefault();
      e.stopPropagation();

      if (!e.dataTransfer?.types.includes('Files')) return;

      const rect = timelineController.getWrapperRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;

      // Calculate beats position
      const exactBeats =
        timelineController.getTimelineStartBeats() +
        x / timelineController.pxPerBeatRef.current;
      const beats = snap(exactBeats, gridSizeRef.current);

      // Find track ID by comparing y coordinate with track header bounding rects
      let trackId: string = AFTER_LAST_TRACK;
      const trackHeaders = Object.entries(
        timelineController.trackHeadersRef.current
      );

      for (const [id, headerEl] of trackHeaders) {
        const headerRect = headerEl.getBoundingClientRect();
        const relativeTop = headerRect.top - rect.top;
        const relativeBottom = headerRect.bottom - rect.top;

        if (y >= relativeTop && y <= relativeBottom) {
          trackId = id;
          break;
        }
      }

      timelineController.frameCountRef.current++;
      draggedFilePositionRef.current = { beats, trackId };
    };

    const handleDrop = async (e: DragEvent) => {
      e.preventDefault();
      e.stopPropagation();

      if (!e.dataTransfer?.files.length || !draggedFilePositionRef.current)
        return;

      const file = e.dataTransfer.files[0];
      if (!file.type.startsWith('audio/')) return;

      const { beats, trackId } = draggedFilePositionRef.current;

      try {
        // Create AudioBuffer from file
        const arrayBuffer = await file.arrayBuffer();
        const audioContext = new (window.AudioContext ||
          (window as any).webkitAudioContext)();
        const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);

        // Calculate duration in beats
        const durationSeconds = audioBuffer.duration;
        const endBeats = addSecondsToBeats(
          durationSeconds,
          beats,
          getDerivedTiming(stateRef.current)
        );

        // Call arrangeFile with the calculated values
        arrangeFile(
          trackId,
          {
            startBeats: beats,
            endBeats,
          },
          file,
          audioBuffer
        );
      } catch (error) {
        console.error('Error processing dropped audio file:', error);
      }

      timelineController.frameCountRef.current++;
      draggedFilePositionRef.current = null;
    };

    const handleDragLeave = (e: DragEvent) => {
      e.preventDefault();
      e.stopPropagation();

      // Only clear drag data if we're leaving the container entirely
      if (!wrapper?.contains(e.relatedTarget as Node)) {
        timelineController.frameCountRef.current++;
        draggedFilePositionRef.current = null;
      }
    };

    wrapper.addEventListener('dragover', handleDragOver);
    wrapper.addEventListener('drop', handleDrop);
    wrapper.addEventListener('dragleave', handleDragLeave);

    return () => {
      wrapper.removeEventListener('dragover', handleDragOver);
      wrapper.removeEventListener('drop', handleDrop);
      wrapper.removeEventListener('dragleave', handleDragLeave);
    };
  }, [
    arrangeFile,
    timelineController.timelineAndTrackHeadersContainer,
    stateRef,
  ]);

  return {
    draggedFilePositionRef,
  };
}
