import {
  AbsoluteFill,
  Audio,
  staticFile,
  useCurrentFrame,
  useVideoConfig,
  interpolate,
} from 'remotion';
import { C } from './lib/colors';
import { clampLerp } from './lib/easings';
import { HookScene } from './scenes/HookScene';
import { PromptScene } from './scenes/PromptScene';
import { SecretsScene } from './scenes/SecretsScene';
import { BuildScene } from './scenes/BuildScene';
import { IsolationScene } from './scenes/IsolationScene';
import { LibraryScene } from './scenes/LibraryScene';
import { DiscoveryScene } from './scenes/DiscoveryScene';
import { LogoLockup } from './scenes/LogoLockup';

export const HERO_FPS = 30;
// 71.5s @ 30fps. Audio is 71.47s — the 30ms tail gives us room for a clean
// loop-fade to black.
export const HERO_DURATION_FRAMES = 2145;

// 8-phase composition matched to the 71.47s narration. Each scene overlaps
// its neighbour by 6 frames so the parent crossfade does the transition.
//
// Phase ranges below match the brief; sum of unique frames = 2145.
//
//   Hook       [   0,  360)  360f   "code-gen solved, plumbing left"
//   Prompt     [ 354,  720)  366f   "one sentence describes your tool"
//   Secrets    [ 714, 1170)  456f   "vault, AES-256, injected at runtime"
//   Build      [1164, 1380)  216f   "TypeScript, checks, ship — < 60s"
//   Isolation  [1374, 1800)  426f   "hardened container · OAuth 2.1+PKCE"
//   Library    [1794, 2010)  216f   "export source · share template"
//   Discovery  [2004, 2100)   96f   "one sentence in · live server out"
//   LogoLockup [2094, 2145)   51f   "BuildMyMCPServer.com"
export const BEAT = {
  hook:       { in:    0, out:  360 },
  prompt:     { in:  354, out:  720 },
  secrets:    { in:  714, out: 1170 },
  build:      { in: 1164, out: 1380 },
  isolation:  { in: 1374, out: 1800 },
  library:    { in: 1794, out: 2010 },
  discovery:  { in: 2004, out: 2100 },
  lockup:     { in: 2094, out: 2145 },
} as const;

const LOOP_FADE_FRAMES = 18;
// Subtitle/caption layer was tried in v7 and pulled — the owner's
// note: most captions felt aggressive and on-the-nose, the technical
// terms (PKCE etc.) created more friction than they removed. We keep
// `components/Subtitle.tsx` around for future use but the composition
// no longer renders it. Scene visuals + the voice-over now carry the
// whole story.

export function HeroVideo() {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  // Loop-clean fade-out on the final 18 frames so the final frame ≈ black.
  // Note: LogoLockup itself also fades internally; this is a safety net.
  const loopFade = interpolate(
    frame,
    [HERO_DURATION_FRAMES - LOOP_FADE_FRAMES, HERO_DURATION_FRAMES - 1],
    [1, 0],
    { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' },
  );

  const hookAlpha       = crossfade(frame, BEAT.hook.in,       BEAT.hook.out,       6);
  const promptAlpha     = crossfade(frame, BEAT.prompt.in,     BEAT.prompt.out,     6);
  const secretsAlpha    = crossfade(frame, BEAT.secrets.in,    BEAT.secrets.out,    6);
  const buildAlpha      = crossfade(frame, BEAT.build.in,      BEAT.build.out,      6);
  const isolationAlpha  = crossfade(frame, BEAT.isolation.in,  BEAT.isolation.out,  6);
  const libraryAlpha    = crossfade(frame, BEAT.library.in,    BEAT.library.out,    6);
  const discoveryAlpha  = crossfade(frame, BEAT.discovery.in,  BEAT.discovery.out,  6);
  const lockupAlpha     = crossfade(frame, BEAT.lockup.in,     BEAT.lockup.out,     6);

  return (
    <AbsoluteFill style={{ backgroundColor: C.bg, overflow: 'hidden' }}>
      <DottedGrid />
      <Vignette />

      <AbsoluteFill style={{ opacity: loopFade }}>
        {hookAlpha > 0 && (
          <AbsoluteFill style={{ opacity: hookAlpha }}>
            <HookScene localFrame={frame - BEAT.hook.in} fps={fps} />
          </AbsoluteFill>
        )}
        {promptAlpha > 0 && (
          <AbsoluteFill style={{ opacity: promptAlpha }}>
            <PromptScene localFrame={frame - BEAT.prompt.in} fps={fps} />
          </AbsoluteFill>
        )}
        {secretsAlpha > 0 && (
          <AbsoluteFill style={{ opacity: secretsAlpha }}>
            <SecretsScene localFrame={frame - BEAT.secrets.in} fps={fps} />
          </AbsoluteFill>
        )}
        {buildAlpha > 0 && (
          <AbsoluteFill style={{ opacity: buildAlpha }}>
            <BuildScene localFrame={frame - BEAT.build.in} fps={fps} />
          </AbsoluteFill>
        )}
        {isolationAlpha > 0 && (
          <AbsoluteFill style={{ opacity: isolationAlpha }}>
            <IsolationScene localFrame={frame - BEAT.isolation.in} fps={fps} />
          </AbsoluteFill>
        )}
        {libraryAlpha > 0 && (
          <AbsoluteFill style={{ opacity: libraryAlpha }}>
            <LibraryScene localFrame={frame - BEAT.library.in} fps={fps} />
          </AbsoluteFill>
        )}
        {discoveryAlpha > 0 && (
          <AbsoluteFill style={{ opacity: discoveryAlpha }}>
            <DiscoveryScene localFrame={frame - BEAT.discovery.in} fps={fps} />
          </AbsoluteFill>
        )}
        {lockupAlpha > 0 && (
          <AbsoluteFill style={{ opacity: lockupAlpha }}>
            <LogoLockup localFrame={frame - BEAT.lockup.in} fps={fps} />
          </AbsoluteFill>
        )}
      </AbsoluteFill>

      {/* Audio: voice-over at full volume + sub-bass music ducked underneath.
          Music fades in over 30f, fades out over the final 24f. */}
      <Audio src={staticFile('audio.mp3')} />
      <Audio
        src={staticFile('Sub-bass Lullaby.wav')}
        volume={(f) => {
          const fadeIn = interpolate(f, [0, 30], [0, 0.16], {
            extrapolateLeft: 'clamp',
            extrapolateRight: 'clamp',
          });
          const fadeOut = interpolate(
            f,
            [HERO_DURATION_FRAMES - 24, HERO_DURATION_FRAMES - 1],
            [0.16, 0],
            { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' },
          );
          return Math.min(fadeIn, fadeOut);
        }}
      />
    </AbsoluteFill>
  );
}

function crossfade(frame: number, start: number, end: number, overlap: number) {
  if (frame < start || frame >= end) return 0;
  const intoStart = clampLerp(frame, start, start + overlap);
  const beforeEnd = 1 - clampLerp(frame, end - overlap, end);
  return Math.min(intoStart, beforeEnd);
}

function DottedGrid() {
  const dotSize = 1.5;
  const gap = 48;
  const dots = [];
  for (let y = 0; y < 1080 + gap; y += gap) {
    for (let x = 0; x < 1920 + gap; x += gap) {
      dots.push(
        <circle key={`${x}-${y}`} cx={x} cy={y} r={dotSize} fill={C.borderStrong} opacity={0.45} />,
      );
    }
  }
  return (
    <svg width={1920} height={1080} style={{ position: 'absolute', inset: 0 }}>
      <defs>
        <radialGradient id="vignette" cx="50%" cy="50%" r="65%">
          <stop offset="0%" stopColor="white" stopOpacity="1" />
          <stop offset="100%" stopColor="white" stopOpacity="0" />
        </radialGradient>
        <mask id="vignetteMask">
          <rect width="1920" height="1080" fill="url(#vignette)" />
        </mask>
      </defs>
      <g mask="url(#vignetteMask)">{dots}</g>
    </svg>
  );
}

function Vignette() {
  return (
    <div
      style={{
        position: 'absolute',
        inset: 0,
        background:
          'radial-gradient(ellipse 60% 50% at 50% 50%, rgba(99,102,241,0.10) 0%, rgba(99,102,241,0) 70%)',
        pointerEvents: 'none',
      }}
    />
  );
}
