/* SCUTOID 3D — complex double-scutoid topology + true liquid glass.
   Two scutoids stacked back-to-back:
     hex (top) → pent (middle) → hex (bottom)
   with TWO Y-junctions on opposite sides — the canonical biological
   "scutoid" pair found in epithelial tissue. */

function buildComplexScutoid({ r = 1.0, h = 1.15 } = {}) {
  const THREE = window.THREE;
  const v = []; // [x,y,z] arrays
  const tri = [];
  const TOP = 6, MID = 5, BOT = 6;
  const topStart = -Math.PI / 2 + Math.PI / TOP;
  const midStart = topStart + Math.PI / (2 * MID);
  const botRot = Math.PI; // mirror Y to opposite side
  const botStart = topStart + Math.PI / TOP + botRot;

  // top hex y=+h
  for (let i = 0; i < TOP; i++) {
    const a = topStart + (i / TOP) * Math.PI * 2;
    v.push([Math.cos(a) * r, +h, Math.sin(a) * r]);
  }
  // middle pent y=0 (slightly bulged outward)
  for (let i = 0; i < MID; i++) {
    const a = midStart + (i / MID) * Math.PI * 2;
    v.push([Math.cos(a) * r * 1.08, 0, Math.sin(a) * r * 1.08]);
  }
  // bottom hex y=-h, rotated 180° so Y is mirrored
  for (let i = 0; i < BOT; i++) {
    const a = botStart + (i / BOT) * Math.PI * 2;
    v.push([Math.cos(a) * r, -h, Math.sin(a) * r]);
  }

  const T = i => i;
  const P = i => TOP + i;
  const B = i => TOP + MID + i;

  // Y midpoints — pushed outward at half-height
  const pushMid = (a, b, y) => {
    const x = (v[a][0] + v[b][0]) / 2;
    const z = (v[a][2] + v[b][2]) / 2;
    const L = Math.hypot(x, z);
    const s = r * 1.12 / L;
    v.push([x * s, y, z * s]);
    return v.length - 1;
  };
  const M1 = pushMid(T(5), T(0), +h / 2);   // upper Y between T5/T0
  const M2 = pushMid(B(5), B(0), -h / 2);   // lower Y between B5/B0 (on opposite side)

  // ---- FACES ----
  // top cap (hex fan)
  for (let i = 1; i < 5; i++) tri.push(T(0), T(i + 1), T(i));
  // bottom cap (hex fan, opposite winding)
  for (let i = 1; i < 5; i++) tri.push(B(0), B(i), B(i + 1));

  // upper scutoid sides — 4 quads + Y
  // map hex edges T1-T2..T4-T5  to pent edges P0-P1..P3-P4
  for (let i = 1; i <= 4; i++) {
    tri.push(T(i), T(i + 1), P(i));
    tri.push(T(i), P(i), P(i - 1));
  }
  // Y junction on top
  tri.push(T(5), T(0), M1);
  tri.push(T(5), M1, P(4));
  tri.push(T(0), P(0), M1);
  tri.push(M1, P(0), P(4));
  tri.push(T(0), T(1), P(0));

  // lower scutoid sides — mirror topology
  // map pent edges P0-P1..P3-P4 to hex edges B1-B2..B4-B5 (offset by 1 due to bottom rotation)
  for (let i = 0; i <= 3; i++) {
    tri.push(P(i), P(i + 1), B(i + 2));
    tri.push(P(i), B(i + 2), B(i + 1));
  }
  // Y junction on bottom (between B5 and B0/B1 area, with M2)
  tri.push(P(4), P(0), M2);
  tri.push(P(4), M2, B(5));
  tri.push(P(0), B(1), M2);
  tri.push(M2, B(1), B(0));
  tri.push(M2, B(0), B(5));

  const positions = new Float32Array(v.length * 3);
  v.forEach((p, i) => { positions[i*3]=p[0]; positions[i*3+1]=p[1]; positions[i*3+2]=p[2]; });

  // Build TWO geometries from the same vertices, split by centroid:
  // certain faces become solid glossy panels, others stay as liquid glass.
  // We pick faces whose centroid lies on the "front" side of the figure
  // (x < 0 OR specific face indices) to be solid.
  const solidIdx = [], glassIdx = [];
  const isSolidFace = (i) => {
    const a = tri[i*3], b = tri[i*3+1], c = tri[i*3+2];
    const cx = (v[a][0] + v[b][0] + v[c][0]) / 3;
    const cy = (v[a][1] + v[b][1] + v[c][1]) / 3;
    const cz = (v[a][2] + v[b][2] + v[c][2]) / 3;
    // Pick faces on the left/back (negative X), mid-band, as solid panels.
    // Mix it up a bit so it looks like the logo (some panels, some open).
    if (cy > h * 0.95) return false;                  // keep top hex face open (glass)
    if (cy < -h * 0.95) return false;                 // keep bottom hex face open (glass)
    return (cx + cz * 0.3) < -0.05;                   // left-ish half
  };
  const nTris = tri.length / 3;
  for (let i = 0; i < nTris; i++) {
    const dest = isSolidFace(i) ? solidIdx : glassIdx;
    dest.push(tri[i*3], tri[i*3+1], tri[i*3+2]);
  }

  const make = (indices) => {
    const g = new THREE.BufferGeometry();
    g.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    g.setIndex(indices);
    g.computeVertexNormals();
    return g;
  };
  const glassGeo = make(glassIdx);
  const solidGeo = make(solidIdx);
  const fullGeo  = make(tri);
  return { glassGeo, solidGeo, fullGeo };
}

/* Build a denser wireframe by adding mid-edge diagonals and centroids on each face.
   This gives the "complex faceted" look from the logo. */
function buildWireFromGeometry(geom, { withCentroids = true, centroidPull = 0.05 } = {}) {
  const THREE = window.THREE;
  const pos = geom.attributes.position.array;
  const idx = geom.index.array;
  const segs = [];
  const seen = new Set();
  const addEdge = (ax, ay, az, bx, by, bz) => {
    const k = [ax, ay, az, bx, by, bz].map(x => x.toFixed(4)).sort().join(',');
    if (seen.has(k)) return;
    seen.add(k);
    segs.push(ax, ay, az, bx, by, bz);
  };
  const P = (i) => [pos[i*3], pos[i*3+1], pos[i*3+2]];
  for (let i = 0; i < idx.length; i += 3) {
    const a = P(idx[i]), b = P(idx[i+1]), c = P(idx[i+2]);
    addEdge(...a, ...b);
    addEdge(...b, ...c);
    addEdge(...c, ...a);
    if (withCentroids) {
      // centroid pulled slightly outward along the face normal
      const cx = (a[0]+b[0]+c[0])/3;
      const cy = (a[1]+b[1]+c[1])/3;
      const cz = (a[2]+b[2]+c[2])/3;
      // approximate outward direction = centroid normalized in XZ plane
      const L = Math.hypot(cx, cz) || 1;
      const ox = cx + (cx / L) * centroidPull;
      const oy = cy;
      const oz = cz + (cz / L) * centroidPull;
      addEdge(...a, ox, oy, oz);
      addEdge(...b, ox, oy, oz);
      addEdge(...c, ox, oy, oz);
    }
  }
  const g = new THREE.BufferGeometry();
  g.setAttribute('position', new THREE.BufferAttribute(new Float32Array(segs), 3));
  return g;
}

function Scutoid3D() {
  const ref = React.useRef(null);
  // Skip the entire WebGL stack on low-perf devices — transmission + 5 dynamic lights
  // + halos easily produces 5–15 FPS on budget mobiles. We render a styled PNG instead.
  //
  // Listen for runtime 'perfdowngrade' (dispatched by app.jsx FPS probe when the
  // device is actually rendering slowly): we flip lowPerf to true, which forces
  // re-render with the static glyph and triggers the effect cleanup that tears
  // down the renderer. One-way only — once low, we stay low.
  const initial = (typeof document !== 'undefined') && document.body.dataset.perf === 'low';
  const [lowPerf, setLowPerf] = React.useState(initial);

  React.useEffect(() => {
    const onDowngrade = () => setLowPerf(true);
    window.addEventListener('perfdowngrade', onDowngrade);
    return () => window.removeEventListener('perfdowngrade', onDowngrade);
  }, []);

  React.useEffect(() => {
    if (lowPerf) return undefined;
    const THREE = window.THREE;
    if (!THREE) return;
    const container = ref.current;
    if (!container) return;

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(28, 1, 0.1, 100);
    camera.position.set(0, 0.15, 8.4);

    const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setClearColor(0x000000, 0);
    renderer.outputColorSpace = THREE.SRGBColorSpace;
    container.appendChild(renderer.domElement);

    const group = new THREE.Group();
    scene.add(group);

    // --- HDR-like environment for glass reflections ---
    const pmrem = new THREE.PMREMGenerator(renderer);
    const envScene = new THREE.Scene();
    // simple gradient sphere as env
    const envGeo = new THREE.SphereGeometry(50, 16, 12);
    const envCanvas = document.createElement('canvas');
    envCanvas.width = 256; envCanvas.height = 256;
    const ctx = envCanvas.getContext('2d');
    const grad = ctx.createLinearGradient(0, 0, 0, 256);
    grad.addColorStop(0, '#08111f');
    grad.addColorStop(0.4, '#0e2546');
    grad.addColorStop(0.6, '#4db8ff');
    grad.addColorStop(0.9, '#0a1428');
    grad.addColorStop(1, '#04060a');
    ctx.fillStyle = grad;
    ctx.fillRect(0, 0, 256, 256);
    // a couple of bright streaks for highlights
    ctx.fillStyle = 'rgba(180,220,255,0.95)';
    ctx.fillRect(0, 90, 256, 4);
    ctx.fillStyle = 'rgba(140,200,255,0.5)';
    ctx.fillRect(0, 70, 256, 1);
    ctx.fillRect(0, 110, 256, 1);
    const envTex = new THREE.CanvasTexture(envCanvas);
    envTex.mapping = THREE.EquirectangularReflectionMapping;
    envTex.colorSpace = THREE.SRGBColorSpace;
    const envMap = pmrem.fromEquirectangular(envTex).texture;
    scene.environment = envMap;

    // --- geometry ---
    const { glassGeo, solidGeo, fullGeo } = buildComplexScutoid({ r: 1.05, h: 1.25 });

    // Liquid glass — transmissive, near-clear, slight blue tint
    const glassMat = new THREE.MeshPhysicalMaterial({
      color: 0xaad4ff,
      metalness: 0.0,
      roughness: 0.06,
      transmission: 1.0,
      thickness: 0.85,
      ior: 1.45,
      attenuationColor: 0x4db8ff,
      attenuationDistance: 3.2,
      clearcoat: 1.0,
      clearcoatRoughness: 0.06,
      reflectivity: 0.55,
      transparent: true,
      opacity: 0.32,
      side: THREE.DoubleSide,
      envMapIntensity: 1.2,
    });
    const glassMesh = new THREE.Mesh(glassGeo, glassMat);
    group.add(glassMesh);

    // Dark glossy navy panels
    const solidMat = new THREE.MeshPhysicalMaterial({
      color: 0x0a1830,
      metalness: 0.85,
      roughness: 0.18,
      clearcoat: 1.0,
      clearcoatRoughness: 0.08,
      reflectivity: 0.8,
      envMapIntensity: 1.4,
      side: THREE.DoubleSide,
    });
    const solidMesh = new THREE.Mesh(solidGeo, solidMat);
    group.add(solidMesh);

    // Inner triangle wires (every triangle edge + centroid spokes) — over full shape
    const innerWires = buildWireFromGeometry(fullGeo, { withCentroids: true, centroidPull: 0.04 });
    const wireMat = new THREE.LineBasicMaterial({
      color: 0x6cc5ff, transparent: true, opacity: 0.72,
    });
    const wires = new THREE.LineSegments(innerWires, wireMat);
    group.add(wires);

    // Sharp silhouette edges over full shape
    const edgesGeo = new THREE.EdgesGeometry(fullGeo, 12);
    const edgeMat = new THREE.LineBasicMaterial({
      color: 0xc8e6ff, transparent: true, opacity: 1.0,
    });
    const sharp = new THREE.LineSegments(edgesGeo, edgeMat);
    group.add(sharp);

    // Glow halo on top of wires (additive, slightly enlarged)
    const haloMat = new THREE.LineBasicMaterial({
      color: 0x4db8ff, transparent: true, opacity: 0.28,
      blending: THREE.AdditiveBlending, depthWrite: false,
    });
    const halo = new THREE.LineSegments(innerWires, haloMat);
    halo.scale.setScalar(1.025);
    group.add(halo);

    const haloMat2 = new THREE.LineBasicMaterial({
      color: 0x9ad6ff, transparent: true, opacity: 0.16,
      blending: THREE.AdditiveBlending, depthWrite: false,
    });
    const halo2 = new THREE.LineSegments(edgesGeo, haloMat2);
    halo2.scale.setScalar(1.06);
    group.add(halo2);

    // Lights — keep some so the glass picks up depth even on dark BG
    scene.add(new THREE.AmbientLight(0x1c2740, 0.6));
    const k    = new THREE.PointLight(0x4db8ff, 28, 22, 2); scene.add(k);
    const f    = new THREE.PointLight(0xffffff,  6, 22, 2); scene.add(f);
    const rim  = new THREE.PointLight(0xb388ff, 12, 22, 2); scene.add(rim);
    const top  = new THREE.PointLight(0x00e5ff, 16, 22, 2); scene.add(top);
    const warm = new THREE.PointLight(0xffb168,  8, 22, 2); scene.add(warm);

    // Each light has its own orbit (radius, angular speed, phase, Y level + wobble).
    // Negative speeds flip direction so orbits cross visually.
    const orbits = [
      { l: k,    r: 4.2, w:  0.55, p: 0.0,           yBase:  2.4, yAmp: 0.5, ys: 0.7 },
      { l: f,    r: 5.0, w:  0.32, p: Math.PI * 0.7, yBase: -0.6, yAmp: 1.4, ys: 0.5 },
      { l: rim,  r: 3.7, w:  0.62, p: Math.PI * 1.2, yBase: -2.6, yAmp: 0.4, ys: 0.9 },
      { l: top,  r: 3.8, w: -0.40, p: Math.PI * 0.4, yBase:  3.0, yAmp: 0.3, ys: 0.4 },
      { l: warm, r: 4.5, w:  0.28, p: Math.PI * 1.7, yBase: -1.8, yAmp: 1.0, ys: 0.6 },
    ];

    // ---- interaction ----
    let targetX = -0.22, targetY = -0.6;
    let curX = targetX, curY = targetY;
    let baseY = 0;

    const onMove = (e) => {
      const r = container.getBoundingClientRect();
      const cx = r.left + r.width / 2;
      const cy = r.top + r.height / 2;
      const dx = (e.clientX - cx) / (window.innerWidth / 2);
      const dy = (e.clientY - cy) / (window.innerHeight / 2);
      targetY = dx * 1.0 - 0.3;
      targetX = -dy * 0.55 - 0.05;
    };
    window.addEventListener('mousemove', onMove);

    const onResize = () => {
      const w = container.clientWidth;
      const h = container.clientHeight;
      if (w === 0 || h === 0) return;
      renderer.setSize(w, h, false);
      camera.aspect = w / h;
      camera.updateProjectionMatrix();
    };
    onResize();
    window.addEventListener('resize', onResize);
    const ro = new ResizeObserver(onResize);
    ro.observe(container);

    let raf, t = 0;
    const animate = () => {
      t += 0.016;
      curX += (targetX - curX) * 0.05;
      curY += (targetY - curY) * 0.05;
      baseY += 0.0028;
      group.rotation.x = curX;
      group.rotation.y = curY + baseY;
      group.position.y = Math.sin(t * 0.7) * 0.05;
      halo.material.opacity = 0.22 + Math.sin(t * 1.6) * 0.06;
      halo2.material.opacity = 0.12 + Math.sin(t * 1.2 + 1) * 0.05;
      for (let i = 0; i < orbits.length; i++) {
        const o = orbits[i];
        const a = t * o.w + o.p;
        o.l.position.x = Math.cos(a) * o.r;
        o.l.position.z = Math.sin(a) * o.r;
        o.l.position.y = o.yBase + Math.sin(t * o.ys + o.p) * o.yAmp;
      }
      renderer.render(scene, camera);
      raf = requestAnimationFrame(animate);
    };
    animate();

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('resize', onResize);
      ro.disconnect();
      glassGeo.dispose();
      solidGeo.dispose();
      fullGeo.dispose();
      innerWires.dispose();
      edgesGeo.dispose();
      glassMat.dispose();
      solidMat.dispose();
      wireMat.dispose();
      edgeMat.dispose();
      haloMat.dispose();
      haloMat2.dispose();
      envMap.dispose();
      envTex.dispose();
      pmrem.dispose();
      renderer.dispose();
      if (renderer.domElement.parentNode === container) {
        container.removeChild(renderer.domElement);
      }
    };
  }, [lowPerf]);

  if (lowPerf) {
    return (
      <div ref={ref} className="hero-3d hero-3d-static" aria-label="Scutoid">
        <img src="assets/scutoid.png" alt="Scutoid — символ студии D-one" className="scutoid-glyph" width="320" height="320" decoding="async"/>
      </div>
    );
  }
  return <div ref={ref} className="hero-3d" aria-label="Scutoid"/>;
}

window.Scutoid3D = Scutoid3D;
