// app.jsx — Stashrun Terminal Onboarding prototype
// Click-thru state machine. 3 videos in /assets, sequential beats reveal text and CTA buttons.

const { useState, useEffect, useRef, useCallback } = React;

// ── Beat sequence ────────────────────────────────────────────────
// Each beat appends transcript lines, optionally swaps the video,
// and shows a CTA button (or tap-anywhere advance) when ready.

const BEATS = [
  // 0 — initial. Video 1 plays once and stops; eyebrow appears immediately.
  //     When video 1 ends → advance to beat 1.
  {
    appends: [
      { kind: "eyebrow", text: "Loading… >" },
    ],
    video: 1,
    loop: false,
    button: null,
    waitForVideoEnd: true,
  },
  // 1 — Video 1 ends: A CAUSE NEEDS YOU + HOW CAN I HELP?
  //     Keep video 1 on its last frame (don't restart loop).
  {
    appends: [
      { kind: "body", text: "A CAUSE NEEDS YOU." },
    ],
    loop: false,
    button: "HOW CAN I HELP?",
  },
  // 2 — Video 2 starts: START A RUN… (no CTA — auto-advances)
  {
    appends: [
      { kind: "spacer" },
      { kind: "eyebrow", text: "Start a run… >" },
      { kind: "body", text: "START A RUN WITH $2 TO UNLOCK 2 INVITES FOR 2 FRIENDS" },
    ],
    video: 2,
    button: null,
    autoAdvance: true,
  },
  // 3 — Mid-video 2: YOUR LEG OF THE RELAY + WHAT'S NEXT?
  {
    appends: [
      { kind: "spacer" },
      { kind: "eyebrow", text: "Your leg… >" },
      { kind: "body", text: "THAT'S YOUR LEG OF THE RELAY." },
    ],
    button: "WHAT'S NEXT?",
  },
  // 4 — Video 3 starts: FRIENDS CAN JOIN… (no CTA — auto-advances)
  {
    appends: [
      { kind: "spacer" },
      { kind: "eyebrow", text: "The relay >" },
      { kind: "body", text: "FRIENDS CAN JOIN YOUR RUN BY ADDING A DONATION AND PAYING IT FORWARD TO MORE FRIENDS…" },
    ],
    video: 3,
    button: null,
    autoAdvance: true,
  },
  // 5 — RELAY GETS LONGER + AS MORE PEOPLE JOIN + THAT'S BIG
  {
    appends: [
      { kind: "spacer" },
      { kind: "eyebrow", text: "The relay >" },
      { kind: "body", text: "THE RELAY GETS LONGER AND LONGER…" },
      { kind: "spacer" },
      { kind: "eyebrow", text: "The payoff >" },
      { kind: "body", text: "AS MORE PEOPLE JOIN $2 CAN BECOME THOUSANDS." },
    ],
    button: "THAT'S BIG",
  },
  // 6 — Final: invite expires (the video itself shows the countdown) + GOT IT
  {
    clear: true,
    appends: [
      { kind: "eyebrow", text: "The key >" },
      { kind: "body", text: "BUT INVITES ARE ONLY LIVE FOR 24 HRS.\nIF NOBODY JOINS YOUR RUN, IT ENDS." },
    ],
    video: 3,
    button: "GOT IT",
  },
];

const VIDEO_SRC = {
  1: "assets/terminal-screen-1.mp4",
  2: "assets/terminal-screen-2.mp4",
  3: "assets/terminal-screen-3.mp4",
};

// ── Tweak defaults ───────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "bodyFadeMs": 500,
  "btnFadeMs": 400,
  "btnDelayMs": 900,
  "dwellMs": 3200,
  "xfadeMs": 400,
  "videoRate": 1,
  "audioOnByDefault": false,
  "loopVideo": true
}/*EDITMODE-END*/;

// ── Main app ─────────────────────────────────────────────────────

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [beatIdx, setBeatIdx] = useState(0);
  const [transcript, setTranscript] = useState([]);
  const [activeIds, setActiveIds] = useState([]);
  const [btnShown, setBtnShown] = useState(false);
  const [btnLabel, setBtnLabel] = useState(null);
  const [showRestart, setShowRestart] = useState(false);
  const [muted, setMuted] = useState(!t.audioOnByDefault);
  const [currentVideo, setCurrentVideo] = useState(1);

  const videoRef = useRef(null);
  const beatTimers = useRef([]);
  const lineIdCounter = useRef(0);
  const autoAdvanceRef = useRef(null);

  const clearBeatTimers = () => {
    beatTimers.current.forEach((id) => clearTimeout(id));
    beatTimers.current = [];
  };
  const scheduleBeat = (fn, delay) => {
    const id = setTimeout(fn, delay);
    beatTimers.current.push(id);
  };

  const armCta = useCallback((beat) => {
    if (beat.button) {
      setBtnLabel(beat.button);
      setBtnShown(true);
    }
  }, []);

  // ── enter a beat ───────────────────────────────────────────────
  const enterBeat = useCallback((idx) => {
    clearBeatTimers();
    setBtnShown(false);
    setBtnLabel(null);
    setShowRestart(false);

    const beat = BEATS[idx];
    if (!beat) return;

    // Video switch
    if (beat.video) {
      setCurrentVideo((cv) => (cv !== beat.video ? beat.video : cv));
    }

    // Allocate stable IDs upfront
    const appendedIds = beat.appends.map(() => ++lineIdCounter.current);

    setTranscript((prev) => {
      const base = beat.clear ? [] : prev.map((l) => ({ ...l, faded: true }));
      const newLines = beat.appends.map((l, i) => ({
        ...l,
        id: appendedIds[i],
        faded: false,
      }));
      return [...base, ...newLines];
    });

    // If this beat clears, reset activeIds
    if (beat.clear) setActiveIds([]);

    // Stagger reveal of newly appended lines
    const baseDelay = 80;
    const perLine = (i) => baseDelay + i * Math.max(160, t.bodyFadeMs * 0.6);

    appendedIds.forEach((id, i) => {
      scheduleBeat(() => {
        setActiveIds((cur) => (cur.includes(id) ? cur : [...cur, id]));
      }, perLine(i));
    });

    // Schedule CTA
    const lastLineDelay = perLine(beat.appends.length - 1) + t.bodyFadeMs;
    const showCtaAt = lastLineDelay + t.btnDelayMs;

    if (beat.waitForVideoEnd) {
      // Primary advance comes from video.onEnded; fallback after a generous delay
      // (in case the video errors or stalls).
      scheduleBeat(() => {
        autoAdvanceRef.current && autoAdvanceRef.current();
      }, 14000);
    } else if (beat.autoAdvance) {
      // No CTA — wait for user to read, then advance automatically.
      scheduleBeat(() => {
        // advance via state setter — read latest beatIdx through callback in caller
        autoAdvanceRef.current && autoAdvanceRef.current();
      }, lastLineDelay + t.dwellMs);
    } else {
      scheduleBeat(() => armCta(beat), showCtaAt);
    }
  }, [t.bodyFadeMs, t.btnDelayMs, t.dwellMs, armCta]);

  // ── advance / restart ──────────────────────────────────────────
  const advance = useCallback(() => {
    const next = beatIdx + 1;
    if (next >= BEATS.length) {
      setShowRestart(true);
      return;
    }
    setBeatIdx(next);
  }, [beatIdx]);

  // Keep latest `advance` accessible from scheduled timers in enterBeat
  useEffect(() => { autoAdvanceRef.current = advance; }, [advance]);

  const restart = useCallback(() => {
    clearBeatTimers();
    setTranscript([]);
    setActiveIds([]);
    setBtnShown(false);
    setBtnLabel(null);
    setShowRestart(false);
    setCurrentVideo(1);
    lineIdCounter.current = 0;
    if (beatIdx === 0) {
      // force re-enter
      setBeatIdx(-1);
      setTimeout(() => setBeatIdx(0), 0);
    } else {
      setBeatIdx(0);
    }
  }, [beatIdx]);

  // ── effects ────────────────────────────────────────────────────
  useEffect(() => {
    if (beatIdx >= 0) enterBeat(beatIdx);
    return clearBeatTimers;
    // eslint-disable-next-line
  }, [beatIdx]);

  useEffect(() => {
    const v = videoRef.current;
    if (!v) return;
    v.muted = muted;
    v.playbackRate = t.videoRate;
    const beat = BEATS[beatIdx];
    const shouldLoop = beat && beat.loop === false ? false : !!t.loopVideo;
    v.loop = shouldLoop;
    // Don't restart a finished video when entering a non-looping beat — let it stay on last frame.
    if (v.ended && !shouldLoop) return;
    const p = v.play();
    if (p && p.catch) p.catch(() => {});
  }, [currentVideo, beatIdx, muted, t.videoRate, t.loopVideo]);

  const onVideoEnded = () => {
    const beat = BEATS[beatIdx];
    if (beat && beat.waitForVideoEnd) {
      // advance to the next beat (which reveals body + button)
      autoAdvanceRef.current && autoAdvanceRef.current();
    }
  };

  const cssVars = {
    "--body-fade": `${t.bodyFadeMs}ms`,
    "--btn-fade":  `${t.btnFadeMs}ms`,
    "--xfade":     `${t.xfadeMs}ms`,
  };

  return (
    <div className="sr-stage">
      <div className="sr-phone" style={cssVars}>
        {/* Header: static asset */}
        <div className="sr-header">
          <img src="assets/navbar-nav-only.png" alt="STASHRUN BETA"/>
        </div>

        {/* TV area */}
        <div className="sr-tv">
          <video
            ref={videoRef}
            className="sr-tv__video sr-tv__crossfade"
            key={currentVideo}
            src={VIDEO_SRC[currentVideo]}
            autoPlay
            playsInline
            muted={muted}
            onEnded={onVideoEnded}
          />
        </div>

        {/* Bottom (transcript + CTA + mute) */}
        <div className="sr-bottom" onClick={advance}>
          {/* Mute toggle — top right of black section */}
          <button
            className="sr-mute"
            aria-label={muted ? "unmute" : "mute"}
            onClick={(e) => { e.stopPropagation(); setMuted((m) => !m); }}
            title={muted ? "Unmute" : "Mute"}
          >
            {muted ? (
              <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
                <path d="M3 7v4h2l4 3V4L5 7H3Z" fill="currentColor"/>
                <path d="M12 6l4 6M16 6l-4 6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
              </svg>
            ) : (
              <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
                <path d="M3 7v4h2l4 3V4L5 7H3Z" fill="currentColor"/>
                <path d="M11.5 6a3.5 3.5 0 0 1 0 6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" fill="none"/>
                <path d="M14 4a6 6 0 0 1 0 10" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" fill="none" opacity="0.7"/>
              </svg>
            )}
          </button>

          <div className="sr-transcript">
            {transcript.map((l) => {
              if (l.kind === "spacer") {
                return <div key={l.id} className={`sr-line sr-line--spacer ${l.faded ? "is-faded" : ""} is-shown`}></div>;
              }
              const cls = [
                "sr-line",
                `sr-line--${l.kind}`,
                activeIds.includes(l.id) ? "is-shown" : "",
                l.faded ? "is-faded" : "",
              ].filter(Boolean).join(" ");
              return <div key={l.id} className={cls}>{l.text}</div>;
            })}
          </div>

          <div className="sr-btn-area">
            {btnLabel && (
              <button
                className={`crt-btn ${btnShown ? "is-shown" : ""}`}
                onClick={(e) => { e.stopPropagation(); advance(); }}
              >
                <span className="crt-btn__label">{btnLabel}</span>
              </button>
            )}
          </div>
        </div>

        {/* Restart overlay */}
        <div className={`sr-restart ${showRestart ? "is-shown" : ""}`}>
          <div className="sr-restart__msg">END OF DEMO</div>
          <button className="crt-btn is-shown" style={{maxWidth: 240}} onClick={restart}>
            <span className="crt-btn__label">RESTART</span>
          </button>
        </div>
      </div>

      {/* Tweaks panel */}
      <TweaksPanel>
        <TweakSection label="Reveal timing"/>
        <TweakSlider label="Body fade" value={t.bodyFadeMs} min={120} max={1200} step={20} unit="ms"
          onChange={(v)=>setTweak('bodyFadeMs', v)} />
        <TweakSlider label="Button fade" value={t.btnFadeMs} min={120} max={1200} step={20} unit="ms"
          onChange={(v)=>setTweak('btnFadeMs', v)} />
        <TweakSlider label="Button delay" value={t.btnDelayMs} min={0} max={3500} step={50} unit="ms"
          onChange={(v)=>setTweak('btnDelayMs', v)} />
        <TweakSlider label="Auto-advance dwell" value={t.dwellMs} min={800} max={8000} step={100} unit="ms"
          onChange={(v)=>setTweak('dwellMs', v)} />
        <TweakSlider label="Cross-fade" value={t.xfadeMs} min={0} max={1500} step={20} unit="ms"
          onChange={(v)=>setTweak('xfadeMs', v)} />

        <TweakSection label="Video"/>
        <TweakSlider label="Playback rate" value={t.videoRate} min={0.25} max={2} step={0.05} unit="×"
          onChange={(v)=>setTweak('videoRate', v)} />
        <TweakToggle label="Loop video" value={t.loopVideo}
          onChange={(v)=>setTweak('loopVideo', v)} />
        <TweakToggle label="Audio on by default" value={t.audioOnByDefault}
          onChange={(v)=>setTweak('audioOnByDefault', v)} />

        <TweakSection label="Nav"/>
        <TweakButton label={`Restart from top`} onClick={restart} />
        <TweakButton label={`Skip beat (→)`} onClick={advance} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
