// ReelPlayer — a ReelCrafter-style reel: one transport, a waveform scrubber,
// a horizontal volume slider, a description + genre-tag panel for the current
// track, and a playlist that auto-advances. Every interaction is reported to
// window.ListenTracker (see listen-tracker.js) so the Admin dashboard can show
// who listened, to what, and for how long.
//
// Props:
//   tracks      [{ title, subtitle, src, description, tags:[] }]
//   playerId    'quick' | 'full'   (tagged on every tracked event)
//   footer      optional React node rendered under the player (e.g. CTA link)
//
// Swap the demo arrays in ComposerPage for the composer's own files.

const reelFmt = (t) => {
  if (!Number.isFinite(t)) return "0:00";
  const m = Math.floor(t / 60);
  const s = Math.floor(t % 60);
  return `${m}:${String(s).padStart(2, "0")}`;
};

// Deterministic waveform shape seeded by the src, so bars are stable per track.
function reelWaveform(src, count) {
  const seed = String(src || "")
    .split("")
    .reduce((s, c) => ((s * 31) + c.charCodeAt(0)) >>> 0, 5381);
  const bars = [];
  for (let i = 0; i < count; i++) {
    const x = i / Math.max(1, count - 1);
    const w =
      Math.sin((i + seed) * 0.42) * 0.45 +
      Math.sin((i + seed * 7) * 0.13) * 0.35 +
      Math.cos((i + seed * 3) * 0.71) * 0.20;
    const envelope = Math.sin(x * Math.PI) ** 0.4;
    const h = (0.32 + Math.abs(w) * 0.68) * (0.55 + 0.45 * envelope);
    bars.push(Math.max(0.14, Math.min(1, h)));
  }
  return bars;
}

// ── Horizontal volume slider ────────────────────────────────────────────────
function VolumeSlider({ value, onChange }) {
  const ref = React.useRef(null);
  const muted = value === 0;

  const setFromEvent = (e) => {
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const clientX = "clientX" in e ? e.clientX : (e.touches?.[0]?.clientX ?? r.left);
    onChange(Math.max(0, Math.min(1, (clientX - r.left) / r.width)));
  };

  const onPointerDown = (e) => {
    e.preventDefault();
    setFromEvent(e);
    const move = (ev) => setFromEvent(ev);
    const up = () => {
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", up);
    };
    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", up);
  };

  return (
    <div className="reel-vol">
      <button
        type="button"
        className="reel-vol-icon"
        aria-label={muted ? "Unmute" : "Mute"}
        onClick={() => onChange(muted ? 0.8 : 0)}
      >
        <svg width="17" height="14" viewBox="0 0 17 14" aria-hidden="true">
          <path d="M0 4.5H3L7 1V13L3 9.5H0V4.5Z" fill="currentColor" />
          {!muted && (
            <>
              <path d="M9.5 4.2C10.6 5 10.6 9 9.5 9.8" stroke="currentColor" strokeWidth="1.4" fill="none" strokeLinecap="round" opacity={value > 0.05 ? 1 : 0.25} />
              <path d="M11.8 2.4C13.9 3.8 13.9 10.2 11.8 11.6" stroke="currentColor" strokeWidth="1.4" fill="none" strokeLinecap="round" opacity={value > 0.5 ? 1 : 0.25} />
            </>
          )}
          {muted && (
            <path d="M10.5 5L15 9M15 5L10.5 9" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
          )}
        </svg>
      </button>
      <div
        ref={ref}
        className="reel-vol-track"
        role="slider"
        aria-label="Volume"
        aria-valuemin={0}
        aria-valuemax={100}
        aria-valuenow={Math.round(value * 100)}
        tabIndex={0}
        onPointerDown={onPointerDown}
        onKeyDown={(e) => {
          if (e.key === "ArrowRight" || e.key === "ArrowUp") { e.preventDefault(); onChange(Math.min(1, value + 0.05)); }
          if (e.key === "ArrowLeft" || e.key === "ArrowDown") { e.preventDefault(); onChange(Math.max(0, value - 0.05)); }
        }}
      >
        <span className="reel-vol-fill" style={{ width: `${value * 100}%` }} />
        <span className="reel-vol-thumb" style={{ left: `${value * 100}%` }} />
      </div>
    </div>
  );
}

function ReelPlayer({ tracks, playerId, footer }) {
  const list = (tracks && tracks.length ? tracks : []).slice(0, 12);
  const audioRef = React.useRef(null);

  const [index, setIndex]             = React.useState(0);
  const [playing, setPlaying]         = React.useState(false);
  const [duration, setDuration]       = React.useState(0);
  const [currentTime, setCurrentTime] = React.useState(0);
  const [seeking, setSeeking]         = React.useState(false);
  const [volume, setVolume]           = React.useState(() => {
    const v = parseFloat(localStorage.getItem("reel-volume"));
    return Number.isFinite(v) ? v : 0.8;
  });
  const wantPlay = React.useRef(false);

  const cur = list[index] || list[0] || {};

  // ── Tracking bookkeeping ──────────────────────────────────────────────────
  const tracker = () => window.ListenTracker;
  // accumulate listened seconds per track between flushes
  const acc = React.useRef({ seconds: 0, maxPos: 0, lastTick: 0 });
  const meta = () => ({
    player: playerId,
    trackId: cur.id != null ? cur.id : index,
    trackTitle: cur.title,
    duration,
  });
  const flushProgress = (eventName) => {
    const a = acc.current;
    const pct = duration ? Math.min(100, (a.maxPos / duration) * 100) : 0;
    if (a.seconds > 0.4 || eventName) {
      tracker()?.event(eventName || "progress", {
        ...meta(),
        position: a.maxPos,
        seconds: a.seconds,
        pct,
      });
      a.seconds = 0;
    }
  };

  const bars = React.useMemo(() => reelWaveform(cur.src, 80), [cur.src]);

  React.useEffect(() => {
    if (audioRef.current) audioRef.current.volume = volume;
    localStorage.setItem("reel-volume", String(volume));
  }, [volume]);

  // Wire native audio events
  React.useEffect(() => {
    const a = audioRef.current;
    if (!a) return;
    a.volume = volume;

    const onPlay = () => {
      setPlaying(true);
      if (window.__currentAudio && window.__currentAudio !== a) {
        try { window.__currentAudio.pause(); } catch (e) {}
      }
      window.__currentAudio = a;
      acc.current.lastTick = a.currentTime;
      tracker()?.event("play", { ...meta(), position: a.currentTime });
    };
    const onPause = () => {
      setPlaying(false);
      flushProgress("pause");
    };
    const onTime = () => {
      if (!seeking) setCurrentTime(a.currentTime);
      const ac = acc.current;
      const dt = a.currentTime - ac.lastTick;
      if (dt > 0 && dt < 2) ac.seconds += dt;     // ignore seeks/jumps
      ac.lastTick = a.currentTime;
      ac.maxPos = Math.max(ac.maxPos, a.currentTime);
      if (ac.seconds >= 8) flushProgress();        // periodic batch
    };
    const onMeta = () => setDuration(Number.isFinite(a.duration) ? a.duration : 0);
    const onEnded = () => {
      acc.current.maxPos = duration || acc.current.maxPos;
      flushProgress("ended");
      acc.current = { seconds: 0, maxPos: 0, lastTick: 0 };
      if (index < list.length - 1) {
        wantPlay.current = true;
        setIndex(index + 1);
      } else {
        wantPlay.current = false;
        setPlaying(false);
        setCurrentTime(0);
        a.currentTime = 0;
      }
    };

    a.addEventListener("play", onPlay);
    a.addEventListener("pause", onPause);
    a.addEventListener("timeupdate", onTime);
    a.addEventListener("loadedmetadata", onMeta);
    a.addEventListener("durationchange", onMeta);
    a.addEventListener("ended", onEnded);
    return () => {
      a.removeEventListener("play", onPlay);
      a.removeEventListener("pause", onPause);
      a.removeEventListener("timeupdate", onTime);
      a.removeEventListener("loadedmetadata", onMeta);
      a.removeEventListener("durationchange", onMeta);
      a.removeEventListener("ended", onEnded);
      if (window.__currentAudio === a) window.__currentAudio = null;
    };
  }, [index, seeking, list.length, duration]);

  // Track changed: load + resume if we were playing
  React.useEffect(() => {
    const a = audioRef.current;
    if (!a) return;
    setCurrentTime(0);
    setDuration(0);
    acc.current = { seconds: 0, maxPos: 0, lastTick: 0 };
    if (wantPlay.current) a.play().catch(() => {});
  }, [index]);

  const toggle = () => {
    const a = audioRef.current;
    if (!a) return;
    if (a.paused) { wantPlay.current = true; a.play().catch(() => {}); }
    else { wantPlay.current = false; a.pause(); }
  };

  const playTrack = (i) => {
    if (i === index) { toggle(); return; }
    flushProgress("skip");
    wantPlay.current = true;
    setIndex(i);
  };

  // Waveform seek
  const trackRef = React.useRef(null);
  const setTimeFromEvent = (e) => {
    const a = audioRef.current, trk = trackRef.current;
    if (!a || !trk || !duration) return;
    const rect = trk.getBoundingClientRect();
    const clientX = "clientX" in e ? e.clientX : (e.touches?.[0]?.clientX ?? 0);
    const pct = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
    a.currentTime = pct * duration;
    setCurrentTime(pct * duration);
    acc.current.lastTick = a.currentTime;     // don't count the jump as listening
  };
  const onPointerDown = (e) => {
    setSeeking(true);
    setTimeFromEvent(e);
    e.preventDefault();
    const move = (ev) => setTimeFromEvent(ev);
    const up = () => {
      setSeeking(false);
      window.removeEventListener("pointermove", move);
      window.removeEventListener("pointerup", up);
    };
    window.addEventListener("pointermove", move);
    window.addEventListener("pointerup", up);
  };

  const progress = duration > 0 ? Math.max(0, Math.min(1, currentTime / duration)) : 0;

  return (
    <div className={`reel reel--${playerId || "default"}`}>
      <audio ref={audioRef} src={cur.src} preload="metadata" />

      {/* Transport */}
      <div className="reel-transport">
        <button
          type="button"
          className={`reel-play ${playing ? "playing" : ""}`}
          onClick={toggle}
          aria-label={playing ? "Pause" : "Play"}
        >
          {playing ? (
            <svg width="13" height="14" viewBox="0 0 13 14" aria-hidden="true">
              <rect x="0" y="0" width="4" height="14" fill="currentColor" />
              <rect x="9" y="0" width="4" height="14" fill="currentColor" />
            </svg>
          ) : (
            <svg width="12" height="14" viewBox="0 0 12 14" aria-hidden="true" style={{ marginLeft: 2 }}>
              <path d="M0 0L12 7L0 14V0Z" fill="currentColor" />
            </svg>
          )}
        </button>

        <div className="reel-now">
          <div className="reel-now-title">{cur.title}</div>
          <div className="mono-sm reel-now-sub">{cur.subtitle}</div>
        </div>

        <div
          ref={trackRef}
          className="reel-wave"
          onPointerDown={onPointerDown}
          role="slider"
          aria-label="Seek"
          aria-valuemin={0}
          aria-valuemax={duration || 0}
          aria-valuenow={currentTime}
        >
          {bars.map((h, i) => {
            const played = i / bars.length <= progress;
            return (
              <span
                key={i}
                className={`reel-bar ${played ? "played" : ""}`}
                style={{ height: `${Math.round(h * 100)}%` }}
              />
            );
          })}
        </div>

        <div className="reel-time mono-sm">
          <span>{reelFmt(currentTime)}</span>
          <span className="reel-time-sep">/</span>
          <span className="reel-time-total">{reelFmt(duration)}</span>
        </div>

        <VolumeSlider value={volume} onChange={setVolume} />
      </div>

      {/* Playlist */}
      <ol className="reel-list">
        {list.map((t, i) => {
          const active = i === index;
          return (
            <li
              key={i}
              className={`reel-item ${active ? "active" : ""}`}
            >
              <div
                className={`reel-row ${active ? "active" : ""} ${active && playing ? "playing" : ""}`}
                onClick={() => playTrack(i)}
              >
                <span className="reel-row-idx mono-sm">
                  {active && playing ? (
                    <span className="reel-eq" aria-hidden="true"><span /><span /><span /></span>
                  ) : (
                    String(i + 1).padStart(2, "0")
                  )}
                </span>
                <span className="reel-row-title">{t.title}</span>
                {t.tags && t.tags.length > 0 && (
                  <span className="reel-row-tag mono-sm">{t.tags[0]}</span>
                )}
                <span className="reel-row-sub mono-sm">{t.subtitle}</span>
              </div>
              {active && (t.description || (t.tags && t.tags.length)) && (
                <div className="reel-row-desc">
                  {t.tags && t.tags.length > 0 && (
                    <div className="reel-tags">
                      {t.tags.map((g, gi) => (
                        <span key={gi} className="reel-tag">{g}</span>
                      ))}
                    </div>
                  )}
                  {t.description && <p className="reel-desc-text">{t.description}</p>}
                </div>
              )}
            </li>
          );
        })}
      </ol>

      {footer && <div className="reel-footer">{footer}</div>}
    </div>
  );
}

window.ReelPlayer = ReelPlayer;
