/**
 * Audio — Drop-in replacement for Remotion's <Audio>.
 *
 * An <audio> element that syncs to the player's current frame.
 * Supports volume as a per-frame callback function.
 *
 * Key design: During playback, let the browser handle audio naturally
 * and only seek when drift exceeds a threshold. This prevents audio glitching.
 */
import React, { useRef, useEffect } from 'react';
import { useCurrentFrame } from '../player/useCurrentFrame';
import { useVideoConfig } from '../player/useVideoConfig';
import { usePlayerState } from '../player/PlayerContext';

interface AudioProps {
  src: string;
  volume?: number | ((frame: number) => number);
  playbackRate?: number;
  startFrom?: number; // frame
  endAt?: number; // frame
}

/** Maximum drift (in seconds) before we force a seek during playback */
const DRIFT_THRESHOLD_PLAYING = 0.25;
/** Maximum drift when paused/seeking */
const DRIFT_THRESHOLD_PAUSED = 0.05;

export const Audio: React.FC<AudioProps> = ({
  src,
  volume: volumeProp,
  playbackRate = 1,
  startFrom = 0,
}) => {
  const volume: number | ((frame: number) => number) = volumeProp ?? 1;
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const { playing } = usePlayerState();
  const audioRef = useRef<HTMLAudioElement>(null);
  const wasPlayingRef = useRef(false);

  // Compute the audio's local time
  const audioFrame = frame + startFrom;
  const targetTime = audioFrame / fps;

  // Sync time — different strategies for playing vs paused
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;

    const drift = Math.abs(audio.currentTime - targetTime);

    if (playing) {
      // During playback: only seek if drift is significant
      if (drift > DRIFT_THRESHOLD_PLAYING) {
        audio.currentTime = targetTime;
      }
    } else {
      // When paused: sync precisely
      if (drift > DRIFT_THRESHOLD_PAUSED) {
        audio.currentTime = targetTime;
      }
    }
  }, [targetTime, playing]);

  // Sync play/pause state
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;

    if (playing) {
      // When starting playback, sync time first then play
      if (!wasPlayingRef.current) {
        audio.currentTime = targetTime;
      }
      audio.play().catch(() => {});
    } else {
      audio.pause();
      audio.currentTime = targetTime;
    }

    wasPlayingRef.current = playing;
  }, [playing]);

  // Set playback rate
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;
    audio.playbackRate = playbackRate;
  }, [playbackRate]);

  // Set volume per frame
  useEffect(() => {
    const audio = audioRef.current;
    if (!audio) return;
    const vol = typeof volume === 'function' ? volume(frame) : volume;
    audio.volume = Math.max(0, Math.min(1, vol));
  }, [volume, frame]);

  return (
    <audio
      ref={audioRef}
      src={src}
      preload="auto"
      style={{ display: 'none' }}
    />
  );
};
