// video-components.jsx
// Equiti+ explainer video — reusable scene components.
// Load AFTER animations.jsx (which defines Stage, Sprite, etc).

const BRAND = {
  teal: '#00AFAB',
  tealSoft: '#CCF7F3',
  tealDim: 'rgba(0,175,171,0.14)',
  black: '#0E1420',
  blue: '#2E90FA',
  error: '#F04438',
  warning: '#F79009',
  success: '#17B26A',
  bg: '#0E1420',
  bgSoft: '#182230',
  white: '#FFFFFF',
  text: '#FFFFFF',
  textMuted: 'rgba(255,255,255,0.6)',
  textDim: 'rgba(255,255,255,0.4)',
  border: 'rgba(255,255,255,0.08)',
  borderStrong: 'rgba(255,255,255,0.18)',
};

const FONTS = {
  display: "'Gilroy', 'Inter', system-ui, sans-serif",
  body: "'Gilroy', 'Inter', system-ui, sans-serif",
  mono: "'Gilroy', 'JetBrains Mono', ui-monospace, monospace",
};

// ── Background layers ────────────────────────────────────────────────────
function SceneBg({ color = BRAND.bg, children }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: color,
      overflow: 'hidden',
    }}>
      {children}
    </div>
  );
}

function GridBg({ opacity = 0.05, size = 80 }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      backgroundImage:
        `linear-gradient(${BRAND.teal} 1px, transparent 1px), linear-gradient(90deg, ${BRAND.teal} 1px, transparent 1px)`,
      backgroundSize: `${size}px ${size}px`,
      opacity,
    }} />
  );
}

// Radial teal glow that drifts
function TealGlow({ x = 50, y = 50, size = 800 }) {
  const t = useTime();
  const drift = Math.sin(t * 0.3) * 40;
  return (
    <div style={{
      position: 'absolute',
      left: `${x}%`, top: `${y}%`,
      width: size, height: size,
      transform: `translate(-50%, -50%) translateX(${drift}px)`,
      background: `radial-gradient(circle, rgba(0,175,171,0.25) 0%, rgba(0,175,171,0) 60%)`,
      filter: 'blur(10px)',
      pointerEvents: 'none',
    }} />
  );
}

// ── Basic text elements with fade/slide ────────────────────────────────
// Fade in on entry, hold, fade on exit. Uses useSprite progress.
function FadeText({
  children,
  x = 0, y = 0,
  width = 'auto',
  size = 48,
  weight = 600,
  color = BRAND.text,
  font = FONTS.display,
  align = 'left',
  letterSpacing = '-0.01em',
  lineHeight = 1.1,
  entryDur = 0.5,
  exitDur = 0.4,
  style: extra = {},
}) {
  const { localTime, duration } = useSprite();
  const exitStart = Math.max(0, duration - exitDur);

  let opacity = 1;
  let ty = 0;

  if (localTime < entryDur) {
    const t = Easing.easeOutCubic(clamp(localTime / entryDur, 0, 1));
    opacity = t;
    ty = (1 - t) * 20;
  } else if (localTime > exitStart) {
    const t = Easing.easeInCubic(clamp((localTime - exitStart) / exitDur, 0, 1));
    opacity = 1 - t;
    ty = -t * 10;
  }

  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      width,
      textAlign: align,
      transform: `translateY(${ty}px)`,
      opacity,
      fontFamily: font,
      fontSize: size,
      fontWeight: weight,
      color,
      letterSpacing,
      lineHeight,
      willChange: 'transform, opacity',
      ...extra,
    }}>
      {children}
    </div>
  );
}

// Eyebrow (small label over a title)
function Eyebrow({ children, x, y, color = BRAND.teal }) {
  return (
    <FadeText x={x} y={y} size={22} weight={700} color={color} font={FONTS.body}
      letterSpacing="0.18em" lineHeight={1}
      style={{ textTransform: 'uppercase' }}>
      {children}
    </FadeText>
  );
}

// ── Counter (count-up number) ────────────────────────────────────────
function Counter({ x, y, from = 0, to = 100, suffix = '', prefix = '', size = 140, color = BRAND.teal, duration: dur = 1.2, decimals = 0, weight = 800 }) {
  const { localTime, duration: spriteDur } = useSprite();
  // Count runs in first `dur` seconds of sprite
  const t = Easing.easeOutQuart(clamp(localTime / dur, 0, 1));
  const val = from + (to - from) * t;

  // Entry fade
  const entryDur = 0.35;
  const exitDur = 0.4;
  const exitStart = Math.max(0, spriteDur - exitDur);
  let opacity = 1;
  if (localTime < entryDur) opacity = Easing.easeOutCubic(clamp(localTime / entryDur, 0, 1));
  else if (localTime > exitStart) opacity = 1 - Easing.easeInCubic(clamp((localTime - exitStart) / exitDur, 0, 1));

  const display = decimals > 0 ? val.toFixed(decimals) : Math.round(val).toLocaleString();
  return (
    <div style={{
      position: 'absolute', left: x, top: y,
      fontFamily: FONTS.display,
      fontWeight: weight,
      fontSize: size,
      color,
      letterSpacing: '-0.03em',
      lineHeight: 1,
      opacity,
      fontVariantNumeric: 'tabular-nums',
    }}>
      {prefix}{display}<span style={{ color: BRAND.textMuted, fontSize: size * 0.5, marginLeft: 4 }}>{suffix}</span>
    </div>
  );
}

// ── Equiti+ logo (two modes: wordmark image, or typed letters) ─────────
function EquitiLogo({
  x, y, width = 600,
  style: modeProp, // 'wordmark' or 'typed' — defaults to window.__equitiLogoMode
  showPlus = true,
  plusColor = BRAND.teal,
  color = BRAND.white,
}) {
  const mode = modeProp || (typeof window !== 'undefined' ? window.__equitiLogoMode : null) || 'wordmark';
  const { localTime, duration } = useSprite();

  if (mode === 'wordmark') {
    // Wordmark PNG fades/scales in, then the + bounces in
    const wordEntry = 0.6;
    const wordT = Easing.easeOutCubic(clamp(localTime / wordEntry, 0, 1));
    const wordOp = wordT;
    const wordScale = 0.88 + 0.12 * wordT;

    const plusStart = 0.55;
    const plusT = Easing.easeOutBack(clamp((localTime - plusStart) / 0.55, 0, 1));
    const plusOp = clamp((localTime - plusStart) / 0.35, 0, 1);

    // PNG is 1841×416 with visible glyphs ending at x=1361 (26% right-side padding).
    // Don't crop — just compute where the last "i" actually ends and put "+" right after.
    const PNG_W = 1841, PNG_H = 416, GLYPH_END = 1361;
    const visibleRatio = GLYPH_END / PNG_W; // ~0.739
    const h = width / (PNG_W / PNG_H); // natural height for full PNG at `width`
    const glyphEndX = width * visibleRatio; // where the last "i" ends in our box
    const plusSize = h * 1.1;
    const plusGap = plusSize * 0.05; // small visual gap

    return (
      <div style={{
        position: 'absolute', left: x, top: y,
        width, height: h,
      }}>
        <img src="assets/logos/equiti-wordmark-white.png"
          style={{
            width: '100%', height: '100%',
            objectFit: 'fill',
            display: 'block',
            opacity: wordOp,
            transform: `scale(${wordScale})`,
            transformOrigin: 'left center',
            filter: color === BRAND.white ? 'none' : 'brightness(0.4)',
          }}/>
        {showPlus && (
          <span style={{
            position: 'absolute',
            left: glyphEndX + plusGap,
            top: '50%',
            transform: `translateY(-58%) scale(${0.35 + 0.65 * plusT}) rotate(${(1 - plusT) * 60}deg)`,
            transformOrigin: 'left center',
            color: plusColor,
            opacity: plusOp,
            fontFamily: FONTS.display,
            fontWeight: 900,
            fontSize: plusSize,
            lineHeight: 1,
            textShadow: `0 0 40px ${plusColor}66`,
          }}>+</span>
        )}
      </div>
    );
  }

  // Typed / letter-by-letter fallback
  const letters = 'equiti'.split('');
  const entryDur = 0.08;
  return (
    <div style={{
      position: 'absolute', left: x, top: y,
      display: 'flex', alignItems: 'baseline',
      fontFamily: FONTS.display,
      fontWeight: 800,
      fontSize: 160,
      letterSpacing: '-0.06em',
      color,
      lineHeight: 1,
    }}>
      {letters.map((L, i) => {
        const start = i * entryDur;
        const t = Easing.easeOutBack(clamp((localTime - start) / 0.35, 0, 1));
        return (
          <span key={i} style={{
            opacity: t,
            transform: `translateY(${(1 - t) * 30}px)`,
            display: 'inline-block',
          }}>{L}</span>
        );
      })}
      {showPlus && (() => {
        const plusStart = letters.length * entryDur + 0.1;
        const t = Easing.easeOutBack(clamp((localTime - plusStart) / 0.45, 0, 1));
        return (
          <span style={{
            color: plusColor,
            opacity: t,
            transform: `scale(${0.4 + 0.6 * t}) rotate(${(1 - t) * 45}deg)`,
            display: 'inline-block',
            marginLeft: -8,
            fontWeight: 900,
          }}>+</span>
        );
      })()}
    </div>
  );
}

// ── Phone frame ────────────────────────────────────────
function PhoneFrame({ x, y, width = 420, scale = 1, children, entry = true }) {
  const { localTime, duration } = useSprite();
  const h = width * 2.05;
  const entryDur = 0.6;
  const exitDur = 0.5;
  const exitStart = Math.max(0, duration - exitDur);

  let opacity = 1, ty = 0, s = 1;
  if (entry) {
    if (localTime < entryDur) {
      const t = Easing.easeOutCubic(clamp(localTime / entryDur, 0, 1));
      opacity = t;
      ty = (1 - t) * 60;
      s = 0.94 + 0.06 * t;
    } else if (localTime > exitStart) {
      const t = Easing.easeInCubic(clamp((localTime - exitStart) / exitDur, 0, 1));
      opacity = 1 - t;
      ty = -t * 20;
    }
  }

  return (
    <div style={{
      position: 'absolute',
      left: x, top: y,
      width: width * scale,
      height: h * scale,
      transform: `translateY(${ty}px) scale(${s * scale})`,
      transformOrigin: 'center center',
      opacity,
      willChange: 'transform, opacity',
    }}>
      <div style={{
        width: width, height: h,
        transform: scale !== 1 ? `scale(${scale})` : 'none',
        transformOrigin: 'top left',
        position: 'relative',
        background: '#000',
        borderRadius: 58,
        padding: 12,
        boxShadow: '0 40px 80px -20px rgba(0,0,0,0.7), 0 0 0 2px rgba(255,255,255,0.06) inset',
      }}>
        {/* Notch */}
        <div style={{
          position: 'absolute',
          top: 18, left: '50%',
          transform: 'translateX(-50%)',
          width: 140, height: 34,
          background: '#000',
          borderRadius: 20,
          zIndex: 10,
        }}/>
        <div style={{
          width: '100%', height: '100%',
          borderRadius: 46,
          overflow: 'hidden',
          position: 'relative',
          background: BRAND.bg,
        }}>
          {children}
        </div>
      </div>
    </div>
  );
}

// VerticalSlide — scene-level transition. Slides content up from below on entry,
// continues sliding up on exit so outgoing + incoming stack like a vertical pan.
function VerticalSlide({ children, entryDur = 0.6, exitDur = 0.6, distance = 1080 }) {
  const { localTime, duration } = useSprite();
  const exitStart = Math.max(0, duration - exitDur);
  let ty = 0;
  if (localTime < entryDur) {
    const t = Easing.easeOutCubic(clamp(localTime / entryDur, 0, 1));
    ty = (1 - t) * distance;        // from +1080 → 0
  } else if (localTime > exitStart) {
    const t = Easing.easeInCubic(clamp((localTime - exitStart) / exitDur, 0, 1));
    ty = -t * distance;             // from 0 → -1080
  }
  return (
    <div style={{
      position: 'absolute', inset: 0,
      transform: `translateY(${ty}px)`,
      willChange: 'transform',
    }}>{children}</div>
  );
}

Object.assign(window, {
  BRAND, FONTS,
  SceneBg, GridBg, TealGlow,
  FadeText, Eyebrow, Counter,
  EquitiLogo, PhoneFrame, VerticalSlide,
});
