/* kit/rosto.jsx — o rosto de quem fala, que muda de forma (camadas.js).
 *
 *   "o vídeo da pessoa é contínuo, mas o ROSTO muda de forma: tela cheia,
 *    cartão com borda que brilha, bolinha no canto, some" (Joel, 14/10)
 *
 * Duas peças:
 *  - palco: o fundo escuro com brilho que respira e grão (o que aparece
 *    quando o rosto não ocupa a tela);
 *  - rosto: a gravação dentro de uma caixa que vai de `antes` para `agora`
 *    com mola (kit/natural.js): recua um tico (antecipação), vai, passa do
 *    ponto e assenta. A gravação começa em `deOrigem` e NÃO reinicia: cada
 *    cena pega do ponto em que a anterior parou, e o áudio dela é a fala do
 *    vídeo. Quando "some", o vídeo continua tocando, só invisível.
 * A geometria (onde fica cada caixa) vem pronta do camadas.js; aqui só se
 * interpola. Nada de sorteio: o mesmo quadro sai igual sempre. */
import React from 'react';
import { AbsoluteFill, Video, staticFile, useVideoConfig } from 'remotion';
import { mola, ruido } from './natural';

const lerp = (a, b, k) => a + (b - a) * k;
const clamp = (x, a, b) => Math.max(a, Math.min(b, x));
const CAMPOS = ['x', 'y', 'w', 'h', 'raio', 'anel', 'zoom', 'fx', 'fy'];

/** A caixa no instante t (s) da cena. */
export function caixaNoTempo(el, t) {
  const B = el.agora || el.antes || {};
  const A = el.antes || B;
  if (!el.mudou) return { ...B };
  const s = t - 0.05;
  // A mola com peso: um pouco de sobra, e assenta em ~0,6 s.
  const k = mola(s, { freq: 1.55, amort: 0.62 });
  const kc = clamp(k, 0, 1);
  // A antecipação: antes de ir, a caixa encolhe um tico (o "respiro" do gesto).
  const prep = s > 0 && s < 0.14 ? Math.sin(Math.PI * (s / 0.14)) * 0.035 : 0;
  const o = {};
  for (const c of CAMPOS) o[c] = lerp(A[c] ?? B[c] ?? 0, B[c] ?? A[c] ?? 0, k);
  o.raio = Math.max(0, o.raio);
  o.anel = Math.max(0, o.anel);
  o.w = Math.max(1, o.w); o.h = Math.max(1, o.h);
  o.opacidade = lerp(A.opacidade ?? 1, B.opacidade ?? 1, kc);
  o.escala = Math.max(0, lerp(A.escala ?? 1, B.escala ?? 1, k)) * (1 - prep);
  return o;
}

export function Rosto({ el, t, fps }) {
  const c = caixaNoTempo(el, t);
  const ac = el.acento || '#8B7CF6';
  const origem = el.origem || {};
  const sw = origem.largura || 1080, sh = origem.altura || 1920;
  // A gravação cobre a caixa (cover) com o zoom, e o rosto (fx, fy) vai para o centro, acima do meio (o queixo longe da legenda).
  const s = Math.max(c.w / sw, c.h / sh) * Math.max(1, c.zoom || 1);
  const vw = sw * s, vh = sh * s;
  const left = clamp(c.w * 0.5 - (c.fx ?? 0.5) * vw, c.w - vw, 0);
  const top = clamp(c.h * 0.38 - (c.fy ?? 0.4) * vh, c.h - vh, 0);
  // A bolinha e o cartão flutuam de leve: parado de todo parece colado.
  const flutua = el.forma === 'bolinha' || el.forma === 'cartao' ? ruido(t * 0.35, 11) * 4 : 0;
  const anel = c.anel;
  const brilho = anel > 0
    ? `0 0 ${Math.round(18 + anel * 5)}px ${ac}88, 0 0 ${Math.round(60 + anel * 10)}px ${ac}33, 0 26px 70px rgba(0,0,0,.6)`
    : 'none';
  return (
    <div style={{
      position: 'absolute', left: c.x, top: c.y + flutua, width: c.w, height: c.h,
      transform: `scale(${c.escala})`, transformOrigin: '50% 50%', opacity: c.opacidade,
      borderRadius: c.raio, padding: anel,
      // A borda que brilha: um degradê do acento ao branco, por trás do vídeo.
      background: anel > 0 ? `linear-gradient(150deg, #ffffffcc, ${ac} 38%, ${ac}aa 70%, #ffffff88)` : 'transparent',
      boxShadow: brilho, boxSizing: 'border-box',
    }}>
      <div style={{ position: 'relative', width: '100%', height: '100%', overflow: 'hidden', borderRadius: Math.max(0, c.raio - anel), background: '#000' }}>
        <Video
          src={staticFile(el.src)}
          startFrom={el.deOrigem ? Math.round(el.deOrigem * fps) : undefined}
          volume={el.volume ?? 1}
          style={{ position: 'absolute', left, top, width: vw, height: vh, maxWidth: 'none' }}
        />
      </div>
    </div>
  );
}

/** O palco: preto azulado, dois brilhos que derivam devagar, vinheta e grão. */
export function Palco({ el, t }) {
  const { width: W, height: H } = useVideoConfig();
  const ac = el.acento || '#8B7CF6';
  const dx = ruido(t * 0.12, 3 + (el.semente || 0)) * 7;
  const dy = ruido(t * 0.1, 7 + (el.semente || 0)) * 5;
  const quadro = Math.floor(t * 24);
  const id = 'grao-' + (el.semente || 0);
  return (
    <AbsoluteFill style={{ background: '#07060c', overflow: 'hidden' }}>
      <div style={{ position: 'absolute', inset: 0, background:
        `radial-gradient(55% 38% at ${50 + dx}% ${58 + dy}%, ${ac}40, transparent 72%),`
        + `radial-gradient(45% 30% at ${16 - dx}% ${16 - dy}%, #4f46e526, transparent 70%),`
        + `radial-gradient(70% 45% at ${80 + dy}% ${96}%, #1d4ed838, transparent 72%)` }} />
      <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(120% 80% at 50% 45%, transparent 55%, rgba(0,0,0,.55) 100%)' }} />
      <svg width={W} height={H} style={{ position: 'absolute', inset: 0, opacity: el.grao ?? 0.13, mixBlendMode: 'screen' }}>
        <filter id={id}>
          <feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" seed={quadro % 61} stitchTiles="stitch" />
          <feColorMatrix type="matrix" values="0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 1.1 -0.45" />
        </filter>
        <rect width="100%" height="100%" filter={`url(#${id})`} />
      </svg>
    </AbsoluteFill>
  );
}

export const ROSTO = { rosto: Rosto, palco: Palco };
