// Browser-synced audio layer for the Huggie promo.
// The actual audio files are generated by scripts/generate-audio.mjs.

const DEFAULT_HUGGIE_AUDIO_MANIFEST = {
  duration: 60,
  music: {
    id: 'music-bed',
    label: 'ElevenLabs instrumental bed',
    file: 'assets/audio/huggie-music-bed.mp3',
    start: 0,
    duration: 60,
    volume: 0.42,
  },
  cues: [
    { id: 'scene-01-problem', label: 'Scene 1 problem', start: 0,  duration: 5,  volume: 0.55, file: 'assets/audio/scene-01-problem.wav' },
    { id: 'scene-02-incoming', label: 'Scene 2 incoming call', start: 5,  duration: 5,  volume: 0.52, file: 'assets/audio/scene-02-incoming.wav' },
    { id: 'scene-03-magic', label: 'Scene 3 translation magic', start: 10, duration: 15, volume: 0.42, file: 'assets/audio/scene-03-magic.wav' },
    { id: 'scene-04-human', label: 'Scene 4 human moment', start: 25, duration: 13, volume: 0.44, file: 'assets/audio/scene-04-human.wav' },
    { id: 'scene-05-connection', label: 'Scene 5 connection', start: 38, duration: 12, volume: 0.45, file: 'assets/audio/scene-05-connection.wav' },
    { id: 'scene-06-logo', label: 'Scene 6 logo reveal', start: 50, duration: 10, volume: 0.52, file: 'assets/audio/scene-06-logo.wav' },
  ],
};

function HuggieAudioController({ manifest = window.HUGGIE_AUDIO_MANIFEST || DEFAULT_HUGGIE_AUDIO_MANIFEST }) {
  const { time, actualTime = time, playing } = useTimeline();
  const musicRef = React.useRef(null);
  const cueRefs = React.useRef({});
  const mutedRef = React.useRef(true);
  const latestSyncRef = React.useRef(() => {});
  const [blocked, setBlocked] = React.useState(false);
  const [muted, setMuted] = React.useState(true);

  // Sync at a gentle rate. Native audio playback carries the in-between frames.
  const audioTime = actualTime;
  const syncTick = Math.round(audioTime * 4) / 4;

  React.useEffect(() => {
    mutedRef.current = muted;
  }, [muted]);

  const seekElement = React.useCallback((el, safeTime) => {
    if (!el) return false;

    const nextTime = Math.max(0, safeTime || 0);
    el.dataset.huggieTargetTime = String(nextTime);

    if (el.readyState < 1) {
      el.preload = 'auto';
      if (el.networkState === HTMLMediaElement.NETWORK_EMPTY && el.dataset.huggieLoadRequested !== 'true') {
        el.dataset.huggieLoadRequested = 'true';
        try { el.load(); } catch {}
      }
      return false;
    }

    try {
      if (Number.isFinite(nextTime) && Math.abs((el.currentTime || 0) - nextTime) > 0.18) {
        el.currentTime = nextTime;
      }
      return true;
    } catch {
      return false;
    }
  }, []);

  const syncElement = React.useCallback((el, targetTime, shouldPlay, volume) => {
    if (!el) return;

    const safeTime = Math.max(0, targetTime || 0);
    const isMuted = mutedRef.current;
    const masterVolume = isMuted ? 0 : 1;
    el.volume = clamp(volume * masterVolume, 0, 1);

    const seeked = seekElement(el, safeTime);

    if (!shouldPlay || isMuted) {
      el.pause();
      return;
    }

    if (el.paused) {
      const attempt = el.play();
      if (attempt && typeof attempt.catch === 'function') {
        attempt
          .then(() => {
            const pendingTime = parseFloat(el.dataset.huggieTargetTime || '');
            if (Number.isFinite(pendingTime)) seekElement(el, pendingTime);
          })
          .catch(() => setBlocked(true));
      }
    } else if (!seeked && el.readyState < 1 && el.networkState === HTMLMediaElement.NETWORK_EMPTY && el.dataset.huggieLoadRequested !== 'true') {
      el.dataset.huggieLoadRequested = 'true';
      try { el.load(); } catch {}
    }
  }, [seekElement]);

  const syncAll = React.useCallback(() => {
    const speechActive = (manifest.cues || []).some(cue => {
      const cueStart = cue.start || 0;
      return cue.type === 'speech' && audioTime >= cueStart && audioTime < cueStart + (cue.duration || 0);
    });
    const music = manifest.music;
    if (music) {
      const localMusicTime = clamp(audioTime - (music.start || 0), 0, music.duration || manifest.duration || 60);
      const musicActive = audioTime >= (music.start || 0) && audioTime <= ((music.start || 0) + (music.duration || manifest.duration || 60));
      syncElement(musicRef.current, localMusicTime, playing && musicActive, (music.volume ?? 0.4) * (speechActive ? 0.5 : 1));
    }

    for (const cue of manifest.cues || []) {
      const el = cueRefs.current[cue.id];
      const cueStart = cue.start || 0;
      const cueEnd = cueStart + (cue.duration || 0);
      const active = audioTime >= cueStart && audioTime < cueEnd;
      if (!active) {
        if (el) {
          el.pause();
          try { el.currentTime = 0; } catch {}
        }
        continue;
      }
      syncElement(el, audioTime - cueStart, playing, cue.volume ?? 0.45);
    }
  }, [audioTime, manifest, playing, syncElement]);

  React.useEffect(() => {
    syncAll();
  }, [syncTick, playing, muted, syncAll]);

  React.useEffect(() => {
    latestSyncRef.current = syncAll;
  }, [syncAll]);

  const unlockAudio = () => {
    setBlocked(false);
    mutedRef.current = false;
    setMuted(false);
    syncAll();
  };

  const toggleSound = () => {
    const nextMuted = !mutedRef.current;
    mutedRef.current = nextMuted;
    setMuted(nextMuted);
    setBlocked(false);
    requestAnimationFrame(() => latestSyncRef.current());
  };

  const handleAudioReady = React.useCallback((event) => {
    const el = event.currentTarget;
    const pendingTime = parseFloat(el.dataset.huggieTargetTime || '');
    if (Number.isFinite(pendingTime)) seekElement(el, pendingTime);
    latestSyncRef.current();
  }, [seekElement]);

  const controls = (
    <div style={{
      position: 'fixed', right: 18, bottom: 10, zIndex: 20,
      display: 'flex', gap: 8, alignItems: 'center',
      fontFamily: fontMono,
    }}>
      {blocked && (
        <button
          onClick={unlockAudio}
          title="Enable synced promo audio"
          style={audioButtonStyle(true)}
        >
          enable audio
        </button>
      )}
      <button
        onClick={toggleSound}
        title={muted ? 'Start promo sound' : 'Stop promo sound'}
        style={audioButtonStyle(false)}
      >
        {muted ? 'start sound' : 'stop sound'}
      </button>
    </div>
  );

  return (
    <>
      {manifest.music && (
        <audio
          ref={musicRef}
          src={manifest.music.file}
          preload="auto"
          onLoadedMetadata={handleAudioReady}
          onCanPlay={handleAudioReady}
          data-huggie-audio={manifest.music.id}
        />
      )}
      {(manifest.cues || []).map(cue => (
        <audio
          key={cue.id}
          ref={el => {
            if (el) cueRefs.current[cue.id] = el;
            else delete cueRefs.current[cue.id];
          }}
          src={cue.file}
          preload="auto"
          onLoadedMetadata={handleAudioReady}
          onCanPlay={handleAudioReady}
          data-huggie-audio={cue.id}
        />
      ))}
      {ReactDOM.createPortal(controls, document.body)}
    </>
  );
}

function audioButtonStyle(primary) {
  return {
    border: primary ? `1px solid ${V.accent}` : '1px solid rgba(255,255,255,0.14)',
    background: primary ? V.accent : 'rgba(20,20,20,0.82)',
    color: '#fff',
    borderRadius: 6,
    padding: '7px 10px',
    fontSize: 11,
    letterSpacing: 1.2,
    textTransform: 'uppercase',
    cursor: 'pointer',
    boxShadow: '0 6px 18px rgba(0,0,0,0.24)',
  };
}

Object.assign(window, {
  DEFAULT_HUGGIE_AUDIO_MANIFEST,
  HuggieAudioController,
});
