import { snapWithEvent } from '@/utils/snap';

import { StudioContextType } from '../StudioContext';
import toggleTimelineLoop from '../actions/toggleTimelineLoop';
import { inFlightDragKeys } from '../hooks/useInFlightDrags';
import { RULER_HEIGHT } from './makeStudioBeatGridRegion';
import { SCROLLBAR_HEIGHT } from './makeStudioScrollbarRegions';

const EDGE_TOUCH_TARGET_WIDTH = 10;

export function getEffectiveTimelineLoop(studioContext: StudioContextType) {
  const loop = studioContext.state.loop;

  const startDelta =
    studioContext.inFlightDrags.get(
      inFlightDragKeys.timelineLoopEdge('start')
    ) || 0;

  const endDelta =
    studioContext.inFlightDrags.get(inFlightDragKeys.timelineLoopEdge('end')) ||
    0;

  return {
    ...loop,
    startBeats: loop.startBeats + startDelta,
    endBeats: loop.endBeats + endDelta,
  };
}

const makeStudioTimelineLoopRegions = (studioContext: StudioContextType) => {
  const { loop } = studioContext.state;
  const wrapperRect = studioContext.timelineController.getWrapperRect();
  const effectiveLoop = getEffectiveTimelineLoop(studioContext);
  const startX = studioContext.beatsToCanvasX(effectiveLoop.startBeats);
  const endX = studioContext.beatsToCanvasX(effectiveLoop.endBeats);

  if (!loop.enabled) return [];

  return [
    {
      renderTop: (ctx: CanvasRenderingContext2D) => {
        // Draw semi-transparent overlay for loop region
        ctx.fillStyle = 'rgba(253, 66, 156, 0.1)';
        ctx.fillRect(
          startX,
          0,
          endX - startX,
          wrapperRect.height - SCROLLBAR_HEIGHT
        );

        // Draw loop boundaries
        ctx.strokeStyle = 'rgba(253, 66, 156, 0.3)';
        ctx.lineJoin = 'round';
        ctx.lineCap = 'round';
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.moveTo(startX, 0);
        ctx.lineTo(startX, wrapperRect.height - SCROLLBAR_HEIGHT);
        ctx.moveTo(endX, 0);
        ctx.lineTo(endX, wrapperRect.height - SCROLLBAR_HEIGHT);
        ctx.stroke();

        ctx.beginPath();
        ctx.moveTo(endX, 1);
        ctx.lineTo(endX - 11, 1);
        ctx.lineTo(endX - 11, 10);
        ctx.lineTo(endX, 10);
        ctx.lineTo(endX, RULER_HEIGHT);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
      },
    },
    {
      touchTarget: {
        bounds: {
          top: 0,
          left: startX - EDGE_TOUCH_TARGET_WIDTH,
          right: startX + EDGE_TOUCH_TARGET_WIDTH,
          bottom: RULER_HEIGHT,
        },
        hoverCursor: 'ew-resize',
        dragCursor: 'ew-resize',
        onMouseDown: studioContext.handleClickDrag(() => {
          const originalStartBeats = loop.startBeats;
          const originalEndBeats = loop.endBeats;

          return {
            onMouseMove: ({ moveBeats, moveEvent }) => {
              const delta =
                snapWithEvent(
                  moveEvent,
                  moveBeats,
                  studioContext.gridSizeRef.current,
                  null,
                  studioContext.getMaxShift()
                ) - originalStartBeats;
              const resultingStart = originalStartBeats + delta;
              // Only enforce minimum gap if not holding cmd/ctrl
              const minGap =
                moveEvent.ctrlKey || moveEvent.metaKey
                  ? 0
                  : Math.min(1, studioContext.gridSizeRef.current);
              const constrainedStart = Math.min(
                resultingStart,
                originalEndBeats - minGap
              );
              const trueDelta = constrainedStart - originalStartBeats;

              studioContext.inFlightDrags.update(
                inFlightDragKeys.timelineLoopEdge('start'),
                trueDelta
              );

              studioContext.playbackController.setLoopStart(constrainedStart);
            },
            onMouseUp: ({ upBeats, upEvent }) => {
              const delta =
                snapWithEvent(
                  upEvent,
                  upBeats,
                  studioContext.gridSizeRef.current,
                  null,
                  studioContext.getMaxShift()
                ) - originalStartBeats;
              const resultingStart = originalStartBeats + delta;
              // Only enforce minimum gap if not holding cmd/ctrl
              const minGap =
                upEvent.ctrlKey || upEvent.metaKey
                  ? 0
                  : Math.min(1, studioContext.gridSizeRef.current);
              const constrainedStart = Math.min(
                resultingStart,
                originalEndBeats - minGap
              );

              studioContext.setState(
                toggleTimelineLoop(constrainedStart, originalEndBeats, true)
              );
              studioContext.inFlightDrags.finish(
                inFlightDragKeys.timelineLoopEdge('start')
              );
            },
          };
        }),
      },
      renderTop: (ctx: CanvasRenderingContext2D, hovered: boolean) => {
        if (hovered) {
          ctx.fillStyle = '#FFA4D0';
          ctx.strokeStyle = '#FFA4D0';
        } else {
          ctx.fillStyle = '#FD429C';
          ctx.strokeStyle = '#FD429C';
        }

        ctx.beginPath();
        ctx.moveTo(startX, 1);
        ctx.lineTo(startX + 11, 1);
        ctx.lineTo(startX + 11, 10);
        ctx.lineTo(startX, 10);
        ctx.lineTo(startX, RULER_HEIGHT);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
      },
    },
    {
      touchTarget: {
        bounds: {
          top: 0,
          left: endX - EDGE_TOUCH_TARGET_WIDTH,
          right: endX + EDGE_TOUCH_TARGET_WIDTH,
          bottom: RULER_HEIGHT,
        },
        hoverCursor: 'ew-resize',
        dragCursor: 'ew-resize',
        onMouseDown: studioContext.handleClickDrag(() => {
          const originalStartBeats = loop.startBeats;
          const originalEndBeats = loop.endBeats;

          return {
            onMouseMove: ({ moveBeats, moveEvent }) => {
              const delta =
                snapWithEvent(
                  moveEvent,
                  moveBeats,
                  studioContext.gridSizeRef.current,
                  null,
                  studioContext.getMaxShift()
                ) - originalEndBeats;
              const resultingEnd = originalEndBeats + delta;
              // Only enforce minimum gap if not holding cmd/ctrl
              const minGap =
                moveEvent.ctrlKey || moveEvent.metaKey
                  ? 0
                  : Math.min(1, studioContext.gridSizeRef.current);
              const constrainedEnd = Math.max(
                resultingEnd,
                effectiveLoop.startBeats + minGap
              );
              const trueDelta = constrainedEnd - originalEndBeats;

              studioContext.inFlightDrags.update(
                inFlightDragKeys.timelineLoopEdge('end'),
                trueDelta
              );

              studioContext.playbackController.setLoopEnd(constrainedEnd);
            },
            onMouseUp: ({ upBeats, upEvent }) => {
              const delta =
                snapWithEvent(
                  upEvent,
                  upBeats,
                  studioContext.gridSizeRef.current,
                  null,
                  studioContext.getMaxShift()
                ) - originalEndBeats;
              const resultingEnd = originalEndBeats + delta;
              // Only enforce minimum gap if not holding cmd/ctrl
              const minGap =
                upEvent.ctrlKey || upEvent.metaKey
                  ? 0
                  : Math.min(1, studioContext.gridSizeRef.current);
              const constrainedEnd = Math.max(
                resultingEnd,
                effectiveLoop.startBeats + minGap
              );

              studioContext.setState(
                toggleTimelineLoop(originalStartBeats, constrainedEnd, true)
              );
              studioContext.inFlightDrags.finish(
                inFlightDragKeys.timelineLoopEdge('end')
              );
            },
          };
        }),
      },
      renderTop: (ctx: CanvasRenderingContext2D, hovered: boolean) => {
        if (hovered) {
          ctx.fillStyle = '#FFA4D0';
          ctx.strokeStyle = '#FFA4D0';
        } else {
          ctx.fillStyle = '#FD429C';
          ctx.strokeStyle = '#FD429C';
        }

        ctx.beginPath();
        ctx.moveTo(endX, 1);
        ctx.lineTo(endX - 11, 1);
        ctx.lineTo(endX - 11, 10);
        ctx.lineTo(endX, 10);
        ctx.lineTo(endX, RULER_HEIGHT);
        ctx.closePath();
        ctx.stroke();
        ctx.fill();
      },
    },
  ];
};

export default makeStudioTimelineLoopRegions;
