import type { ReactNode } from 'react';
import { interpolate } from 'remotion';
import { C } from '../lib/colors';
import { softSpring, clampLerp } from '../lib/easings';

// Phase 5 · 14 s · 426 frames.
//
// Re-laid out from the original orbit-and-dock design after the owner
// flagged two collisions and the PKCE jargon:
//
//   - "0.5 CPU" used to sit at angle π − 0.5 with distance 200, which
//     drops the bubble right onto the bottom-left corner of the
//     notion-search card. Fixed by moving chips to a clean vertical
//     column at x = 720, well to the right of the card.
//   - "no-new-privileges" docked at angle π/2 + 0.7 (lower-left) which
//     overlapped the green "your tokens only" badge below the card.
//     Fixed by merging tokens-only into the same chip column as the
//     6th green item — one ordered list of guarantees.
//   - "OAuth 2.1 · PKCE" was unreadable to anyone who doesn't write
//     auth code. Replaced with a three-stage flow labelled in plain
//     language: your client → verified → scoped token reaches the
//     server. The technical name stays as a small subscript so it's
//     google-able, but it isn't the headline.
//   - The vault wasn't visible in this scene at all — secrets just
//     appeared as a pill on the card in BuildScene and were never
//     shown again. Added a vault graphic below the card with a
//     dashed arrow up into its env slot, so the runtime-injection
//     story is complete in one frame.

interface ChipDef {
  id: string;
  label: string;
  /** Local frame when this chip enters. */
  enterAt: number;
  /** Final colour: indigo accent for hardening, green for tokens-only. */
  tone: 'accent' | 'success';
}

// Plain-language labels, no docker flag names except where they're
// unambiguous ("read-only filesystem" is clearer than `--read-only`).
const CHIPS: ChipDef[] = [
  { id: 'ro',     label: 'read-only filesystem', enterAt: 40,  tone: 'accent' },
  { id: 'cap',   label: 'dropped capabilities',  enterAt: 62,  tone: 'accent' },
  { id: 'nnp',   label: 'no new privileges',     enterAt: 84,  tone: 'accent' },
  { id: 'mem',   label: '512 MB memory cap',     enterAt: 106, tone: 'accent' },
  { id: 'cpu',   label: '0.5 CPU limit',         enterAt: 128, tone: 'accent' },
  { id: 'token', label: 'your token only',       enterAt: 360, tone: 'success' },
];

// Layout anchors — generous gaps so nothing collides.
const CARD_X = 320;       // card centre
const CARD_Y = 460;       // card centre (lifted up so vault has room)
const CARD_W = 360;
const CARD_H = 200;

const VAULT_X = 320;
const VAULT_Y = 740;      // vault centre, below the card with a 60 px gap
const VAULT_W = 340;
const VAULT_H = 130;

const CHIP_COL_X = 760;   // right of the card
const CHIP_COL_TOP = 350;
const CHIP_SPACING = 64;

const OAUTH_HEADER_Y = 280;
const OAUTH_FLOW_Y = 700;

// Three-stage simplified auth flow — no PKCE / verifier / SHA256 in the
// on-screen labels. The full technical name appears once, small, under
// the section header.
const OAUTH_STAGES = [
  { t: 0,  label: 'your client',    x: 1180, y: OAUTH_FLOW_Y },
  { t: 14, label: 'verified',       x: 1440, y: OAUTH_FLOW_Y },
  { t: 28, label: 'scoped token',   x: 1700, y: OAUTH_FLOW_Y },
];
const OAUTH_START = 200;

const GLOW_START = 340;       // server card flashes green here
const TOKEN_CHIP_ENTER = 360; // token chip joins the column at this frame
const SCENE_LEN = 426;

export function IsolationScene({ localFrame, fps }: { localFrame: number; fps: number }) {
  const sceneOpacity = clampLerp(localFrame, 0, 12);

  // Green pulse on the server card — the moment the chain locks in.
  const glowT = clampLerp(localFrame, GLOW_START, GLOW_START + 30);
  const glowDown = clampLerp(localFrame, GLOW_START + 30, GLOW_START + 60);
  const greenPulse = glowT - glowDown;

  // Exit lift in the last few frames (parent crossfades).
  const exitProgress = clampLerp(localFrame, SCENE_LEN - 12, SCENE_LEN);
  const exitY = interpolate(exitProgress, [0, 1], [0, -18]);

  return (
    <div style={{ position: 'absolute', inset: 0, opacity: sceneOpacity, transform: `translateY(${exitY}px)` }}>
      {/* Section caption */}
      <div
        style={{
          position: 'absolute',
          top: 130,
          left: 0,
          right: 0,
          textAlign: 'center',
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 16,
          letterSpacing: 5,
          textTransform: 'uppercase',
          color: C.fgSubtle,
        }}
      >
        hardened by default
      </div>

      {/* Server card (left) */}
      <ServerCard
        cx={CARD_X}
        cy={CARD_Y}
        w={CARD_W}
        h={CARD_H}
        localFrame={localFrame}
        greenPulse={greenPulse}
      />

      {/* Vault graphic (below card) — completes the architecture: the
          secret in the vault is the one runtime-injected into the
          card above it. Without this the scene only shows "server is
          safe" and leaves the viewer wondering where the secret lives. */}
      <Vault
        cx={VAULT_X}
        cy={VAULT_Y}
        w={VAULT_W}
        h={VAULT_H}
        localFrame={localFrame}
      />

      {/* Hardening chips column (right of card) — one ordered list. The
          first five enter staggered while the OAuth flow plays on the
          far right; the final green "your token only" chip lands last
          to close the scene. */}
      <div
        style={{
          position: 'absolute',
          left: CHIP_COL_X,
          top: CHIP_COL_TOP,
        }}
      >
        {CHIPS.map((chip, i) => (
          <HardeningChip
            key={chip.id}
            chip={chip}
            index={i}
            localFrame={localFrame}
            fps={fps}
          />
        ))}
      </div>

      {/* OAuth flow (far right) — three stages, plain language. The PKCE
          term sits small under the header so it's findable but never
          the headline. */}
      <OAuthFlow localFrame={localFrame} fps={fps} />
    </div>
  );
}

// ---------- ServerCard ----------

function ServerCard({
  cx,
  cy,
  w,
  h,
  localFrame,
  greenPulse,
}: {
  cx: number;
  cy: number;
  w: number;
  h: number;
  localFrame: number;
  greenPulse: number;
}) {
  const entryT = clampLerp(localFrame, 0, 18);
  const scale = interpolate(entryT, [0, 1], [0.92, 1]);
  const borderColor =
    greenPulse > 0.05 ? interpolateColor(C.accent, C.success, greenPulse) : C.accent;
  return (
    <div
      style={{
        position: 'absolute',
        left: cx - w / 2,
        top: cy - h / 2,
        width: w,
        height: h,
        backgroundColor: C.bgElevated,
        border: `1.5px solid ${borderColor}`,
        borderRadius: 16,
        boxShadow:
          greenPulse > 0.05
            ? `0 0 0 ${4 + greenPulse * 4}px rgba(34,197,94,${0.20 + greenPulse * 0.20}), 0 24px 70px rgba(0,0,0,0.6)`
            : `0 0 0 5px ${C.accentGlow}, 0 24px 70px rgba(0,0,0,0.6)`,
        opacity: entryT,
        transform: `scale(${scale})`,
        padding: '22px 24px',
        display: 'flex',
        flexDirection: 'column',
        gap: 16,
      }}
    >
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ fontFamily: 'ui-monospace, SF Mono, Menlo, monospace', fontSize: 18, color: C.fg }}>
          notion-search
        </div>
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            gap: 6,
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 11,
            letterSpacing: 1.5,
            textTransform: 'uppercase',
            color: C.success,
          }}
        >
          <div
            style={{
              width: 7,
              height: 7,
              borderRadius: 4,
              backgroundColor: C.success,
              boxShadow: `0 0 ${6 + greenPulse * 12}px ${C.success}`,
            }}
          />
          live
        </div>
      </div>

      <div
        style={{
          display: 'flex',
          flexDirection: 'column',
          gap: 8,
          fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
          fontSize: 13,
          color: C.fgMuted,
        }}
      >
        <Row label="image"   value="bmm/notion-search:1" />
        <Row label="runtime" value="node-22-slim" />
        <Row label="uptime"  value="00:00:14" />
      </div>
    </div>
  );
}

function Row({ label, value }: { label: string; value: string }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
      <span>{label}</span>
      <span style={{ color: C.fg }}>{value}</span>
    </div>
  );
}

// ---------- Vault ----------

function Vault({
  cx,
  cy,
  w,
  h,
  localFrame,
}: {
  cx: number;
  cy: number;
  w: number;
  h: number;
  localFrame: number;
}) {
  // Vault enters slightly after the card so the eye reads the card first.
  const entryT = clampLerp(localFrame, 12, 32);
  const arrowReveal = clampLerp(localFrame, 30, 60);
  const opacity = entryT;
  const scale = interpolate(entryT, [0, 1], [0.92, 1]);

  // Dashed arrow from vault top edge up into card bottom edge.
  const cardBottom = 460 + 100; // CARD_Y + CARD_H/2 = 560
  const vaultTop = cy - h / 2;
  const arrowX = cx; // vertically aligned
  const arrowYStart = vaultTop - 4;
  const arrowYEnd = cardBottom + 4;
  const arrowLen = arrowYStart - arrowYEnd;

  return (
    <>
      {/* Connector arrow */}
      <svg
        width={1920}
        height={1080}
        style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}
      >
        <line
          x1={arrowX}
          y1={arrowYStart}
          x2={arrowX}
          y2={arrowYEnd + 8}
          stroke={C.accent}
          strokeWidth={1.6}
          strokeDasharray="6 6"
          strokeDashoffset={(1 - arrowReveal) * arrowLen}
          opacity={0.85 * arrowReveal}
        />
        <polygon
          points={`${arrowX - 5},${arrowYEnd + 8} ${arrowX + 5},${arrowYEnd + 8} ${arrowX},${arrowYEnd}`}
          fill={C.accent}
          opacity={Math.max(0, arrowReveal - 0.7) * 3}
        />
      </svg>

      <div
        style={{
          position: 'absolute',
          left: cx - w / 2,
          top: cy - h / 2,
          width: w,
          height: h,
          backgroundColor: C.bgElevated,
          border: `1.5px solid ${C.accentDim}`,
          borderRadius: 14,
          boxShadow: `0 0 0 4px rgba(79,70,229,0.12), 0 18px 50px rgba(0,0,0,0.55)`,
          opacity,
          transform: `scale(${scale})`,
          padding: '14px 18px',
          display: 'flex',
          flexDirection: 'column',
          gap: 8,
        }}
      >
        <div
          style={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-between',
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 11,
            letterSpacing: 2.5,
            textTransform: 'uppercase',
            color: C.fgSubtle,
          }}
        >
          <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <SmallLockIcon />
            secrets vault
          </span>
          <span
            style={{
              color: C.accent,
              border: `1px solid ${C.accentDim}`,
              padding: '3px 8px',
              borderRadius: 999,
              fontSize: 10,
              letterSpacing: 1.2,
              backgroundColor: 'rgba(99,102,241,0.08)',
            }}
          >
            AES-256
          </span>
        </div>

        <div
          style={{
            backgroundColor: C.bgSubtle,
            border: `1px solid ${C.border}`,
            borderRadius: 8,
            padding: '12px 14px',
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 14,
            color: C.fg,
            display: 'flex',
            alignItems: 'center',
            gap: 10,
          }}
        >
          <span style={{ color: C.fgSubtle }}>NOTION_API_KEY</span>
          <span style={{ color: C.fgMuted }}>=</span>
          <span>secret_•••••••3a8f</span>
        </div>
      </div>
    </>
  );
}

function SmallLockIcon() {
  return (
    <svg width="11" height="11" viewBox="0 0 16 16" fill="none">
      <path d="M 5 8 L 5 6 A 3 3 0 0 1 11 6 L 11 8" stroke={C.accent} strokeWidth={1.3} fill="none" />
      <rect x="3.5" y="7.5" width="9" height="7" rx="1.5" fill="rgba(99,102,241,0.20)" stroke={C.accent} strokeWidth={1.3} />
      <circle cx="8" cy="11" r="1" fill={C.accent} />
    </svg>
  );
}

// ---------- HardeningChip ----------

function HardeningChip({
  chip,
  index,
  localFrame,
  fps,
}: {
  chip: ChipDef;
  index: number;
  localFrame: number;
  fps: number;
}) {
  // Spring from off-screen-right (+260 px) to the docked position.
  const t = softSpring(localFrame, fps, chip.enterAt, 22);
  if (t < 0.01) return null;
  const x = interpolate(t, [0, 1], [260, 0]);
  const opacity = clampLerp(localFrame, chip.enterAt, chip.enterAt + 12);

  const isSuccess = chip.tone === 'success';
  const colour = isSuccess ? C.success : C.accent;
  const fill = isSuccess ? 'rgba(34,197,94,0.10)' : 'rgba(99,102,241,0.10)';
  const glow = isSuccess
    ? '0 0 18px rgba(34,197,94,0.25)'
    : '0 0 12px rgba(99,102,241,0.18)';

  return (
    <div
      style={{
        position: 'absolute',
        top: index * CHIP_SPACING,
        left: 0,
        transform: `translateX(${x}px)`,
        opacity,
        display: 'inline-flex',
        alignItems: 'center',
        gap: 10,
        padding: '10px 16px',
        fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
        fontSize: 13,
        letterSpacing: 1.5,
        textTransform: 'uppercase',
        color: colour,
        backgroundColor: fill,
        border: `1px solid ${colour}`,
        borderRadius: 999,
        boxShadow: glow,
        whiteSpace: 'nowrap',
      }}
    >
      <ChipBullet tone={chip.tone} />
      {chip.label}
    </div>
  );
}

function ChipBullet({ tone }: { tone: 'accent' | 'success' }) {
  const colour = tone === 'success' ? C.success : C.accent;
  if (tone === 'success') {
    return (
      <svg width="13" height="13" viewBox="0 0 14 14" fill="none">
        <path d="M 2 7 L 6 11 L 12 3" stroke={colour} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" fill="none" />
      </svg>
    );
  }
  return (
    <span style={{ display: 'inline-block', width: 6, height: 6, borderRadius: 3, backgroundColor: colour }} />
  );
}

// ---------- OAuthFlow ----------

function OAuthFlow({ localFrame, fps }: { localFrame: number; fps: number }) {
  const headerIn = clampLerp(localFrame, OAUTH_START - 20, OAUTH_START);

  return (
    <>
      {/* Plain-language header. The technical name sits small underneath
          so anyone curious can google it; nobody is forced to know it. */}
      <div
        style={{
          position: 'absolute',
          left: 1100,
          top: OAUTH_HEADER_Y,
          width: 700,
          textAlign: 'center',
          opacity: headerIn,
        }}
      >
        <div
          style={{
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 16,
            letterSpacing: 5,
            textTransform: 'uppercase',
            color: C.fgSubtle,
          }}
        >
          only your token gets in
        </div>
        <div
          style={{
            marginTop: 6,
            fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
            fontSize: 11,
            letterSpacing: 2,
            color: C.fgSubtle,
            opacity: 0.6,
          }}
        >
          oauth 2.1 · proof-key flow
        </div>
      </div>

      {/* Three-stage flow, plain labels */}
      {OAUTH_STAGES.map((stage, i) => {
        const start = OAUTH_START + stage.t;
        const inProg = softSpring(localFrame, fps, start, 18);
        const op = clampLerp(localFrame, start, start + 12);
        if (op < 0.01) return null;
        return (
          <div
            key={i}
            style={{
              position: 'absolute',
              left: stage.x,
              top: stage.y,
              transform: `translate(-50%, -50%) scale(${interpolate(inProg, [0, 1], [0.78, 1])})`,
              opacity: op,
              fontFamily: 'ui-monospace, SF Mono, Menlo, monospace',
              fontSize: 13,
              letterSpacing: 1.5,
              textTransform: 'uppercase',
              color: C.fg,
              border: `1px solid ${C.accent}`,
              backgroundColor: 'rgba(99,102,241,0.10)',
              borderRadius: 10,
              padding: '10px 18px',
              boxShadow: `0 6px 18px rgba(0,0,0,0.5)`,
              whiteSpace: 'nowrap',
            }}
          >
            {stage.label}
          </div>
        );
      })}

      {/* Arrows between stages */}
      <OAuthArrows localFrame={localFrame} />

      {/* Final arrow from "scoped token" down-left INTO the server card —
          completes the loop: this token is what reaches your server. */}
      <SettlementArrow localFrame={localFrame} />
    </>
  );
}

function OAuthArrows({ localFrame }: { localFrame: number }) {
  return (
    <svg width={1920} height={1080} style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
      <defs>
        <marker id="arrowOauth" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
          <path d="M 0 0 L 10 5 L 0 10 Z" fill={C.accent} />
        </marker>
      </defs>
      {OAUTH_STAGES.slice(0, -1).map((stage, i) => {
        const next = OAUTH_STAGES[i + 1];
        const start = OAUTH_START + stage.t + 10;
        const end = OAUTH_START + next.t;
        const progress = clampLerp(localFrame, start, end);
        if (progress < 0.01) return null;
        const padFrom = 70;
        const padTo = 70;
        const x1 = stage.x + padFrom;
        const y1 = stage.y;
        const x2 = next.x - padTo;
        const y2 = next.y;
        const segLen = Math.hypot(x2 - x1, y2 - y1);
        return (
          <line
            key={i}
            x1={x1}
            y1={y1}
            x2={x2}
            y2={y2}
            stroke={C.accent}
            strokeWidth={1.6}
            strokeDasharray={segLen}
            strokeDashoffset={(1 - progress) * segLen}
            opacity={0.85}
            markerEnd="url(#arrowOauth)"
          />
        );
      })}
    </svg>
  );
}

function SettlementArrow({ localFrame }: { localFrame: number }) {
  // Drawn after the last OAuth stage lands. Curves from the rightmost
  // "scoped token" pill down and left toward the server card's right
  // edge, showing the token completing the round-trip into the server.
  const start = OAUTH_START + OAUTH_STAGES[OAUTH_STAGES.length - 1].t + 10;
  const reveal = clampLerp(localFrame, start, start + 30);
  if (reveal < 0.01) return null;
  const fromX = OAUTH_STAGES[OAUTH_STAGES.length - 1].x;
  const fromY = OAUTH_STAGES[OAUTH_STAGES.length - 1].y + 26;
  const toX = CARD_X + CARD_W / 2 + 8;
  const toY = CARD_Y - 10;
  // Bezier control points for a graceful S-curve.
  const cx1 = fromX;
  const cy1 = fromY + 240;
  const cx2 = toX + 220;
  const cy2 = toY + 30;
  const pathD = `M ${fromX} ${fromY} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${toX} ${toY}`;
  // Approximate path length for dash offset.
  const approxLen = 1200;
  return (
    <svg width={1920} height={1080} style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
      <path
        d={pathD}
        stroke={C.success}
        strokeWidth={1.7}
        strokeDasharray="7 6"
        fill="none"
        strokeDashoffset={(1 - reveal) * approxLen}
        opacity={0.85 * reveal}
      />
    </svg>
  );
}

// ---------- helpers ----------

function interpolateColor(a: string, b: string, t: number) {
  const pa = parseHex(a);
  const pb = parseHex(b);
  const r = Math.round(pa.r + (pb.r - pa.r) * t);
  const g = Math.round(pa.g + (pb.g - pa.g) * t);
  const bb = Math.round(pa.b + (pb.b - pa.b) * t);
  return `rgb(${r},${g},${bb})`;
}

function parseHex(h: string) {
  const s = h.replace('#', '');
  return {
    r: parseInt(s.slice(0, 2), 16),
    g: parseInt(s.slice(2, 4), 16),
    b: parseInt(s.slice(4, 6), 16),
  };
}
