export function createTimelinePointerController(options) {
  const {
    waveCanvas,
    rulerCanvas,
    waveContainer,
    timelineViewport,
    headerWidth = 0,
    windowTarget = window,
    getVisibleStart,
    getPixelsPerSecond,
    getDuration,
    isLoopEnabled,
    getLoopStart,
    getLoopEnd,
    setLoopStart,
    setLoopEnd,
    onSeek,
    onLoopChange,
  } = options;
  let isScrubbing = false;
  let draggingLoopMarker = null;

  function timelineOriginLeft() {
    if (timelineViewport) {
      return timelineViewport.getBoundingClientRect().left + headerWidth;
    }
    return rulerCanvas.getBoundingClientRect().left;
  }

  function timeFromEvent(event) {
    // The ruler and legacy waveform both move with horizontally scrolled
    // content. Measure from the viewport's fixed track-content edge instead;
    // otherwise adding getVisibleStart() counts the scroll offset twice.
    const x = event.clientX - timelineOriginLeft();
    return Math.max(0, Math.min(
      getDuration(),
      getVisibleStart() + x / getPixelsPerSecond()
    ));
  }

  function loopMarkerAt(event) {
    if (!isLoopEnabled()) return null;
    const rect = rulerCanvas.getBoundingClientRect();
    const x = event.clientX - timelineOriginLeft();
    if (event.clientY - rect.top > 14) return null;
    const start = getVisibleStart();
    const pixelsPerSecond = getPixelsPerSecond();
    if (Math.abs(x - (getLoopStart() - start) * pixelsPerSecond) < 12) return 'start';
    if (Math.abs(x - (getLoopEnd() - start) * pixelsPerSecond) < 12) return 'end';
    return null;
  }

  function handleMouseDown(event) {
    if (event.target === rulerCanvas) {
      const marker = loopMarkerAt(event);
      if (marker) {
        draggingLoopMarker = marker;
        event.preventDefault();
        return;
      }
    }
    isScrubbing = true;
    onSeek(timeFromEvent(event));
    event.preventDefault();
  }

  function handleMouseMove(event) {
    if (draggingLoopMarker) {
      const time = timeFromEvent(event);
      if (draggingLoopMarker === 'start') {
        setLoopStart(Math.min(time, getLoopEnd() - 0.5));
      } else {
        setLoopEnd(Math.max(time, getLoopStart() + 0.5));
      }
      onLoopChange();
      return;
    }
    if (isScrubbing) onSeek(timeFromEvent(event));
  }

  function handleMouseUp() {
    isScrubbing = false;
    draggingLoopMarker = null;
  }

  function handleRulerHover(event) {
    if (draggingLoopMarker || isScrubbing) return;
    rulerCanvas.style.cursor = loopMarkerAt(event) ? 'ew-resize' : 'pointer';
  }

  waveCanvas.addEventListener('mousedown', handleMouseDown);
  rulerCanvas.addEventListener('mousedown', handleMouseDown);
  waveContainer?.addEventListener('mousedown', handleMouseDown);
  windowTarget.addEventListener('mousemove', handleMouseMove);
  windowTarget.addEventListener('mouseup', handleMouseUp);
  rulerCanvas.addEventListener('mousemove', handleRulerHover);

  return {
    destroy() {
      waveCanvas.removeEventListener('mousedown', handleMouseDown);
      rulerCanvas.removeEventListener('mousedown', handleMouseDown);
      waveContainer?.removeEventListener('mousedown', handleMouseDown);
      windowTarget.removeEventListener('mousemove', handleMouseMove);
      windowTarget.removeEventListener('mouseup', handleMouseUp);
      rulerCanvas.removeEventListener('mousemove', handleRulerHover);
    },
    getState: () => ({ isScrubbing, draggingLoopMarker }),
  };
}
