// audio-controller.jsx
// Syncs audio tracks (VO, music bed, SFX) to the Stage timeline.
// Usage:
//   <AudioTrack src="..." start={0} volume={1} muted={false} />
//   <SFXTrigger src="..." at={2.3} volume={0.9} muted={false} />

// Hook: owns a single <audio> element per component instance. Recreates if src changes.
function useAudioElement(src, { volume = 1, loop = false, muted = false } = {}) {
  const ref = React.useRef(null);

  // (Re)create on src change.
  React.useEffect(() => {
    const a = new Audio(src);
    a.preload = 'auto';
    a.loop = loop;
    a.volume = muted ? 0 : volume;
    ref.current = a;
    return () => {
      // Stop cleanly before discarding. Setting src='' + load() forces the media
      // pipeline to release the network/decoder resources synchronously so we
      // don't get ghost playback from an orphaned element.
      try {
        a.pause();
        a.removeAttribute('src');
        a.load();
      } catch {}
      if (ref.current === a) ref.current = null;
    };
  }, [src, loop]);

  // Live-update volume + mute.
  React.useEffect(() => {
    if (ref.current) ref.current.volume = muted ? 0 : volume;
  }, [volume, muted]);

  return ref.current;
}

// Plays a clip starting at `start` seconds on the timeline. Handles seek, pause,
// and "catches up" if the playhead jumps. Mute just silences — doesn't unmount.
function AudioTrack({ src, start = 0, volume = 1, loop = false, muted = false }) {
  const { time, playing } = useTimeline();
  const audio = useAudioElement(src, { volume, loop, muted });

  React.useEffect(() => {
    if (!audio) return;
    const local = time - start;
    if (local < 0) {
      // Before start — make sure audio is paused and rewound
      if (!audio.paused) audio.pause();
      if (audio.currentTime !== 0) audio.currentTime = 0;
      return;
    }
    // Sync currentTime if drift > 0.25s (handles scrubbing)
    if (Math.abs(audio.currentTime - local) > 0.25) {
      try { audio.currentTime = Math.min(local, audio.duration || local); } catch {}
    }
    if (playing && audio.paused) {
      audio.play().catch(() => {});
    } else if (!playing && !audio.paused) {
      audio.pause();
    }
  }, [time, playing, start, audio]);

  return null;
}

// Fires a one-shot sound when the timeline crosses `at`. Re-armable on scrub-back.
function SFXTrigger({ src, at, volume = 1, muted = false }) {
  const { time, playing } = useTimeline();
  const firedRef = React.useRef(false);
  const audioRef = React.useRef(null);

  // (Re)create audio when src changes. Cleanup is essential here to avoid ghosts.
  React.useEffect(() => {
    const a = new Audio(src);
    a.preload = 'auto';
    a.volume = muted ? 0 : volume;
    audioRef.current = a;
    return () => {
      try {
        a.pause();
        a.removeAttribute('src');
        a.load();
      } catch {}
      if (audioRef.current === a) audioRef.current = null;
    };
    // We intentionally exclude volume/muted — those are synced by the effect below.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [src]);

  // Live update volume / mute.
  React.useEffect(() => {
    if (audioRef.current) audioRef.current.volume = muted ? 0 : volume;
  }, [volume, muted]);

  React.useEffect(() => {
    const a = audioRef.current;
    if (!a) return;
    // Re-arm if user scrubs back before the trigger
    if (time < at - 0.1) {
      firedRef.current = false;
      if (!a.paused) { a.pause(); a.currentTime = 0; }
      return;
    }
    // Fire once
    if (playing && !firedRef.current && time >= at && time < at + 0.5) {
      firedRef.current = true;
      try { a.currentTime = 0; a.play().catch(() => {}); } catch {}
    }
    // If paused, don't let it keep playing
    if (!playing && !a.paused) { a.pause(); }
  }, [time, playing, at]);

  return null;
}

// Mute toggle UI (bottom-right badge on the stage).
function MuteToggle({ muted, onToggle }) {
  return (
    <button
      onClick={onToggle}
      title={muted ? 'Unmute' : 'Mute'}
      style={{
        position: 'absolute',
        right: 24, bottom: 24,
        zIndex: 10,
        width: 44, height: 44,
        background: 'rgba(20,20,20,0.85)',
        border: '1px solid rgba(255,255,255,0.15)',
        borderRadius: 22,
        color: '#f5f4f1',
        cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        backdropFilter: 'blur(8px)',
      }}
    >
      {muted ? (
        <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
          <path d="M3 6v6h3l4 3V3L6 6H3z" fill="currentColor"/>
          <line x1="12" y1="6" x2="17" y2="11" stroke="currentColor" strokeWidth="1.5"/>
          <line x1="17" y1="6" x2="12" y2="11" stroke="currentColor" strokeWidth="1.5"/>
        </svg>
      ) : (
        <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
          <path d="M3 6v6h3l4 3V3L6 6H3z" fill="currentColor"/>
          <path d="M12 6c1.5 1 1.5 5 0 6" stroke="currentColor" strokeWidth="1.5" fill="none"/>
          <path d="M14 4c3 2.5 3 7.5 0 10" stroke="currentColor" strokeWidth="1.5" fill="none"/>
        </svg>
      )}
    </button>
  );
}

Object.assign(window, { AudioTrack, SFXTrigger, MuteToggle });
