// Audio player + waveform + synced transcript
const { useState: useStateP, useEffect: useEffectP, useRef: useRefP, useMemo: useMemoP } = React;

const BAR_COUNT = 220;

function Player({ suspect, flagged, onFlag, onUnflag, jumpToken }) {
  const [time, setTime] = useStateP(0);
  const [playing, setPlaying] = useStateP(false);
  const [rate, setRate] = useStateP(1);
  const [peaks, setPeaks] = useStateP(null);
  const [loop, setLoop] = useStateP(null); // {start, end} | null
  const [hoverBar, setHoverBar] = useStateP(-1);
  const rafRef = useRefP(null);
  const waveRef = useRefP(null);

  // Render audio + peaks when suspect changes
  useEffectP(() => {
    let cancelled = false;
    setPeaks(null);
    setTime(0);
    setPlaying(false);
    setLoop(null);
    window.LIE_AUDIO.stop();
    (async () => {
      await window.LIE_AUDIO.render(suspect);
      if (cancelled) return;
      const p = window.LIE_AUDIO.getPeaks(suspect.id, BAR_COUNT);
      setPeaks(p);
    })();
    return () => { cancelled = true; window.LIE_AUDIO.stop(); };
  }, [suspect.id]);

  // Jump-to-time signal from notebook
  useEffectP(() => {
    if (!jumpToken) return;
    if (jumpToken.suspectId !== suspect.id) return;
    seek(jumpToken.time);
  }, [jumpToken && jumpToken.token]);

  // RAF poll for current time
  useEffectP(() => {
    function tick() {
      const t = window.LIE_AUDIO.getTime();
      setTime(t);
      const p = window.LIE_AUDIO.isPlaying();
      if (p !== playing) setPlaying(p);
      if (loop && p && t >= loop.end) {
        window.LIE_AUDIO.play(suspect, loop.start);
      }
      rafRef.current = requestAnimationFrame(tick);
    }
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [suspect.id, playing, loop]);

  function toggle() {
    if (window.LIE_AUDIO.isPlaying()) {
      window.LIE_AUDIO.pause();
      setPlaying(false);
    } else {
      window.LIE_AUDIO.play(suspect, time >= suspect.duration - 0.05 ? 0 : time);
      setPlaying(true);
    }
  }
  function seek(t) {
    const wasPlaying = window.LIE_AUDIO.isPlaying();
    window.LIE_AUDIO.stop();
    setPlaying(false);
    setTime(t);
    if (wasPlaying) {
      window.LIE_AUDIO.play(suspect, t);
      setPlaying(true);
    }
  }
  function changeRate(r) {
    setRate(r);
    window.LIE_AUDIO.setRate(r);
  }

  // Spacebar play/pause + F to flag current line
  useEffectP(() => {
    function key(e) {
      if (e.target && (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")) return;
      if (e.code === "Space") { e.preventDefault(); toggle(); }
      else if (e.key && e.key.toLowerCase() === "f") {
        const line = suspect.lines.find(l => time >= l.start && time < l.end);
        if (line) {
          const key = suspect.id + ":" + line.id;
          if (flagged.has(key)) onUnflag(suspect, line);
          else onFlag(suspect, line);
        }
      }
    }
    window.addEventListener("keydown", key);
    return () => window.removeEventListener("keydown", key);
  });

  // Waveform interaction
  function waveClick(e) {
    const rect = waveRef.current.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width;
    seek(x * suspect.duration);
  }
  function waveMove(e) {
    const rect = waveRef.current.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width;
    setHoverBar(Math.floor(x * BAR_COUNT));
  }

  const activeLine = suspect.lines.find(l => time >= l.start && time < l.end);
  const playProgress = time / suspect.duration;

  return (
    <section className="player">
      <div className="col-header">
        <span className="col-header-num">02</span>
        <span className="col-header-label">TESTIMONY</span>
        <span className="col-header-count">{suspect.role.toUpperCase()}</span>
      </div>

      <div className="player-titlebar">
        <div className="player-titlebar-l">
          <div className="rec-dot" />
          <span>RECORDING · {suspect.id.toUpperCase()}.WAV</span>
        </div>
        <div className="player-titlebar-r">
          <span>16-BIT · 48kHz · MONO</span>
        </div>
      </div>

      <div className="player-name">
        <div className="player-name-l">
          <div className="player-suspect-name">{suspect.name}</div>
          <div className="player-suspect-role">— {suspect.role} · age {suspect.age}</div>
        </div>
        <div className="player-name-r">
          <div className="timecode">
            <span className="tc-played">{formatTime(time)}</span>
            <span className="tc-sep">/</span>
            <span className="tc-total">{formatTime(suspect.duration)}</span>
          </div>
        </div>
      </div>

      <div
        className="waveform"
        ref={waveRef}
        onClick={waveClick}
        onMouseMove={waveMove}
        onMouseLeave={() => setHoverBar(-1)}
      >
        <div className="wave-axis-top">
          {Array.from({ length: 11 }).map((_, i) => (
            <span key={i} style={{ left: (i * 10) + "%" }}>
              {formatTime((i / 10) * suspect.duration)}
            </span>
          ))}
        </div>
        <div className="wave-bars">
          {peaks ? peaks.map((p, i) => {
            const playedFrac = i / BAR_COUNT;
            const played = playedFrac < playProgress;
            const inLoop = loop && playedFrac >= loop.start / suspect.duration && playedFrac < loop.end / suspect.duration;
            const h = Math.max(2, p * 100);
            return (
              <span
                key={i}
                className={"wave-bar" + (played ? " played" : "") + (inLoop ? " in-loop" : "") + (i === hoverBar ? " hover" : "")}
                style={{ height: h + "%" }}
              />
            );
          }) : (
            <div className="wave-loading">RENDERING SIGNAL…</div>
          )}
        </div>
        <div className="wave-playhead" style={{ left: (playProgress * 100) + "%" }}>
          <div className="wave-playhead-line" />
          <div className="wave-playhead-tag">{formatTime(time)}</div>
        </div>
        {/* Lie marker (hidden until verdict) is left out — designer choice: no spoilers */}
        <div className="wave-line-markers">
          {suspect.lines.map(l => (
            <span
              key={l.id}
              className="wave-line-marker"
              style={{ left: ((l.start / suspect.duration) * 100) + "%" }}
              title={l.text}
            />
          ))}
        </div>
      </div>

      <div className="transport">
        <button className="transport-btn back" onClick={() => seek(Math.max(0, time - 3))}>
          <span>⟲ 3s</span>
        </button>
        <button className="transport-btn play" onClick={toggle}>
          {playing ? <PauseIcon /> : <PlayIcon />}
          <span>{playing ? "PAUSE" : "PLAY"}</span>
        </button>
        <button className="transport-btn fwd" onClick={() => seek(Math.min(suspect.duration, time + 3))}>
          <span>3s ⟳</span>
        </button>
        <div className="transport-rate">
          <span className="transport-label">RATE</span>
          {[0.5, 1.0, 1.5].map(r => (
            <button
              key={r}
              className={"rate-btn" + (rate === r ? " on" : "")}
              onClick={() => changeRate(r)}
            >{r.toFixed(1)}×</button>
          ))}
        </div>
        <div className="transport-loop">
          <button
            className={"loop-btn" + (loop ? " on" : "")}
            onClick={() => {
              if (loop) setLoop(null);
              else if (activeLine) setLoop({ start: activeLine.start, end: activeLine.end });
            }}
          >
            <span className="loop-glyph">↻</span>
            <span>{loop ? "LOOP ON" : "LOOP LINE"}</span>
          </button>
        </div>
      </div>

      <div className="transcript">
        <div className="transcript-header">
          <span>TRANSCRIPT · LIVE</span>
          <span className="transcript-hint">F TO FLAG · SPACE TO PLAY</span>
        </div>
        <div className="transcript-body">
          {suspect.lines.map(l => {
            const key = suspect.id + ":" + l.id;
            const isFlagged = flagged.has(key);
            const isActive = activeLine && activeLine.id === l.id;
            // Karaoke: word-level highlight by time within line
            const inLine = Math.max(0, Math.min(1, (time - l.start) / (l.end - l.start)));
            return (
              <div
                key={l.id}
                className={"line" + (isActive ? " active" : "") + (isFlagged ? " flagged" : "")}
              >
                <button className="line-jump" onClick={() => seek(l.start)} title="Jump">
                  <span>{formatTime(l.start)}</span>
                </button>
                <div className="line-text" onClick={() => seek(l.start)}>
                  {l.tag && <span className={"line-tag tag-" + l.tag.toLowerCase()}>{l.tag}</span>}
                  <KaraokeLine text={l.text} progress={isActive ? inLine : (time > l.end ? 1 : 0)} />
                </div>
                <button
                  className={"line-flag" + (isFlagged ? " on" : "")}
                  onClick={() => isFlagged ? onUnflag(suspect, l) : onFlag(suspect, l)}
                  title={isFlagged ? "Unflag" : "Flag"}
                >
                  <span className="flag-glyph">▣</span>
                  <span className="flag-label">{isFlagged ? "FLAGGED" : "FLAG"}</span>
                </button>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

function KaraokeLine({ text, progress }) {
  const words = text.split(/(\s+)/);
  const total = words.filter(w => w.trim().length > 0).length;
  let spoken = 0;
  const cutoff = progress * total;
  return (
    <span>
      {words.map((w, i) => {
        if (!w.trim()) return <span key={i}>{w}</span>;
        const isSpoken = spoken < cutoff;
        spoken += 1;
        return <span key={i} className={isSpoken ? "w spoken" : "w"}>{w}</span>;
      })}
    </span>
  );
}

function PlayIcon() {
  return <svg viewBox="0 0 14 14" width="14" height="14"><polygon points="3,2 12,7 3,12" fill="currentColor" /></svg>;
}
function PauseIcon() {
  return <svg viewBox="0 0 14 14" width="14" height="14"><rect x="3" y="2" width="3" height="10" fill="currentColor" /><rect x="8" y="2" width="3" height="10" fill="currentColor" /></svg>;
}

function formatTime(sec) {
  if (!isFinite(sec) || sec < 0) sec = 0;
  const m = Math.floor(sec / 60);
  const s = Math.floor(sec % 60);
  const cs = Math.floor((sec - Math.floor(sec)) * 100);
  return String(m).padStart(2, "0") + ":" + String(s).padStart(2, "0") + "." + String(cs).padStart(2, "0");
}

window.Player = Player;
window.formatTime = formatTime;
