import { MutableRefObject, useCallback, useEffect, useRef } from 'react';

import useClickDrag from '@/hooks/useClickDrag';
import { ExtendLeftIcon, ExtendRightIcon } from '@/icons';

// This component could pull some of these values from UploadStateContext,
// but if we pass them as props it opens the door to reuse down the line.
const DraggableTrimRegion = ({
  audioBufferDuration,
  trimRangePreviewRef,
  trimRange,
  setTrimRange,
  onSetEndTime,
  maxSeconds,
}: {
  audioBufferDuration: number;
  trimRangePreviewRef: MutableRefObject<[number, number]>;
  trimRange: [number, number];
  setTrimRange: (trimRange: [number, number]) => void;
  onSetEndTime?: (endTime: number) => void;
  maxSeconds: number;
}) => {
  const trimWrapperRef = useRef<HTMLDivElement>(null);

  const updateTrimPreview = useCallback(
    (
      trimStart: number = trimRangePreviewRef.current[0],
      trimEnd: number = trimRangePreviewRef.current[1]
    ) => {
      const trimWrapper = trimWrapperRef.current;
      if (!trimWrapper) return;

      trimWrapper.style.setProperty(
        'left',
        `${(trimStart / audioBufferDuration) * 100}%`
      );
      trimWrapper.style.setProperty(
        'width',
        `${((trimEnd - trimStart) / audioBufferDuration) * 100}%`
      );
      trimRangePreviewRef.current = [trimStart, trimEnd];
      onSetEndTime?.(trimEnd);
    },
    [onSetEndTime, audioBufferDuration, trimRangePreviewRef]
  );

  useEffect(() => {
    updateTrimPreview(trimRange[0], trimRange[1]);
  }, [trimRange, updateTrimPreview]);

  const commitTrimPreview = useCallback(() => {
    setTrimRange(trimRangePreviewRef.current);
  }, [setTrimRange, trimRangePreviewRef]);

  // sets trimRangePreviewRef.current, relative to trimRange.
  // e.g. trimRange = [1, 3]; setTrimRangePreviewRelative(-1, 1); trimRangePreviewRef.current === [0, 4];
  // this is used on each mousemove, because trimRange does not change during a click-drag (only when the mouse is released)
  const setTrimRangePreviewRelative = useCallback(
    (startDeltaFromSaved: number, endDeltaFromSaved: number) => {
      const [start, end] = trimRange;

      if (
        end + endDeltaFromSaved - (start + startDeltaFromSaved) >
        maxSeconds + 0.0000001
      ) {
        return;
      }

      if (end + endDeltaFromSaved < start + startDeltaFromSaved) {
        return;
      }

      if (start + startDeltaFromSaved > end + endDeltaFromSaved) {
        return;
      }

      const newStart = Math.max(
        0,
        end + endDeltaFromSaved - maxSeconds,
        Math.min(
          end + endDeltaFromSaved,
          start + startDeltaFromSaved,
          audioBufferDuration
        )
      );
      const newEnd = Math.min(
        audioBufferDuration,
        newStart + maxSeconds,
        Math.max(newStart, end + endDeltaFromSaved)
      );
      trimRangePreviewRef.current = [newStart, newEnd];
      updateTrimPreview();
    },
    [audioBufferDuration, trimRange, trimRangePreviewRef, updateTrimPreview]
  );

  const getSecondsFromPx = useCallback(
    (px: number) => {
      const parent = trimWrapperRef.current?.parentElement;
      if (!parent) return 0;
      const width = parent.getBoundingClientRect().width;
      return (px / width) * audioBufferDuration;
    },
    [audioBufferDuration]
  );

  const receiveTrimRangeDragTarget = useClickDrag(
    useCallback(
      () => ({
        onMouseMove: ({ deltaXFromStart }) => {
          const deltaSeconds = getSecondsFromPx(deltaXFromStart);
          console.log(deltaSeconds);
          setTrimRangePreviewRelative(deltaSeconds, deltaSeconds);
        },
        onMouseUp: commitTrimPreview,
      }),
      [commitTrimPreview, getSecondsFromPx, setTrimRangePreviewRelative]
    )
  );

  const receiveTrimStartDragTarget = useClickDrag(
    useCallback(
      () => ({
        onMouseMove: ({ deltaXFromStart }) =>
          setTrimRangePreviewRelative(getSecondsFromPx(deltaXFromStart), 0),
        onMouseUp: commitTrimPreview,
      }),
      [commitTrimPreview, setTrimRangePreviewRelative, getSecondsFromPx]
    )
  );

  const receiveTrimEndDragTarget = useClickDrag(
    useCallback(
      () => ({
        onMouseMove: ({ deltaXFromStart }) =>
          setTrimRangePreviewRelative(0, getSecondsFromPx(deltaXFromStart)),
        onMouseUp: commitTrimPreview,
      }),
      [commitTrimPreview, setTrimRangePreviewRelative, getSecondsFromPx]
    )
  );

  return (
    <div
      className='pointer-events-none absolute top-0 bottom-0 rounded-md border-4 border-x-24 border-y-0 border-accent-brand bg-accent-brand/20'
      ref={trimWrapperRef}
    >
      <div
        ref={receiveTrimRangeDragTarget}
        className='pointer-events-auto absolute right-0 left-0 flex h-5 cursor-grab items-center justify-center bg-transparent/30 text-foreground-primary active:cursor-grabbing'
      >
        {/* <DragHorizontalIcon /> */}
        <div className='h-1 w-8 rounded-full bg-accent-brand'></div>
      </div>

      <div className='pointer-events-auto absolute left-0 h-full w-px bg-transparent'>
        <div
          ref={receiveTrimStartDragTarget}
          className='absolute left-[-24px] h-full w-[24px] cursor-ew-resize'
        />
        <ExtendLeftIcon className='pointer-events-none absolute top-[calc(50%-12px)] left-[-20px] h-4 w-4' />
      </div>

      <div className='pointer-events-auto absolute right-0 h-full w-px bg-transparent'>
        <div
          ref={receiveTrimEndDragTarget}
          className='absolute left-0 h-full w-[24px] cursor-ew-resize'
        />
        <ExtendRightIcon className='pointer-events-none absolute top-[calc(50%-12px)] left-1 h-4 w-4' />
      </div>
    </div>
  );
};

export default DraggableTrimRegion;
