// main.jsx — WhiteSpace animation root.
// Story: raw data in → MORPHS through the hero → structured outputs out.
//
// Timing (total 15s):
//   0.0 – 3.0    CHAOS: data sources appear scattered, raw scraps drift out of them (messy)
//   3.0 – 6.5    TRANSFORM: scraps stream INTO hero, hero ignites, OUTPUTS emerge transformed
//   6.5 – 10.5   CONSTELLATION: feature cards assemble, each catching its output stream
//   10.5 – 15.0  HOLD: tagline lands, system breathes

const W = 1920, H = 1080;
const CX = W / 2, CY = H / 2;

// ─── Chaos phase: sources spread out, raw scraps flicker around them ───────

const SOURCE_POSITIONS = [
  { x: 140,  y: 260 },
  { x: 140,  y: 430 },
  { x: 140,  y: 600 },
  { x: 140,  y: 770 },
  { x: 480,  y: 310 },
  { x: 480,  y: 660 },
];

function ChaosPhase() {
  const t = useTime();
  if (t > 7.0) return null; // sources fully gone after transform

  const items = [];

  // Cards — 6 sources, left side
  for (let i = 0; i < DATA_SOURCES.length; i++) {
    const src = DATA_SOURCES[i];
    const pos = SOURCE_POSITIONS[i];
    const appearAt = 0.15 + i * 0.15;
    const dissolveAt = 4.0 + i * 0.08;

    if (t < appearAt) continue;

    const entryT = Easing.easeOutCubic(clamp((t - appearAt) / 0.6, 0, 1));
    const dissolveT = Easing.easeInCubic(clamp((t - dissolveAt) / 1.0, 0, 1));
    const opacity = entryT * (1 - dissolveT);
    if (opacity <= 0.01) continue;

    // Slight float during chaos
    const bobY = Math.sin(t * 1.2 + i * 0.9) * 4;

    items.push(
      <div key={`src${i}`} style={{
        position: 'absolute',
        left: pos.x, top: pos.y + bobY,
        opacity,
        transform: `scale(${0.85 + 0.15 * entryT}) translateX(${-20 * (1 - entryT) - 40 * dissolveT}px)`,
        transformOrigin: 'left center',
        willChange: 'transform, opacity',
      }}>
        <Card data={src} width={320} />
      </div>
    );
  }

  return <>{items}</>;
}

// ─── Raw scrap emission — scraps float out of source cards, then stream to hero ───

function RawScrapStream() {
  const t = useTime();

  // Each scrap has: spawn time, source index, jitter offset, transform path
  // Phase A (before 3.0): scrap appears near its source, drifts chaotically
  // Phase B (3.0 – 5.0): scrap streams TO the hero center
  // Phase C (at ~5.0): scrap is absorbed (fades, implying conversion)

  const scraps = [];

  for (let i = 0; i < RAW_SCRAPS.length; i++) {
    const scrap = RAW_SCRAPS[i];
    const srcIdx = i % SOURCE_POSITIONS.length;
    const src = SOURCE_POSITIONS[srcIdx];

    const spawnAt = 0.8 + i * 0.18;
    const streamAt = 3.0 + i * 0.08;
    const absorbAt = streamAt + 1.2;

    if (t < spawnAt || t > absorbAt + 0.2) continue;

    // Origin near source card (right edge)
    const ox = src.x + 320 + 20;
    const oy = src.y + 30 + (i % 3) * 18;

    // Chaotic drift position during phase A
    const driftX = ox + Math.sin(t * 1.5 + i * 2) * 30 + (t - spawnAt) * 18;
    const driftY = oy + Math.cos(t * 1.3 + i) * 12;

    let x = driftX, y = driftY, op = 1, scale = 1;

    // Entry fade
    const entryT = clamp((t - spawnAt) / 0.4, 0, 1);
    op = entryT;

    if (t >= streamAt) {
      // Stream toward hero
      const streamT = Easing.easeInCubic(clamp((t - streamAt) / 1.2, 0, 1));
      const targetX = CX;
      const targetY = CY;
      x = driftX + (targetX - driftX) * streamT;
      y = driftY + (targetY - driftY) * streamT;
      scale = 1 - streamT * 0.5;
      // Fade out as absorbed
      if (streamT > 0.75) {
        op = (1 - streamT) / 0.25;
      }
    }

    scraps.push(
      <div key={`scrap${i}`} style={{
        position: 'absolute',
        left: x, top: y,
        opacity: op,
        transform: `scale(${scale}) translate(-50%, -50%)`,
        willChange: 'transform, opacity',
      }}>
        <RawScrap scrap={scrap} />
      </div>
    );
  }

  return <>{scraps}</>;
}

// ─── Hero phase ─────────────────────────────────────────────────────────────

function HeroPhase() {
  const t = useTime();
  const appearAt = 3.2;
  if (t < appearAt) return null;

  const appearT = Easing.easeOutBack(clamp((t - appearAt) / 0.7, 0, 1));

  // Stats tick while scraps are streaming in (3.5 – 5.5)
  const statsProgress = Easing.easeOutCubic(clamp((t - 3.8) / 2.2, 0, 1));

  // Glow ramps up during transform, stays high during constellation, gentle pulse after
  let glow = 0;
  if (t < 5.0) {
    glow = Easing.easeOutQuad(clamp((t - appearAt) / 1.5, 0, 1)) * 0.5;
  } else if (t < 6.5) {
    glow = 0.5 + Easing.easeOutQuad(clamp((t - 5.0) / 1.5, 0, 1)) * 0.5; // peak at ignition
  } else {
    glow = 0.85 + Math.sin(t * 1.2) * 0.1;
  }

  // Scale ignition pulse at ~5.0-5.5
  let pulse = 0;
  if (t > 4.9 && t < 5.7) {
    const pt = (t - 4.9) / 0.8;
    pulse = Math.sin(pt * Math.PI) * 0.06;
  }

  let breath = 0;
  if (t > 10.5) breath = Math.sin((t - 10.5) * 1.2) * 0.012;

  const scale = appearT * (1 + pulse + breath);

  return (
    <>
      {/* Radial ignition burst */}
      {t > 4.9 && t < 6.0 && (
        <div style={{
          position: 'absolute',
          left: CX, top: CY,
          width: 800, height: 800,
          transform: 'translate(-50%, -50%)',
          background: 'radial-gradient(circle, oklch(80% 0.18 240 / 0.25) 0%, transparent 55%)',
          opacity: Math.sin(clamp((t - 4.9) / 1.1, 0, 1) * Math.PI),
          pointerEvents: 'none',
          filter: 'blur(4px)',
        }}/>
      )}
      <div style={{
        position: 'absolute',
        left: CX, top: CY,
        transform: 'translate(-50%, -50%)',
      }}>
        <HeroCard scale={scale} statsProgress={statsProgress} glow={glow} />
      </div>
    </>
  );
}

// ─── Output emission — structured chips stream OUT of hero toward feature cards ───

const OUTPUT_PATHS = [
  // Each: when it emerges, kind, angle (rad) from center, distance
  { kind: 'answer',    emitAt: 5.2, targetIdx: 0 },
  { kind: 'briefing',  emitAt: 5.3, targetIdx: 1 },
  { kind: 'alert',     emitAt: 5.4, targetIdx: 2 },
  { kind: 'chart',     emitAt: 5.5, targetIdx: 3 },
  { kind: 'sentiment', emitAt: 5.6, targetIdx: 5 }, // stock
  { kind: 'promo',     emitAt: 5.7, targetIdx: 6 }, // growth
];

function featureLayout() {
  // Right column (4 cards)
  const rightX = CX + 220;
  const leftX = CX - 220 - 340;
  const rightCol = FEATURES.slice(0, 4).map((f, i) => ({
    data: f, x: rightX, y: CY - 440 + i * 230,
  }));
  const leftCol = FEATURES.slice(4, 8).map((f, i) => ({
    data: f, x: leftX, y: CY - 440 + i * 230,
  }));
  return [...rightCol, ...leftCol];
}

function OutputStream() {
  const t = useTime();
  if (t < 5.0 || t > 8.5) return null;

  const layout = featureLayout();
  const chips = [];

  for (let i = 0; i < OUTPUT_PATHS.length; i++) {
    const path = OUTPUT_PATHS[i];
    const target = layout[path.targetIdx];
    if (!target) continue;

    const travelDur = 1.2;
    const fadeOutDur = 0.3;
    const localT = t - path.emitAt;
    if (localT < 0 || localT > travelDur + fadeOutDur) continue;

    const travelT = Easing.easeOutCubic(clamp(localT / travelDur, 0, 1));
    const fadeT = clamp((localT - travelDur) / fadeOutDur, 0, 1);

    const tx = target.x + 170; // card center
    const ty = target.y + 40;

    const x = CX + (tx - CX) * travelT;
    const y = CY + (ty - CY) * travelT;

    // Entry fade
    const entryT = clamp(localT / 0.25, 0, 1);
    const op = entryT * (1 - fadeT);
    const scale = 0.7 + 0.3 * entryT;

    chips.push(
      <div key={i} style={{
        position: 'absolute',
        left: x, top: y,
        transform: `translate(-50%, -50%) scale(${scale})`,
        opacity: op,
        willChange: 'transform, opacity',
      }}>
        <OutputChip kind={path.kind} />
      </div>
    );
  }

  return <>{chips}</>;
}

// ─── Constellation phase — features assemble ────────────────────────────────

function ConstellationPhase() {
  const t = useTime();
  const startAt = 6.4;
  if (t < startAt) return null;

  const layout = featureLayout();

  return (
    <>
      {layout.map((f, i) => {
        const delay = (i % 4) * 0.1 + (i >= 4 ? 0.4 : 0);
        const appearAt = startAt + delay;
        if (t < appearAt) return null;

        const flyT = Easing.easeOutCubic(clamp((t - appearAt) / 0.7, 0, 1));
        const originX = CX - 170;
        const originY = CY - 80;
        const curX = originX + (f.x - originX) * flyT;
        const curY = originY + (f.y - originY) * flyT;

        const scale = 0.65 + 0.35 * flyT;
        const opacity = flyT;

        const arrivedT = clamp((t - appearAt - 0.7) / 0.4, 0, 1);
        const glow = flyT < 1
          ? 0.2 * flyT
          : 0.3 + Easing.easeOutQuad(arrivedT) * 0.4 + Math.sin((t + i) * 1.1) * 0.06;

        return (
          <React.Fragment key={f.data.id}>
            <ConnectionLine
              x1={CX + (f.x + 170 > CX ? 130 : -130)} y1={CY}
              x2={f.x + 170} y2={f.y + 40}
              color={f.data.accent}
              progress={flyT}
            />
            <div style={{
              position: 'absolute',
              left: curX, top: curY,
              transform: `scale(${scale})`,
              transformOrigin: 'center',
              opacity,
              willChange: 'transform, opacity',
            }}>
              <Card data={f.data} width={340} glow={clamp(glow, 0, 1)} badge={f.data.badge} />
            </div>
          </React.Fragment>
        );
      })}
    </>
  );
}

// ─── Section labels ─────────────────────────────────────────────────────────

function SectionLabels() {
  const t = useTime();
  const showSources = t > 0.3 && t < 4.5;
  const showWhat = t > 7.2;

  const sourcesOp = showSources
    ? (t < 1 ? Easing.easeOutCubic(clamp((t - 0.3) / 0.5, 0, 1)) : t > 4 ? 1 - Easing.easeInCubic(clamp((t - 4) / 0.5, 0, 1)) : 1)
    : 0;

  const whatOp = showWhat
    ? Easing.easeOutCubic(clamp((t - 7.2) / 0.6, 0, 1))
    : 0;

  const lblStyle = {
    position: 'absolute',
    fontSize: 11,
    letterSpacing: '0.28em',
    color: 'rgba(246,244,239,0.5)',
    fontWeight: 500,
  };

  return (
    <>
      <div style={{ ...lblStyle, left: 140, top: 180, opacity: sourcesOp }}>RAW · MESSY · EVERYWHERE</div>
    </>
  );
}

// ─── Captions ───────────────────────────────────────────────────────────────

function OpeningCaption() {
  const t = useTime();
  if (t > 3.2) return null;
  const op = t < 0.5
    ? Easing.easeOutCubic(clamp(t / 0.5, 0, 1))
    : t > 2.7
      ? 1 - Easing.easeInCubic(clamp((t - 2.7) / 0.5, 0, 1))
      : 1;
  const ty = (1 - op) * 10;

  return (
    <div style={{
      position: 'absolute',
      left: '50%', top: 90,
      transform: `translate(-50%, ${ty}px)`,
      opacity: op, textAlign: 'center',
      fontSize: 34,
      color: '#f6f4ef',
      fontWeight: 300,
      letterSpacing: '-0.02em',
    }}>
      You have the <span style={{ fontFamily: '"Instrument Serif", Georgia, serif', fontStyle: 'italic' }}>data</span>.
    </div>
  );
}

function MidCaption() {
  const t = useTime();
  if (t < 4.2 || t > 6.8) return null;
  const op = t < 4.8
    ? Easing.easeOutCubic(clamp((t - 4.2) / 0.6, 0, 1))
    : t > 6.3
      ? 1 - Easing.easeInCubic(clamp((t - 6.3) / 0.5, 0, 1))
      : 1;

  return (
    <div style={{
      position: 'absolute',
      left: '50%', top: 90,
      transform: 'translate(-50%, 0)',
      opacity: op, textAlign: 'center',
      fontSize: 34,
      color: '#f6f4ef',
      fontWeight: 300,
      letterSpacing: '-0.02em',
    }}>
      We turn it into <span style={{ fontFamily: '"Instrument Serif", Georgia, serif', fontStyle: 'italic', color: 'oklch(82% 0.16 90)' }}>understanding</span>.
    </div>
  );
}

function TaglineOverlay() {
  const t = useTime();
  if (t < 10.8) return null;
  const op = Easing.easeOutCubic(clamp((t - 10.8) / 0.8, 0, 1));
  const ty = (1 - op) * 14;

  return (
    <>
      <div style={{
        position: 'absolute',
        left: '50%', top: 80,
        transform: `translate(-50%, ${ty}px)`,
        opacity: op, textAlign: 'center',
        fontSize: 13,
        letterSpacing: '0.32em',
        color: 'oklch(75% 0.19 55)',
        fontWeight: 600,
      }}>
        ANSWERS · ACTIONS · ALERTS
      </div>
      <div style={{
        position: 'absolute',
        left: '50%', bottom: 60,
        transform: `translate(-50%, ${ty}px)`,
        opacity: op, textAlign: 'center',
      }}>
        <div style={{
          fontSize: 40,
          color: '#f6f4ef',
          letterSpacing: '-0.02em',
          fontWeight: 300,
        }}>
          Every number. <span style={{ fontFamily: '"Instrument Serif", Georgia, serif', fontStyle: 'italic', fontWeight: 400, color: 'oklch(75% 0.19 55)' }}>One brain.</span>
        </div>
      </div>
    </>
  );
}

// ─── Debug timestamp label ──────────────────────────────────────────────────

function TimestampLabel() {
  const t = useTime();
  const sec = Math.floor(t);
  React.useEffect(() => {
    const root = document.querySelector('[data-anim-root]');
    if (root) root.setAttribute('data-screen-label', `t=${sec.toFixed(0)}s`);
  }, [sec]);
  return null;
}

// ─── Root ──────────────────────────────────────────────────────────────────

function WhiteSpaceAnimation() {
  const t = useTime();
  const bgBrightness = t < 3.0
    ? 0.3 + Math.sin(t * 2) * 0.08
    : t < 6.5
      ? 0.4 + Easing.easeOutCubic(clamp((t - 3.0) / 3.5, 0, 1)) * 0.3
      : 0.8 + Math.sin(t * 0.5) * 0.08;

  return (
    <div data-anim-root style={{ position: 'absolute', inset: 0, background: '#08080a', overflow: 'hidden' }}>
      <ShaderBackground intensity={0.55} />
      <GridBackground intensity={bgBrightness} />

      {t > 4.5 && (
        <div style={{
          position: 'absolute',
          left: CX, top: CY,
          width: 900, height: 900,
          transform: 'translate(-50%, -50%)',
          background: 'radial-gradient(circle, oklch(80% 0.14 240 / 0.08) 0%, transparent 60%)',
          opacity: clamp((t - 4.5) / 1.5, 0, 1),
          pointerEvents: 'none',
        }}/>
      )}

      <SectionLabels />
      <ChaosPhase />
      <RawScrapStream />
      <HeroPhase />
      <OutputStream />
      <ConstellationPhase />
      <OpeningCaption />
      <MidCaption />
      <TaglineOverlay />
      <TimestampLabel />
    </div>
  );
}

const DEFAULT_TWEAKS = /*EDITMODE-BEGIN*/{
  "speed": 1.0
}/*EDITMODE-END*/;

function App() {
  const [tweaks, setTweaks] = React.useState(DEFAULT_TWEAKS);
  const [editMode, setEditMode] = React.useState(false);

  React.useEffect(() => {
    const onMsg = (e) => {
      const d = e.data || {};
      if (d.type === '__activate_edit_mode') setEditMode(true);
      else if (d.type === '__deactivate_edit_mode') setEditMode(false);
    };
    window.addEventListener('message', onMsg);
    window.parent.postMessage({ type: '__edit_mode_available' }, '*');
    return () => window.removeEventListener('message', onMsg);
  }, []);

  const updateTweak = (k, v) => {
    setTweaks(prev => {
      const next = { ...prev, [k]: v };
      window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { [k]: v } }, '*');
      return next;
    });
  };

  const duration = 15 / tweaks.speed;

  return (
    <>
      <Stage width={W} height={H} duration={duration} background="#08080a" persistKey="whitespace-anim" hideControls={window !== window.parent}>
        <WhiteSpaceAnimation />
      </Stage>
      {editMode && <TweaksPanel tweaks={tweaks} onChange={updateTweak} />}
    </>
  );
}

function TweaksPanel({ tweaks, onChange }) {
  const row = { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, marginBottom: 12 };
  const label = { fontSize: 11, color: 'rgba(246,244,239,0.7)', letterSpacing: '0.08em', textTransform: 'uppercase' };
  return (
    <div style={{
      position: 'fixed', bottom: 70, right: 20, width: 260,
      background: 'rgba(16,16,20,0.95)',
      border: '1px solid rgba(255,255,255,0.1)',
      borderRadius: 10, padding: 16,
      color: '#f6f4ef',
      fontFamily: 'Inter, system-ui, sans-serif',
      zIndex: 1000, backdropFilter: 'blur(12px)',
    }}>
      <div style={{ fontSize: 12, letterSpacing: '0.2em', color: 'rgba(246,244,239,0.5)', marginBottom: 14 }}>TWEAKS</div>
      <div style={row}>
        <span style={label}>Speed</span>
        <input type="range" min="0.5" max="2" step="0.1" value={tweaks.speed} onChange={e => onChange('speed', parseFloat(e.target.value))} style={{ flex: 1 }}/>
        <span style={{ fontSize: 11, width: 30, textAlign: 'right' }}>{tweaks.speed}×</span>
      </div>
      <div style={{ fontSize: 10, color: 'rgba(246,244,239,0.35)', lineHeight: 1.4, marginTop: 8 }}>
        Space: play/pause · ←/→ seek · 0 reset
      </div>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
