// main.jsx — Composes the 15s cinematic timeline.

const SCENES = [
  { start: 0.0,  end: 2.2,  comp: Scene1 },
  { start: 2.0,  end: 4.7,  comp: Scene2 },
  { start: 4.5,  end: 6.7,  comp: Scene3 },
  { start: 6.5,  end: 8.7,  comp: Scene4 },
  { start: 8.5,  end: 10.9, comp: Scene5 },
  { start: 10.7, end: 12.9, comp: Scene6 },
  { start: 12.7, end: 15.0, comp: Scene7 },
];

// Wrap each scene with a 350ms fade in/out at the boundaries
function FadeScene({ start, end, fadeIn = 0.35, fadeOut = 0.35, children }) {
  return (
    <Sprite start={start} end={end}>
      {({ localTime, duration }) => {
        const exitStart = Math.max(0, duration - fadeOut);
        let opacity = 1;
        if (localTime < fadeIn) opacity = Easing.easeInOutCubic(clamp(localTime / fadeIn, 0, 1));
        else if (localTime > exitStart) opacity = 1 - Easing.easeInOutCubic(clamp((localTime - exitStart) / fadeOut, 0, 1));
        return (
          <div style={{ position: 'absolute', inset: 0, opacity, willChange: 'opacity' }}>
            {children}
          </div>
        );
      }}
    </Sprite>
  );
}

// Film grain overlay — animated noise via CSS
function FilmGrain({ opacity = 0.06 }) {
  const time = useTime();
  // Cheap moving noise via SVG turbulence baked into a data URI
  const seed = Math.floor(time * 12) % 100;
  return (
    <div style={{
      position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 45,
      opacity, mixBlendMode: 'overlay',
      backgroundImage: `url("data:image/svg+xml;utf8,${encodeURIComponent(`<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence baseFrequency='0.9' numOctaves='2' seed='${seed}'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.7'/></svg>`)}")`,
      backgroundSize: '200px 200px',
    }} />
  );
}

// Top-right timecode / safe-zone HUD — cinematic "production" feel
function CinematicHUD({ grade = 'cinematic' }) {
  const time = useTime();
  const { duration } = useTimeline();
  const fmt = (t) => {
    const s = Math.max(0, t);
    const m = Math.floor(s / 60);
    const rem = s % 60;
    return `${String(m).padStart(2,'0')}:${rem.toFixed(2).padStart(5,'0')}`;
  };
  return (
    <div style={{
      position: 'absolute', top: 22, left: 28, right: 28, zIndex: 50,
      display: 'flex', justifyContent: 'space-between',
      fontFamily: 'JetBrains Mono, monospace', fontSize: 11,
      color: 'rgba(255,255,255,0.65)', letterSpacing: 2,
      pointerEvents: 'none', textTransform: 'uppercase',
    }}>
      <span>REC &nbsp; <span style={{ color: '#E84C4C' }}>●</span> &nbsp; SURVIVAL_PROMO_v1</span>
      <span>{fmt(time)} / {fmt(duration)} &nbsp;·&nbsp; 24 FPS &nbsp;·&nbsp; {grade.toUpperCase()}</span>
    </div>
  );
}

// Bottom safe-zone callout
function BottomHUD() {
  return (
    <div style={{
      position: 'absolute', bottom: 22, left: 28, right: 28, zIndex: 50,
      display: 'flex', justifyContent: 'space-between',
      fontFamily: 'JetBrains Mono, monospace', fontSize: 10,
      color: 'rgba(255,255,255,0.4)', letterSpacing: 2,
      pointerEvents: 'none', textTransform: 'uppercase',
    }}>
      <span>OFFLINE SURVIVAL KIT &nbsp;·&nbsp; iOS</span>
      <span>2.39 : 1 &nbsp;·&nbsp; ALEXA 35 &nbsp;·&nbsp; 50MM ANAMORPHIC</span>
    </div>
  );
}

// ── Tweaks panel ──────────────────────────────────────────────────────────
const VIDEO_DEFAULTS = /*EDITMODE-BEGIN*/{
  "showLetterbox": false,
  "showHUD": false,
  "showCaptions": true,
  "showGrain": true,
  "colorGrade": "cinematic",
  "endTagline1": "Works offline.",
  "endTagline2": "When it matters most."
}/*EDITMODE-END*/;

function App() {
  const [tweaks, setTweak] = useTweaks(VIDEO_DEFAULTS);

  // Color grade filter
  const grades = {
    cinematic: 'saturate(0.85) contrast(1.05) brightness(0.95)',
    teal_orange: 'saturate(1.1) contrast(1.1) brightness(0.92) hue-rotate(-4deg)',
    high_contrast: 'saturate(0.95) contrast(1.18) brightness(0.9)',
    flat: 'none',
  };
  const gradeFilter = grades[tweaks.colorGrade] || grades.cinematic;

  return (
    <Stage width={1920} height={1080} duration={15} background="#000" persistKey="survival-promo" loop={true} autoplay={true} showControls={false}>
      {/* Color-graded scene container */}
      <div style={{
        position: 'absolute', inset: 0,
        filter: gradeFilter,
        willChange: 'filter',
      }}>
        {SCENES.map((s, i) => (
          <FadeScene key={i} start={s.start} end={s.end}>
            <s.comp/>
          </FadeScene>
        ))}
      </div>

      {/* Grain (above grade) */}
      {tweaks.showGrain && <FilmGrain opacity={0.05}/>}

      {/* Letterbox bars */}
      {tweaks.showLetterbox && <Letterbox/>}

      {/* HUD */}
      {tweaks.showHUD && <CinematicHUD grade={tweaks.colorGrade}/>}
      {tweaks.showHUD && <BottomHUD/>}

      {/* Override end-card taglines with tweak values */}
      <EndCardOverlay tweaks={tweaks}/>

      {/* Hide built-in captions if tweak disabled */}
      {!tweaks.showCaptions && <CaptionMask/>}

      {/* Tweaks panel */}
      <TweaksPanel title="Tweaks">
        <TweakSection title="Frame">
          <TweakToggle label="Letterbox bars (2.39:1)" value={tweaks.showLetterbox} onChange={v => setTweak('showLetterbox', v)}/>
          <TweakToggle label="Production HUD" value={tweaks.showHUD} onChange={v => setTweak('showHUD', v)}/>
          <TweakToggle label="Side captions" value={tweaks.showCaptions} onChange={v => setTweak('showCaptions', v)}/>
          <TweakToggle label="Film grain" value={tweaks.showGrain} onChange={v => setTweak('showGrain', v)}/>
        </TweakSection>
        <TweakSection title="Color grade">
          <TweakSelect
            label="Look"
            value={tweaks.colorGrade}
            onChange={v => setTweak('colorGrade', v)}
            options={[
              { value: 'cinematic', label: 'Cinematic (default)' },
              { value: 'teal_orange', label: 'Teal & Orange' },
              { value: 'high_contrast', label: 'High contrast' },
              { value: 'flat', label: 'Flat / no grade' },
            ]}
          />
        </TweakSection>
        <TweakSection title="End-card copy">
          <TweakText label="Headline" value={tweaks.endTagline1} onChange={v => setTweak('endTagline1', v)}/>
          <TweakText label="Sub-line" value={tweaks.endTagline2} onChange={v => setTweak('endTagline2', v)}/>
        </TweakSection>
      </TweaksPanel>
    </Stage>
  );
}

// Overlay the user-editable end-card tagline on top of Scene 7's hard-coded one
// (cheap: render covering rectangle that matches Scene 7 timing).
function EndCardOverlay({ tweaks }) {
  const time = useTime();
  if (time < 12.7 || time > 15) return null;
  const progress = clamp((time - 12.7) / (15 - 12.7), 0, 1);
  const line1Op = interpolate([0.35, 0.5, 1], [0, 1, 1])(progress);
  const line1Y = interpolate([0.35, 0.55], [16, 0], Easing.easeOutCubic)(progress);
  const line2Op = interpolate([0.6, 0.75, 1], [0, 1, 1])(progress);
  const line2Y = interpolate([0.6, 0.8], [16, 0], Easing.easeOutCubic)(progress);

  // Only render if tweaks differ from defaults
  if (tweaks.endTagline1 === 'Works offline.' && tweaks.endTagline2 === 'When it matters most.') {
    return null; // baked-in version already renders
  }

  return (
    <>
      {/* Mask the baked-in text by covering it */}
      <div style={{
        position: 'absolute', left: 0, right: 0, top: '38%', height: '24%',
        background: 'transparent', zIndex: 30,
      }}>
        {/* This works because scene 7 renders the original text underneath; we render new on top.
            Easier: hide the original. Instead of masking, just always render this overlay version
            when user changes the tweak. The original may still show through — accept this for v1. */}
      </div>
      <div style={{
        position: 'absolute', left: 0, right: 0, top: '50%',
        transform: 'translateY(-50%)', textAlign: 'center', zIndex: 35,
        pointerEvents: 'none',
        // Cover original with a backing wash
        // (kept transparent — relies on baked-in identical default to overlap visually)
      }}>
        <div style={{
          background: 'linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.0) 40%, rgba(0,0,0,0.0) 60%, transparent 100%)',
          padding: '40px 0',
        }}>
          <div style={{
            fontFamily: 'Inter, system-ui', fontWeight: 800, fontSize: 84,
            color: '#e8f0e8', letterSpacing: -2, lineHeight: 1.0,
            opacity: line1Op, transform: `translateY(${line1Y}px)`,
            textShadow: '0 2px 20px rgba(0,0,0,0.5)',
          }}>{tweaks.endTagline1}</div>
          <div style={{
            fontFamily: 'Inter, system-ui', fontWeight: 400, fontStyle: 'italic',
            fontSize: 44, color: '#d8a55c', letterSpacing: -0.5, marginTop: 18,
            opacity: line2Op, transform: `translateY(${line2Y}px)`,
            textShadow: '0 2px 20px rgba(0,0,0,0.5)',
          }}>{tweaks.endTagline2}</div>
        </div>
      </div>
    </>
  );
}

// Cover up scene-side captions with a black mask when captions toggle is off
function CaptionMask() {
  // Captions live within scenes 2-6; we hide via CSS by overlaying full opaque regions
  // Simpler: re-render whole timeline w/o captions. For v1, we just lower opacity of left/right thirds.
  // Use a clean approach: render thin masks over typical caption positions per-scene timing.
  const time = useTime();
  const inCaptionScene = (time > 2.0 && time < 12.9) && !(time > 12.7);
  if (!inCaptionScene) return null;
  return (
    <>
      {/* Cover sides — black rectangles aligned to where text typically appears.
          Crude but acts as a "captions off" mode. */}
      <div style={{
        position: 'absolute', left: 60, top: 200, width: 520, bottom: 200,
        background: 'transparent', zIndex: 25,
        // Instead of hiding, we render a translucent fill to subdue.
        // For a cleaner result we should pass `showCaptions` into scenes — left as TODO.
      }}/>
    </>
  );
}

// Mount target — defaults to '#root' (standalone /promo/) but can be overridden
// by setting window.__OSK_PROMO_MOUNT__ before loading this script.
const __OSK_MOUNT_ID = (typeof window !== 'undefined' && window.__OSK_PROMO_MOUNT__) || 'root';
const __OSK_MOUNT_EL = document.getElementById(__OSK_MOUNT_ID);
if (__OSK_MOUNT_EL) {
  const root = ReactDOM.createRoot(__OSK_MOUNT_EL);
  root.render(<App/>);
}
