// voice.jsx — uses the user's own recording as the master narration.
// The audio is ONE continuous track; the timeline playhead drives audio.currentTime
// so visuals and voice stay locked. A calibration overlay lets the user tap the
// exact scene-change moments while listening; results persist to localStorage.

const MARKS_KEY = 'oneway-scene-marks-v4';
const NUM_SCENES = 8;

function loadMarks(defaults) {
  try {
    const raw = localStorage.getItem(MARKS_KEY);
    if (raw) {
      const arr = JSON.parse(raw);
      if (Array.isArray(arr) && arr.length === defaults.length) return arr;
    }
  } catch (e) {}
  return defaults;
}

function VoiceTrack({ src, duration }) {
  const { time, playing, setTime, setPlaying } = useTimeline();
  const audioRef = React.useRef(null);
  const [muted, setMuted] = React.useState(false);
  const [ready, setReady] = React.useState(false);
  const [blocked, setBlocked] = React.useState(false);
  const [visible, setVisible] = React.useState(
    typeof document === 'undefined' || document.visibilityState === 'visible'
  );

  // Only the focused/visible view plays audio — prevents a second open copy
  // of the page (e.g. a background preview) from echoing over this one.
  React.useEffect(() => {
    const onVis = () => setVisible(document.visibilityState === 'visible');
    document.addEventListener('visibilitychange', onVis);
    window.addEventListener('blur', () => setVisible(document.visibilityState === 'visible'));
    return () => document.removeEventListener('visibilitychange', onVis);
  }, []);

  // ── Calibration state ──
  const [calib, setCalib] = React.useState(false);
  const [marksDraft, setMarksDraft] = React.useState([]);

  // Lock audio position to the timeline (scrub-safe). Voice never loops.
  React.useEffect(() => {
    const a = audioRef.current;
    if (!a || !ready) return;
    const target = clamp(time, 0, a.duration || duration);
    if (Math.abs(a.currentTime - target) > 0.28) {
      try { a.currentTime = target; } catch (e) {}
    }
  }, [time, ready, duration]);

  React.useEffect(() => {
    const a = audioRef.current;
    if (!a || !ready) return;
    if (playing && !muted && visible) {
      a.play().then(() => setBlocked(false)).catch(() => setBlocked(true));
    } else {
      a.pause();
    }
  }, [playing, muted, ready, visible]);

  // Autoplay-policy unlock: the first real user gesture starts/raises audio.
  React.useEffect(() => {
    if (!ready) return;
    const unlock = () => {
      const a = audioRef.current;
      if (!a) return;
      if (playing && !muted && visible) {
        a.play().then(() => setBlocked(false)).catch(() => {});
      }
    };
    const opts = { passive: true };
    window.addEventListener('pointerdown', unlock, opts);
    window.addEventListener('keydown', unlock, opts);
    window.addEventListener('touchstart', unlock, opts);
    return () => {
      window.removeEventListener('pointerdown', unlock);
      window.removeEventListener('keydown', unlock);
      window.removeEventListener('touchstart', unlock);
    };
  }, [ready, playing, muted, visible]);

  React.useEffect(() => {
    const a = audioRef.current;
    if (a) a.volume = muted ? 0 : 1;
  }, [muted]);

  // ── Calibration controls ──
  const startCalib = () => {
    setMarksDraft([0]);            // scene 1 always starts at 0
    setCalib(true);
    setTime(0);
    setPlaying(true);
  };
  const dropMark = () => {
    const a = audioRef.current;
    const t = a ? a.currentTime : time;
    setMarksDraft((prev) => {
      const next = [...prev, +t.toFixed(2)];
      if (next.length >= NUM_SCENES) {           // captured all starts → finish
        const final = [...next, +(a ? a.duration : duration).toFixed(2)];
        try { localStorage.setItem(MARKS_KEY, JSON.stringify(final)); } catch (e) {}
        setPlaying(false);
        setTimeout(() => location.reload(), 150);
      }
      return next;
    });
  };
  const cancelCalib = () => { setCalib(false); setPlaying(false); };
  const resetMarks = () => { try { localStorage.removeItem(MARKS_KEY); } catch (e) {} location.reload(); };

  const SCENE_NAMES = ['Open', 'The problem', 'The hidden math', 'ROI calculator', 'Why OneWay', 'How it works', 'Testimonials', 'Book a call'];
  const nextSceneIdx = marksDraft.length; // 1-based index of the next scene to mark

  return (
    <>
      <audio ref={audioRef} src={src} preload="auto" onCanPlay={() => setReady(true)} />

      {/* Tap-for-sound prompt (shown only if the browser blocked autoplay audio) */}
      {blocked && !muted && (
        <div
          onClick={() => { const a = audioRef.current; if (a) a.play().then(() => setBlocked(false)).catch(() => {}); }}
          style={{
            position: 'absolute', inset: 0, zIndex: 70, cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            background: 'rgba(8,10,13,0.55)', backdropFilter: 'blur(3px)',
          }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '18px 28px', borderRadius: 99, background: OW.accent, color: OW.ink, fontFamily: BODY, fontSize: 22, fontWeight: 700, boxShadow: `0 20px 60px -20px ${OW.accentGl}` }}>
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M11 5L6 9H3v6h3l5 4V5z" fill="currentColor" /><path d="M15 8.5a4 4 0 010 7M17.5 6a7.5 7.5 0 010 12" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" /></svg>
            Click for sound
          </div>
        </div>
      )}

      {/* Mute toggle */}
      <button onClick={() => setMuted((m) => !m)}
        style={{
          position: 'absolute', top: 28, right: 32, zIndex: 60,
          display: 'flex', alignItems: 'center', gap: 10, padding: '12px 18px', borderRadius: 99,
          background: !muted ? 'rgba(53,224,161,0.14)' : 'rgba(20,25,35,0.7)',
          border: `1px solid ${!muted ? 'rgba(53,224,161,0.5)' : OW.line}`,
          color: !muted ? OW.accent : OW.muted,
          fontFamily: MONO, fontSize: 14, letterSpacing: '0.08em', textTransform: 'uppercase',
          cursor: 'pointer', backdropFilter: 'blur(6px)',
        }}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
          <path d="M11 5L6 9H3v6h3l5 4V5z" fill="currentColor" />
          {!muted
            ? <path d="M15 8.5a4 4 0 010 7M17.5 6a7.5 7.5 0 010 12" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
            : <path d="M16 9l4 6M20 9l-4 6" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />}
        </svg>
        {!muted ? 'Voice on' : 'Voice off'}
      </button>

      {/* Sync button (hidden in share mode) */}
      {false && (
      <button onClick={startCalib}
        style={{
          position: 'absolute', top: 28, right: 188, zIndex: 60,
          display: 'flex', alignItems: 'center', gap: 9, padding: '12px 18px', borderRadius: 99,
          background: 'rgba(20,25,35,0.7)', border: `1px solid ${OW.line}`, color: OW.muted,
          fontFamily: MONO, fontSize: 14, letterSpacing: '0.08em', textTransform: 'uppercase',
          cursor: 'pointer', backdropFilter: 'blur(6px)',
        }}>
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="12" r="9" stroke="currentColor" strokeWidth="1.8" /><path d="M12 7v5l3 2" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" /></svg>
        Sync voice
      </button>
      )}

      {/* Calibration overlay */}
      {calib && (
        <div style={{
          position: 'absolute', inset: 0, zIndex: 80,
          background: 'rgba(8,10,13,0.86)', backdropFilter: 'blur(10px)',
          display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 30,
        }}>
          <div style={{ fontFamily: MONO, fontSize: 16, letterSpacing: '0.2em', textTransform: 'uppercase', color: OW.accent }}>
            Listening · {marksDraft.length} / {NUM_SCENES} marked
          </div>
          <div style={{ fontFamily: BODY, fontSize: 30, color: OW.text, fontWeight: 500, textAlign: 'center', maxWidth: 900, lineHeight: 1.4 }}>
            When you hear the line for
            <span style={{ color: OW.accent }}> &ldquo;{SCENE_NAMES[nextSceneIdx] || '—'}&rdquo;</span> begin,
            <br />click the button below.
          </div>
          <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
            <button onClick={dropMark}
              style={{ padding: '20px 44px', borderRadius: 99, background: OW.accent, color: OW.ink,
                fontFamily: BODY, fontSize: 22, fontWeight: 700, border: 'none', cursor: 'pointer' }}>
              Mark &ldquo;{SCENE_NAMES[nextSceneIdx] || 'done'}&rdquo;
            </button>
            <button onClick={cancelCalib}
              style={{ padding: '20px 28px', borderRadius: 99, background: 'transparent', color: OW.muted,
                fontFamily: BODY, fontSize: 18, fontWeight: 500, border: `1px solid ${OW.line}`, cursor: 'pointer' }}>
              Cancel
            </button>
          </div>
          <div style={{ fontFamily: MONO, fontSize: 13, color: OW.faint }}>
            Tip: scene 1 starts at 0. Mark scenes 2–8 as you hear each one.
          </div>
        </div>
      )}
    </>
  );
}

Object.assign(window, { VoiceTrack, loadMarks });
