import { useCallback, useState, type RefObject } from "react";
import { TIMELINE_ASSET_MIME, TIMELINE_BLOCK_MIME } from "../../utils/timelineAssetDrop";
import { TRACK_H, resolveTimelineAssetDrop } from "./timelineLayout";
import type { TimelineDropCallbacks } from "./timelineCallbacks";
import type { StackingTimelineLayer, TimelineLayerId } from "./timelineTrackOrder";

interface UseTimelineAssetDropOptions extends TimelineDropCallbacks {
  scrollRef: RefObject<HTMLDivElement | null>;
  ppsRef: RefObject<number>;
  durationRef: RefObject<number>;
  trackOrderRef: RefObject<TimelineLayerId[]>;
  timelineLayersRef: RefObject<StackingTimelineLayer[]>;
}

export function useTimelineAssetDrop({
  scrollRef,
  ppsRef,
  durationRef,
  trackOrderRef,
  timelineLayersRef,
  onFileDrop,
  onAssetDrop,
  onBlockDrop,
}: UseTimelineAssetDropOptions) {
  const [isDragOver, setIsDragOver] = useState(false);

  const handleAssetDragOver = useCallback((e: React.DragEvent) => {
    const hasFiles = e.dataTransfer.files.length > 0;
    const types = Array.from(e.dataTransfer.types);
    const hasAsset = types.includes(TIMELINE_ASSET_MIME);
    const hasBlock = types.includes(TIMELINE_BLOCK_MIME);
    if (!hasFiles && !hasAsset && !hasBlock) return;
    e.preventDefault();
    if (hasAsset || hasBlock) e.dataTransfer.dropEffect = "copy";
    setIsDragOver(true);
  }, []);

  const handleAssetDrop = useCallback(
    // fallow-ignore-next-line complexity
    (e: React.DragEvent) => {
      e.preventDefault();
      setIsDragOver(false);
      const scroll = scrollRef.current;
      const rect = scroll?.getBoundingClientRect();
      const layerById = new Map(timelineLayersRef.current.map((layer) => [layer.id, layer]));
      const trackOrder = trackOrderRef.current
        .map((id) => layerById.get(id)?.placementTrack)
        .filter((track): track is number => track != null);
      const dropInput = {
        rectLeft: rect?.left ?? 0,
        rectTop: rect?.top ?? 0,
        scrollLeft: scroll?.scrollLeft ?? 0,
        scrollTop: scroll?.scrollTop ?? 0,
        pixelsPerSecond: ppsRef.current,
        duration: durationRef.current,
        trackHeight: TRACK_H,
        trackOrder,
      };
      if (onFileDrop && e.dataTransfer.files.length > 0) {
        void onFileDrop(
          Array.from(e.dataTransfer.files),
          scroll && rect ? resolveTimelineAssetDrop(dropInput, e.clientX, e.clientY) : undefined,
        );
        return;
      }
      const assetPayload = e.dataTransfer.getData(TIMELINE_ASSET_MIME);
      if (assetPayload && onAssetDrop && scroll && rect) {
        try {
          const parsed = JSON.parse(assetPayload) as { path?: string };
          if (parsed.path)
            void onAssetDrop(
              parsed.path,
              resolveTimelineAssetDrop(dropInput, e.clientX, e.clientY),
            );
        } catch {
          /* ignore malformed drag payloads */
        }
        return;
      }
      const blockPayload = e.dataTransfer.getData(TIMELINE_BLOCK_MIME);
      if (blockPayload && onBlockDrop && scroll && rect) {
        try {
          const parsed = JSON.parse(blockPayload) as { name?: string };
          if (parsed.name)
            void onBlockDrop(
              parsed.name,
              resolveTimelineAssetDrop(dropInput, e.clientX, e.clientY),
            );
        } catch {
          /* ignore malformed drag payloads */
        }
      }
    },
    [
      onAssetDrop,
      onBlockDrop,
      onFileDrop,
      scrollRef,
      ppsRef,
      durationRef,
      trackOrderRef,
      timelineLayersRef,
    ],
  );

  return { isDragOver, setIsDragOver, handleAssetDragOver, handleAssetDrop };
}
