/* fx3d/modelos.jsx — os objetos 3D que flutuam na mão, feitos de geometria.
 *
 * Nenhum arquivo de marca é baixado: as duas "logos" são DESENHADAS aqui,
 * como formas genéricas que lembram o tema — um nó hexagonal de seis elos
 * vazados (o "assistente verde") e um sol de raios irregulares (o
 * "assistente coral") — extrudadas com chanfro sobre uma placa arredondada,
 * como ícone de app. O cérebro é procedural: dois hemisférios de esfera
 * deformada, com os sulcos feitos de ruído.
 *
 * Todo modelo cabe numa caixa de ~1 unidade de lado, centrado na origem. */
import React, { useMemo } from 'react';
import * as THREE from 'three';

function placa(lado = 1.15, raio = 0.28) {
  const s = new THREE.Shape();
  const m = lado / 2;
  s.moveTo(-m + raio, -m);
  s.lineTo(m - raio, -m); s.quadraticCurveTo(m, -m, m, -m + raio);
  s.lineTo(m, m - raio); s.quadraticCurveTo(m, m, m - raio, m);
  s.lineTo(-m + raio, m); s.quadraticCurveTo(-m, m, -m, m - raio);
  s.lineTo(-m, -m + raio); s.quadraticCurveTo(-m, -m, -m + raio, -m);
  return s;
}

const EXT = (prof, chanfro = 0.035) => ({ depth: prof, bevelEnabled: true, bevelThickness: chanfro, bevelSize: chanfro, bevelSegments: 6, curveSegments: 36 });

function Placa({ cor }) {
  const g = useMemo(() => { const x = new THREE.ExtrudeGeometry(placa(), EXT(0.16, 0.05)); x.translate(0, 0, -0.2); return x; }, []);
  // Fosca o bastante para a cor aparecer: muito verniz e o reflexo da sala lava a placa para branco.
  return (
    <mesh geometry={g}>
      <meshPhysicalMaterial color={cor} roughness={0.5} metalness={0} clearcoat={0.6} clearcoatRoughness={0.35} />
    </mesh>
  );
}

/* Um elo: a cápsula VAZADA (contorno grosso). Seis delas, giradas de 60° em
   volta do centro e inclinadas, formam o nó hexagonal. */
function elo(comp = 0.62, larg = 0.24, esp = 0.065) {
  const capsula = (c, l) => {
    const s = new THREE.Path();
    const r = l / 2, m = c / 2 - r;
    s.moveTo(-m, -r); s.lineTo(m, -r);
    s.absarc(m, 0, r, -Math.PI / 2, Math.PI / 2, false);
    s.lineTo(-m, r);
    s.absarc(-m, 0, r, Math.PI / 2, (3 * Math.PI) / 2, false);
    return s;
  };
  const fora = new THREE.Shape(capsula(comp, larg).getPoints(48));
  fora.holes.push(new THREE.Path(capsula(comp - 2 * esp, larg - 2 * esp).getPoints(48).reverse()));
  return fora;
}

function NoHexagonal() {
  const g = useMemo(() => new THREE.ExtrudeGeometry(elo(), EXT(0.07, 0.018)), []);
  return (
    <group scale={[0.92, 0.92, 0.92]}>
      {Array.from({ length: 6 }, (_, k) => {
        const a = (k * Math.PI) / 3;
        return (
          <group key={k} rotation={[0, 0, a]}>
            <mesh geometry={g} position={[0.04, 0.2, 0.02 + (k % 2) * 0.012]} rotation={[0, 0, Math.PI / 3.2]}>
              <meshPhysicalMaterial color="#ffffff" roughness={0.18} metalness={0.1} clearcoat={1} clearcoatRoughness={0.08} />
            </mesh>
          </group>
        );
      })}
    </group>
  );
}

/* O sol de raios: onze raios de comprimento e largura um pouco diferentes
   (a irregularidade é o que faz parecer desenhado à mão, não engrenagem). */
function raio(comp, base, ponta) {
  const s = new THREE.Shape();
  s.moveTo(-base / 2, 0);
  s.lineTo(-ponta / 2, comp - ponta / 2);
  s.absarc(0, comp - ponta / 2, ponta / 2, Math.PI, 0, true);
  s.lineTo(base / 2, 0);
  s.lineTo(-base / 2, 0);
  return s;
}

function Sol({ cor }) {
  const raios = useMemo(() => {
    const comps = [0.47, 0.42, 0.5, 0.44, 0.48, 0.4, 0.49, 0.43, 0.46, 0.41, 0.5];
    return comps.map((c, k) => ({ g: new THREE.ExtrudeGeometry(raio(c, 0.07, 0.1 + (k % 3) * 0.012), EXT(0.08, 0.02)), a: (k / comps.length) * Math.PI * 2 + 0.12 * Math.sin(k * 2.3) }));
  }, []);
  return (
    <group position={[0, 0, 0.02]}>
      {raios.map((r, k) => (
        <mesh key={k} geometry={r.g} rotation={[0, 0, r.a]}>
          <meshPhysicalMaterial color={cor} roughness={0.35} metalness={0.05} clearcoat={0.8} clearcoatRoughness={0.2} />
        </mesh>
      ))}
      <mesh position={[0, 0, 0.05]}><cylinderGeometry args={[0.09, 0.09, 0.1, 48]} /><meshPhysicalMaterial color={cor} roughness={0.35} clearcoat={0.8} /></mesh>
    </group>
  );
}

/* ── o cérebro ────────────────────────────────────────────────────────────
 * Esfera → elipsoide → giros. O giro é RUÍDO DE CRISTA (1 − |simplex|, com o
 * espaço torcido por outro ruído): dá as linhas sinuosas, contínuas e de
 * largura parecida que o córtex tem — seno sobre seno dava papel amassado.
 * O lado de dentro de cada hemisfério é achatado e a fenda fica no meio. */
const GRAD = [[1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0], [1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1], [0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1]];
const PERM = (() => {
  // Permutação fixa (semente constante): o cérebro sai igual em todo render.
  const p = Array.from({ length: 256 }, (_, i) => i);
  let x = 1234567;
  for (let i = 255; i > 0; i--) { x = (x * 1103515245 + 12345) & 0x7fffffff; const j = x % (i + 1); [p[i], p[j]] = [p[j], p[i]]; }
  return [...p, ...p];
})();
function simplex(xin, yin, zin) {
  const F3 = 1 / 3, G3 = 1 / 6;
  const s = (xin + yin + zin) * F3;
  const i = Math.floor(xin + s), j = Math.floor(yin + s), k = Math.floor(zin + s);
  const t = (i + j + k) * G3;
  const x0 = xin - (i - t), y0 = yin - (j - t), z0 = zin - (k - t);
  let i1, j1, k1, i2, j2, k2;
  if (x0 >= y0) {
    if (y0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; }
  } else if (y0 < z0) { i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; } else if (x0 < z0) { i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; } else { i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; }
  const cantos = [[x0, y0, z0, 0, 0, 0], [x0 - i1 + G3, y0 - j1 + G3, z0 - k1 + G3, i1, j1, k1],
    [x0 - i2 + 2 * G3, y0 - j2 + 2 * G3, z0 - k2 + 2 * G3, i2, j2, k2], [x0 - 1 + 3 * G3, y0 - 1 + 3 * G3, z0 - 1 + 3 * G3, 1, 1, 1]];
  let n = 0;
  const ii = i & 255, jj = j & 255, kk = k & 255;
  for (const [x, y, z, a, b, c] of cantos) {
    let tt = 0.6 - x * x - y * y - z * z;
    if (tt < 0) continue;
    const g = GRAD[PERM[ii + a + PERM[jj + b + PERM[kk + c]]] % 12];
    tt *= tt;
    n += tt * tt * (g[0] * x + g[1] * y + g[2] * z);
  }
  return 32 * n;
}
/* 0 no fundo do sulco, 1 no alto do giro. O sulco é onde o ruído cruza o
   zero — curvas fechadas e sinuosas; o giro é o resto, largo e redondo. */
const liso = (a, b, x) => { const t = Math.max(0, Math.min(1, (x - a) / (b - a))); return t * t * (3 - 2 * t); };
function sulcos(x, y, z) {
  const w = 0.25 * simplex(x * 1.3 + 7, y * 1.3, z * 1.3);
  const n1 = simplex(x * 2.6 + w, y * 2.6 - w, z * 2.6 + w);
  const n2 = simplex(x * 5.2 - w + 3, y * 5.2 + w, z * 5.2);
  return Math.min(liso(0.0, 0.3, Math.abs(n1)), 0.35 + 0.65 * liso(0.0, 0.22, Math.abs(n2)));
}

// Cor de vértice é linear no three: os valores parecem escuros, e saem rosados na tela.
const ROSA_FUNDO = new THREE.Color('#3a0816');
const ROSA_TOPO = new THREE.Color('#d65a7c');

/* Cor por vértice: o fundo do sulco escurece (a "sombra" que a luz sozinha
   não faz numa malha tão fina) e a crista do giro clareia. */
function pintar(g, alturas) {
  const cores = new Float32Array(alturas.length * 3);
  const c = new THREE.Color();
  for (let i = 0; i < alturas.length; i++) {
    c.copy(ROSA_FUNDO).lerp(ROSA_TOPO, 0.25 + 0.75 * alturas[i]);
    cores.set([c.r, c.g, c.b], i * 3);
  }
  g.setAttribute('color', new THREE.BufferAttribute(cores, 3));
}

function hemisferio(lado) {
  const g = new THREE.SphereGeometry(1, 220, 170);
  const p = g.attributes.position;
  const v = new THREE.Vector3();
  const alturas = new Float32Array(p.count);
  for (let i = 0; i < p.count; i++) {
    v.fromBufferAttribute(p, i);
    const s = sulcos(v.x + lado * 0.3, v.y, v.z);
    alturas[i] = s;
    v.multiplyScalar(0.93 + 0.07 * s);
    v.set(v.x * 0.46, v.y * 0.44, v.z * 0.74);
    if (v.x * lado < 0) v.x *= 0.3;                // achata o lado de dentro (contínuo: sem degrau)
    if (v.y < 0) v.y *= 0.72;                      // a base mais plana
    p.setXYZ(i, v.x + lado * 0.16, v.y, v.z);
  }
  g.computeVertexNormals();
  pintar(g, alturas);
  return g;
}

function Cerebro({ cor }) {
  const [e, d] = useMemo(() => [hemisferio(-1), hemisferio(1)], []);
  const mat = <meshPhysicalMaterial vertexColors color={cor || '#ffffff'} roughness={0.5} metalness={0} clearcoat={0.35} clearcoatRoughness={0.4} sheen={0.3} sheenColor="#ffc2d2" />;
  /* Visto de cima e de frente (o topo inclinado para a câmera): é assim que
     se reconhece um cérebro — os dois hemisférios e a fenda no meio. */
  return (
    <group rotation={[0.95, 0, 0]} scale={[1.05, 1.05, 1.05]}>
      <mesh geometry={e}>{mat}</mesh>
      <mesh geometry={d}>{mat}</mesh>
    </group>
  );
}

/* Texto curto numa placa: desenhado num canvas (sem fonte 3D para baixar). */
function PlacaTexto({ texto = '', cor = '#111827' }) {
  const tex = useMemo(() => {
    const cv = document.createElement('canvas');
    cv.width = 512; cv.height = 512;
    const ctx = cv.getContext('2d');
    ctx.fillStyle = '#ffffff';
    const t = String(texto).slice(0, 14).toUpperCase();
    let tam = 200;
    ctx.font = `900 ${tam}px system-ui, -apple-system, Helvetica, Arial`;
    while (ctx.measureText(t).width > 440 && tam > 40) { tam -= 8; ctx.font = `900 ${tam}px system-ui, -apple-system, Helvetica, Arial`; }
    ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
    ctx.fillText(t, 256, 270);
    const x = new THREE.CanvasTexture(cv);
    x.colorSpace = THREE.SRGBColorSpace;
    return x;
  }, [texto]);
  return (
    <group>
      <Placa cor={cor} />
      <mesh position={[0, 0, 0.05]}><planeGeometry args={[1.0, 1.0]} /><meshBasicMaterial map={tex} transparent toneMapped={false} /></mesh>
    </group>
  );
}

/* O navegador: o anel de três arcos (vermelho, amarelo, verde) em volta da
   pupila azul, extrudado — lembra o tema sem copiar o arquivo da marca. */
function Navegador() {
  const arcos = useMemo(() => [0, 1, 2].map((k) => {
    const s = new THREE.Shape();
    const a0 = (k * 2 * Math.PI) / 3 + Math.PI / 2;
    const a1 = a0 + (2 * Math.PI) / 3 - 0.06;
    s.absarc(0, 0, 0.44, a0, a1, false);
    s.absarc(0, 0, 0.2, a1, a0, true);
    return new THREE.ExtrudeGeometry(s, EXT(0.09, 0.02));
  }), []);
  const cores = ['#db4437', '#f4b400', '#0f9d58'];
  return (
    <group position={[0, 0, -0.02]}>
      {arcos.map((g, k) => (<mesh key={k} geometry={g}><meshPhysicalMaterial color={cores[k]} roughness={0.45} clearcoat={0.5} /></mesh>))}
      <mesh position={[0, 0, 0.06]} rotation={[Math.PI / 2, 0, 0]}><cylinderGeometry args={[0.15, 0.15, 0.12, 48]} /><meshPhysicalMaterial color="#ffffff" roughness={0.4} /></mesh>
      <mesh position={[0, 0, 0.1]} rotation={[Math.PI / 2, 0, 0]}><cylinderGeometry args={[0.11, 0.11, 0.12, 48]} /><meshPhysicalMaterial color="#4285f4" roughness={0.35} clearcoat={0.6} /></mesh>
    </group>
  );
}

/* A pasta na nuvem: o triângulo de três faixas (verde, azul, amarelo). */
function Triangulo() {
  const faixas = useMemo(() => {
    const L = 0.5, h = L * Math.sqrt(3);
    const v = [[0, h * 0.62], [-L, -h * 0.38], [L, -h * 0.38]];
    return [0, 1, 2].map((k) => {
      const a = v[k], b = v[(k + 1) % 3];
      const nx = (b[1] - a[1]), ny = -(b[0] - a[0]);
      const n = Math.hypot(nx, ny);
      const e = 0.2;
      const s = new THREE.Shape();
      s.moveTo(a[0], a[1]); s.lineTo(b[0], b[1]);
      s.lineTo(b[0] - (nx / n) * e * 0.9 + (a[0] - b[0]) * 0.18, b[1] - (ny / n) * e * 0.9 + (a[1] - b[1]) * 0.18);
      s.lineTo(a[0] - (nx / n) * e * 0.9 + (b[0] - a[0]) * 0.18, a[1] - (ny / n) * e * 0.9 + (b[1] - a[1]) * 0.18);
      s.closePath();
      return new THREE.ExtrudeGeometry(s, EXT(0.09, 0.02));
    });
  }, []);
  const cores = ['#0f9d58', '#4285f4', '#f4b400'];
  return (
    <group position={[0, 0.02, -0.02]} scale={[0.9, 0.9, 0.9]}>
      {faixas.map((g, k) => (<mesh key={k} geometry={g}><meshPhysicalMaterial color={cores[k]} roughness={0.45} clearcoat={0.5} side={THREE.DoubleSide} /></mesh>))}
    </group>
  );
}

/* O foguete: corpo em revolução (o perfil girado), janela, três aletas e a
   chama que pulsa — de geometria, como o resto. */
function Foguete() {
  const corpo = useMemo(() => {
    const p = [[0, -0.42], [0.15, -0.4], [0.17, -0.1], [0.16, 0.12], [0.12, 0.28], [0.06, 0.4], [0, 0.46]].map(([x, y]) => new THREE.Vector2(x, y));
    return new THREE.LatheGeometry(p, 48);
  }, []);
  const aleta = useMemo(() => {
    const s = new THREE.Shape();
    s.moveTo(0, 0); s.lineTo(0.16, -0.12); s.lineTo(0.16, -0.26); s.lineTo(0, -0.18); s.closePath();
    return new THREE.ExtrudeGeometry(s, EXT(0.03, 0.01));
  }, []);
  return (
    <group rotation={[0, 0, -0.35]} scale={[1.1, 1.1, 1.1]}>
      <mesh geometry={corpo}><meshPhysicalMaterial color="#f3f4f6" roughness={0.3} metalness={0.2} clearcoat={0.8} /></mesh>
      <mesh position={[0, 0.34, 0]}><sphereGeometry args={[0.075, 32, 32]} /><meshPhysicalMaterial color="#e5484d" roughness={0.35} clearcoat={0.6} /></mesh>
      <mesh position={[0, 0.12, 0.15]}><sphereGeometry args={[0.055, 32, 32]} /><meshPhysicalMaterial color="#3b82f6" roughness={0.1} metalness={0.3} clearcoat={1} /></mesh>
      {[0, 1, 2].map((k) => (
        <group key={k} rotation={[0, (k * 2 * Math.PI) / 3, 0]}>
          <mesh geometry={aleta} position={[0.14, -0.14, -0.015]}><meshPhysicalMaterial color="#e5484d" roughness={0.4} clearcoat={0.5} /></mesh>
        </group>
      ))}
      <mesh position={[0, -0.52, 0]} rotation={[Math.PI, 0, 0]}><coneGeometry args={[0.1, 0.24, 32]} /><meshBasicMaterial color="#ffb020" toneMapped={false} /></mesh>
      <mesh position={[0, -0.49, 0]} rotation={[Math.PI, 0, 0]}><coneGeometry args={[0.06, 0.14, 32]} /><meshBasicMaterial color="#fff3c4" toneMapped={false} /></mesh>
    </group>
  );
}

export function Modelo({ modelo, cor, texto }) {
  if (modelo === 'foguete') return <Foguete />;
  if (modelo === 'chrome') return (<group><Placa cor={cor || '#ffffff'} /><Navegador /></group>);
  if (modelo === 'drive') return (<group><Placa cor={cor || '#ffffff'} /><Triangulo /></group>);
  if (modelo === 'chatgpt') return (<group><Placa cor={cor || '#0b8a6a'} /><NoHexagonal /></group>);
  if (modelo === 'claude') return (<group><Placa cor={cor || '#f4efe6'} /><Sol cor="#cf5f3c" /></group>);
  if (modelo === 'cerebro') return <Cerebro cor={cor} />;
  return <PlacaTexto texto={texto || modelo} cor={cor || '#111827'} />;
}
