import { app, setCurrentTime, setDuration, setFps, setIsPlaying, snapToFrame } from '../store.svelte';

export type VideoController = {
  element: HTMLVideoElement;
  destroy: () => void;
  togglePlayPause: () => void;
  stepFrame: (delta: number) => void;
  seek: (time: number) => void;
  setPlaybackRate: (rate: number) => void;
};

export function attachVideoPlayer(video: HTMLVideoElement): VideoController {
  let rafId = 0;
  let lastFrameTime: number | null = null;
  const frameDeltas: number[] = [];

  const onLoadedMetadata = () => {
    setDuration(video.duration);
    setCurrentTime(video.currentTime);
  };

  const onPlay = () => setIsPlaying(true);
  const onPause = () => setIsPlaying(false);
  const onSeeked = () => setCurrentTime(video.currentTime);
  const onEnded = () => setIsPlaying(false);

  const detectFps = (mediaTime: number) => {
    if (lastFrameTime !== null) {
      const delta = mediaTime - lastFrameTime;
      if (delta > 0.001 && delta < 0.2) {
        frameDeltas.push(delta);
        if (frameDeltas.length > 30) frameDeltas.shift();
        if (frameDeltas.length >= 10) {
          const avg = frameDeltas.reduce((a, b) => a + b, 0) / frameDeltas.length;
          const detected = Math.round(1 / avg);
          if (detected >= 10 && detected <= 120) setFps(detected);
        }
      }
    }
    lastFrameTime = mediaTime;
  };

  const frameLoop = (_now: number, metadata: VideoFrameCallbackMetadata) => {
    setCurrentTime(metadata.mediaTime);
    detectFps(metadata.mediaTime);
    if (!video.paused && !video.ended) {
      video.requestVideoFrameCallback(frameLoop);
    }
  };

  const timeLoop = () => {
    if (!video.paused) {
      setCurrentTime(video.currentTime);
      rafId = requestAnimationFrame(timeLoop);
    }
  };

  const onPlayStart = () => {
    if ('requestVideoFrameCallback' in video) {
      video.requestVideoFrameCallback(frameLoop);
    } else {
      rafId = requestAnimationFrame(timeLoop);
    }
  };

  video.addEventListener('loadedmetadata', onLoadedMetadata);
  video.addEventListener('play', onPlay);
  video.addEventListener('play', onPlayStart);
  video.addEventListener('pause', onPause);
  video.addEventListener('seeked', onSeeked);
  video.addEventListener('ended', onEnded);

  if (video.readyState >= 1) onLoadedMetadata();

  video.preservesPitch = true;
  video.playbackRate = app.previewSpeed;

  return {
    element: video,
    destroy() {
      cancelAnimationFrame(rafId);
      video.removeEventListener('loadedmetadata', onLoadedMetadata);
      video.removeEventListener('play', onPlay);
      video.removeEventListener('play', onPlayStart);
      video.removeEventListener('pause', onPause);
      video.removeEventListener('seeked', onSeeked);
      video.removeEventListener('ended', onEnded);
    },
    togglePlayPause() {
      if (video.paused || video.ended) void video.play();
      else video.pause();
    },
    stepFrame(delta: number) {
      video.pause();
      const next = snapToFrame(
        Math.max(0, Math.min(video.duration || 0, video.currentTime + delta / app.fps)),
      );
      video.currentTime = next;
      setCurrentTime(next);
    },
    seek(time: number) {
      const clamped = Math.max(0, Math.min(video.duration || 0, time));
      video.currentTime = clamped;
      setCurrentTime(clamped);
    },
    setPlaybackRate(rate: number) {
      video.playbackRate = rate;
    },
  };
}
