/* global React */
const { useState: useStateMP, useEffect: useEffectMP, useRef: useRefMP } = React;

// Audio source: in the standalone bundle window.__resources.trackAudio is an
// inlined blob URL; otherwise fall back to the on-disk file. The bundle runtime
// populates window.__resources asynchronously, so resolve lazily at play time.
function resolveAudioSrc() {
  if (typeof window !== 'undefined' && window.__resources && window.__resources.trackAudio) {
    return window.__resources.trackAudio;
  }
  return 'music/track-loop.mp3'; // dev / on-disk fallback
}

function MusicPlayer() {
  const [muted, setMuted] = useStateMP(true);
  const [playing, setPlaying] = useStateMP(false);
  const [pulse, setPulse] = useStateMP(true);   // gentle pulse until the user interacts
  const [hint, setHint] = useStateMP(true);     // one-time "tap for music" invitation
  const audioRef = useRefMP(null);
  const startedRef = useRefMP(false);

  // Browsers will NOT *start* unmuted audio before a real user gesture, and —
  // crucially — scroll & wheel do NOT count as audio-activation gestures, so a
  // cold play() fired from a scroll handler gets rejected (that's why before it
  // only worked when you actually clicked the hero).
  //
  // The reliable fix: start the track MUTED right away. Muted autoplay is
  // allowed on every browser (desktop + iOS/Android), so the element is really
  // playing/looping silently. Once it is already playing we can flip it audible
  // by just clearing `muted` — and unmuting an already-playing element does NOT
  // require a fresh activation gesture, so it works on scroll/wheel too. On the
  // rare mobile browser that blocks even muted autoplay, the element is still
  // paused, so the first genuine tap calls play() inside that gesture instead.
  useEffectMP(() => {
    const audio = audioRef.current;
    if (!audio) return;

    audio.volume = 0.45;
    audio.loop = true;
    audio.muted = true;
    audio.setAttribute('playsinline', '');

    // Attach a source up-front so the element can begin buffering immediately.
    if (!audio.src || audio.src === window.location.href) {
      audio.src = resolveAudioSrc();
      audio.load();
    }

    const syncPlay = () => setPlaying(true);
    const syncPause = () => setPlaying(false);
    audio.addEventListener('play', syncPlay);
    audio.addEventListener('pause', syncPause);

    // (1) Kick off muted playback now. Retry on canplay in case the source
    //     wasn't ready on the first attempt.
    function primeMuted() {
      const a = audioRef.current;
      if (!a) return;
      a.muted = true;
      const p = a.play();
      if (p && p.catch) p.catch(() => {});
    }
    primeMuted();
    audio.addEventListener('canplay', primeMuted, { once: true });

    // (2) Turn the sound ON. If muted-autoplay already has us playing we simply
    //     unmute (works from scroll/wheel). If still paused, call play() — this
    //     path only runs from a real gesture handler, so audible play is allowed.
    function start() {
      const a = audioRef.current;
      if (!a) return;
      if (!a.src || a.src === window.location.href) {
        a.src = resolveAudioSrc();
        a.load();
      }
      a.muted = false;
      a.volume = 0.45;
      if (a.paused) {
        const p = a.play();
        if (p && p.catch) p.catch(() => {
          // last-ditch retry, still in-gesture
          try { a.load(); a.play().catch(() => {}); } catch (e) {}
        });
      }
      // NB: we do NOT dismiss the gate here. The UI only flips to "on" once we
      // CONFIRM real audible playback (see confirmAudible) — so a desktop scroll
      // that the browser quietly refuses keeps the prompt visible instead of
      // hiding it and leaving the guest in silence.
    }
    // expose so the gate (and anyone else) can trigger playback
    window.__playMusic = start;

    // Single source of truth for "the music is actually audible now." Fires
    // from real playback (timeupdate only advances when not paused). Once the
    // element is unmuted AND playing, we light up the UI, drop the gate, and
    // stop listening for gestures.
    function confirmAudible() {
      const a = audioRef.current;
      if (!a || a.muted || a.paused) return;
      setMuted(false);
      setPulse(false);
      setHint(false);
      if (!startedRef.current) {
        startedRef.current = true;
        window.dispatchEvent(new Event('wedding-music-started'));
      }
      detach();
      a.removeEventListener('timeupdate', confirmAudible);
    }
    audio.addEventListener('timeupdate', confirmAudible);

    // Catch interaction anywhere on the page. We listen broadly — taps, clicks,
    // keys, and scroll/wheel all attempt to light up the sound. Listeners stay
    // attached (start is idempotent) until confirmAudible tears them down, so a
    // gesture that fails to start audio doesn't burn our only chance.
    const gestureEvents = ['pointerdown', 'pointerup', 'mousedown', 'touchstart', 'touchend', 'click', 'keydown', 'scroll', 'wheel'];
    // Events the browser treats as a real activation gesture: these are
    // GUARANTEED to grant audible playback, so we drop the gate immediately
    // Events the browser treats as a real activation gesture: these are
    // GUARANTEED to grant audible playback, so we drop the gate immediately
    // (waiting on buffering would make the prompt feel dead and invite repeat
    // taps). We deliberately EXCLUDE pointerdown/touchstart: those also fire at
    // the START of a swipe-scroll, and a swipe is not a tap — dismissing then
    // would put the original mobile bug right back. scroll & wheel are likewise
    // not guaranteed; for those the gate stays until confirmAudible verifies it.
    const activationEvents = ['pointerup', 'mousedown', 'touchend', 'click', 'keydown'];
    function onFirstGesture(e) {
      start();
      if (e && activationEvents.indexOf(e.type) !== -1) {
        setMuted(false);
        setPulse(false);
        setHint(false);
      }
    }
    function attach() {
      gestureEvents.forEach(ev => {
        window.addEventListener(ev, onFirstGesture, { capture: true, passive: true });
        document.addEventListener(ev, onFirstGesture, { capture: true, passive: true });
        // the scroller is a nested element — its scroll doesn't bubble to window
      });
      const scroller = document.querySelector('.snap-scroll');
      if (scroller) scroller.addEventListener('scroll', onFirstGesture, { capture: true, passive: true });
    }
    function detach() {
      gestureEvents.forEach(ev => {
        window.removeEventListener(ev, onFirstGesture, { capture: true });
        document.removeEventListener(ev, onFirstGesture, { capture: true });
      });
      const scroller = document.querySelector('.snap-scroll');
      if (scroller) scroller.removeEventListener('scroll', onFirstGesture, { capture: true });
    }
    attach();
    // .snap-scroll can mount a tick after us — re-bind its scroll hook shortly.
    const scrollerHook = setTimeout(attach, 0);

    return () => {
      clearTimeout(scrollerHook);
      audio.removeEventListener('play', syncPlay);
      audio.removeEventListener('pause', syncPause);
      audio.removeEventListener('canplay', primeMuted);
      audio.removeEventListener('timeupdate', confirmAudible);
      detach();
      if (window.__playMusic === start) delete window.__playMusic;
    };
  }, []);

  // The visible pill: play / pause toggle once things are running.
  function toggle() {
    const audio = audioRef.current;
    if (!audio) return;
    setPulse(false);
    setHint(false);
    if (audio.paused || audio.muted) {
      window.__playMusic && window.__playMusic();
    } else {
      audio.pause();
      setPlaying(false);
    }
  }

  const isOn = playing && !muted;
  return (
    <>
      {hint && (
        <div className="music-gate" role="status" aria-live="polite">
          <span className="music-gate-pill">
            <span className="music-hint-note" aria-hidden="true">♪</span>
            <span>Tap anywhere to play music</span>
          </span>
        </div>
      )}
      <div className={`music-pill ${pulse ? 'pulse' : ''}`} onClick={toggle}
           role="button" aria-label={isOn ? 'Pause music' : 'Play music'} tabIndex={0}
           onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggle(); } }}>
        <audio ref={audioRef} loop preload="auto" playsInline />
        <span className="music-eq" aria-hidden="true">
          <span className={isOn ? 'on' : ''} />
          <span className={isOn ? 'on' : ''} />
          <span className={isOn ? 'on' : ''} />
          <span className={isOn ? 'on' : ''} />
        </span>
        <span className="music-label">{isOn ? 'Music on' : 'Music off'}</span>
      </div>
    </>
  );
}

window.MusicPlayer = MusicPlayer;
