/* global React */
// Wireframe data cube — sky-blue grid cube aligned to the Ajaia
// design system (navy ground, sky-400 lines + nodes).
// ────────────────────────────────────────────────────────────────
// Six faces, each subdivided into a 4×4 grid. Lines + node dots are
// rendered with a soft glow so the cube reads as a luminous wire
// lattice rotating slowly in space.

const { useEffect: useEffectCV, useRef: useRefCV } = React;

function ParticleCube({ height = 600 }) {
  const canvasRef = useRefCV(null);
  const wrapRef = useRefCV(null);
  const callouts = [
  { label: "Role-based labs", pos: "top-left", icon: <AjIcon.Layers size={15} /> },
  { label: "Governance guardrails", pos: "top-right", icon: <AjIcon.Shield size={15} /> },
  { label: "Manager reinforcement", pos: "bottom-left", icon: <AjIcon.Users size={15} /> },
  { label: "Adoption measurement", pos: "bottom-right", icon: <AjIcon.Chart size={15} /> }];

  useEffectCV(() => {
    const canvas = canvasRef.current;
    const wrap = wrapRef.current;
    if (!canvas || !wrap) return;

    const ctx = canvas.getContext("2d");
    const DPR = Math.min(window.devicePixelRatio || 1, 2);

    let W = 0,H = 0;
    const resize = () => {
      const r = wrap.getBoundingClientRect();
      W = Math.max(320, r.width);
      H = Math.max(320, r.height);
      canvas.width = W * DPR;
      canvas.height = H * DPR;
      canvas.style.width = W + "px";
      canvas.style.height = H + "px";
      ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(wrap);

    // ─── Build the wireframe geometry ────────────────────────────
    // The cube has edge length 1, centered on the origin. Each face
    // is divided into DIV × DIV cells, so each face has (DIV+1)² grid
    // nodes, and the visible lattice is the union of axis-aligned
    // lines lying on the six faces.
    const DIV = 4;
    const half = 0.5;
    const step = 1 / DIV;

    // 1. Vertices — every node on every face, deduplicated by key
    // 2. Edges — connect adjacent grid nodes along each face
    const vertexMap = new Map(); // key "x,y,z" → index
    const vertices = [];
    const edges = []; // [aIdx, bIdx]

    const addVertex = (x, y, z) => {
      const key = `${x.toFixed(4)},${y.toFixed(4)},${z.toFixed(4)}`;
      if (vertexMap.has(key)) return vertexMap.get(key);
      vertexMap.set(key, vertices.length);
      vertices.push([x, y, z]);
      return vertices.length - 1;
    };

    // Build one face as a 2D grid in face-local (u, v) coordinates,
    // then map to 3D space via the face's axis spec.
    const buildFace = (toXYZ) => {
      // Lay out (DIV+1) × (DIV+1) nodes
      const grid = [];
      for (let i = 0; i <= DIV; i++) {
        const row = [];
        for (let j = 0; j <= DIV; j++) {
          const u = -half + i * step;
          const v = -half + j * step;
          const [x, y, z] = toXYZ(u, v);
          row.push(addVertex(x, y, z));
        }
        grid.push(row);
      }
      // Horizontal segments
      for (let i = 0; i <= DIV; i++) {
        for (let j = 0; j < DIV; j++) {
          edges.push([grid[i][j], grid[i][j + 1]]);
        }
      }
      // Vertical segments
      for (let j = 0; j <= DIV; j++) {
        for (let i = 0; i < DIV; i++) {
          edges.push([grid[i][j], grid[i + 1][j]]);
        }
      }
    };

    // Six faces — front, back, right, left, top, bottom
    buildFace((u, v) => [u, v, half]); // front  (z = +half)
    buildFace((u, v) => [u, v, -half]); // back   (z = -half)
    buildFace((u, v) => [half, u, v]); // right  (x = +half)
    buildFace((u, v) => [-half, u, v]); // left   (x = -half)
    buildFace((u, v) => [u, half, v]); // top    (y = +half) — note: y+ goes "down" in screen space
    buildFace((u, v) => [u, -half, v]); // bottom (y = -half)

    // ─── Render loop ─────────────────────────────────────────────
    const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    let rafId;
    const startTime = performance.now();

    const draw = (now) => {
      const t = (now - startTime) / 1000;

      // Clear background — the wrapper itself has the radial halo,
      // so the canvas only needs to wipe transparent.
      ctx.clearRect(0, 0, W, H);

      // Camera-space projection params. We use a near-orthographic
      // projection (very long focal length) so the cube reads as a
      // PERFECT square-faced cube without perspective foreshortening
      // skewing the faces during rotation.
      const cx = W / 2;
      const cy = H / 2;
      const scale = Math.min(W, H) * 0.34;
      const focal = 30; // near-orthographic

      // Rotation: locked isometric X tilt + smooth continuous Y
      // rotation. The X tilt is the classic ~35° isometric angle so
      // the cube always reads as a recognizable 3-face cube no matter
      // where the Y rotation is.
      const ry = reduced ? Math.PI / 4 : t * 0.30 + Math.PI / 4;
      const rx = -0.61; // ~ -35° isometric tilt
      const cosY = Math.cos(ry),sinY = Math.sin(ry);
      const cosX = Math.cos(rx),sinX = Math.sin(rx);

      // Project all vertices once. With a long focal length the
      // projection is effectively orthographic: face proportions
      // stay square regardless of rotation angle.
      const proj = new Array(vertices.length);
      for (let i = 0; i < vertices.length; i++) {
        const [x0, y0, z0] = vertices[i];
        // Rotate around Y
        const x1 = x0 * cosY + z0 * sinY;
        const z1 = -x0 * sinY + z0 * cosY;
        const y1 = y0;
        // Rotate around X
        const y2 = y1 * cosX - z1 * sinX;
        const z2 = y1 * sinX + z1 * cosX;
        // Near-orthographic projection — inv stays close to 1
        const depth = focal + z2;
        const inv = focal / depth;
        proj[i] = {
          x: cx + x1 * scale * inv,
          y: cy + y2 * scale * inv,
          depth,
          z: z2
        };
      }

      // ─── Draw edges ────────────────────────────────────────────
      // Two passes for a glow effect: a wide soft underlay first,
      // then a crisper line on top. Edges further from the camera
      // are dimmer so the cube has clear front/back hierarchy.
      const drawEdges = (lineWidth, alphaMul, color) => {
        for (let i = 0; i < edges.length; i++) {
          const [aIdx, bIdx] = edges[i];
          const a = proj[aIdx],b = proj[bIdx];
          const midZ = (a.z + b.z) / 2;
          // Map depth in [-half-cushion, +half+cushion] to alpha
          const t01 = Math.max(0, Math.min(1, (midZ + 0.75) / 1.5));
          const alpha = (0.30 + 0.70 * t01) * alphaMul;
          ctx.strokeStyle = color.replace(/A/, alpha.toFixed(3));
          ctx.lineWidth = lineWidth;
          ctx.beginPath();
          ctx.moveTo(a.x, a.y);
          ctx.lineTo(b.x, b.y);
          ctx.stroke();
        }
      };

      // Soft outer glow (additive)
      ctx.globalCompositeOperation = "lighter";
      drawEdges(6, 0.10, "rgba(151,194,247,A)");
      drawEdges(3.5, 0.18, "rgba(182,212,250,A)");
      ctx.globalCompositeOperation = "source-over";

      // Crisp line
      drawEdges(1.3, 1.0, "rgba(151,194,247,A)");

      // ─── Draw nodes at intersections ──────────────────────────
      ctx.globalCompositeOperation = "lighter";
      for (let i = 0; i < proj.length; i++) {
        const p = proj[i];
        const t01 = Math.max(0, Math.min(1, (p.z + 0.75) / 1.5));
        const alpha = 0.30 + 0.70 * t01;
        const r = 2.2 + 1.6 * t01;
        // Halo
        ctx.fillStyle = `rgba(151,194,247,${alpha * 0.30})`;
        ctx.beginPath();
        ctx.arc(p.x, p.y, r * 2.4, 0, Math.PI * 2);
        ctx.fill();
        // Core
        ctx.fillStyle = `rgba(220,233,255,${alpha})`;
        ctx.beginPath();
        ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.globalCompositeOperation = "source-over";

      if (!reduced) rafId = requestAnimationFrame(draw);
    };

    rafId = requestAnimationFrame(draw);

    return () => {
      cancelAnimationFrame(rafId);
      ro.disconnect();
    };
  }, []);

  return (
    <div
      ref={wrapRef}
      className="cube-visual"
      style={{
        position: "relative",
        width: "100%",
        height,
        background:
        "radial-gradient(ellipse 55% 50% at 50% 50%, rgba(86,131,255,0.22) 0%, rgba(20,40,120,0.10) 40%, rgba(7,16,58,0) 75%)",
        overflow: "visible",
        isolation: "isolate"
      }}>
      
      <canvas
        ref={canvasRef}
        style={{
          position: "absolute",
          inset: 0,
          width: "100%",
          height: "100%"
        }}
        aria-hidden="true" />

      <div className="cube-callouts" aria-label="Ajaia enterprise training features">
        {callouts.map((c, i) =>
        <div key={c.label} className={`cube-callout ${c.pos}`}>
            <span className="cube-feature">
              <span className="cube-feature-icon">{c.icon}</span>
              <span>{c.label}</span>
            </span>
          </div>
        )}
      </div>
      
      {/* Outer atmospheric halo */}
      <div
        aria-hidden="true"
        style={{
          position: "absolute",
          inset: 0,
          pointerEvents: "none",
          background:
          "radial-gradient(circle at 50% 50%, rgba(151,194,247,0.10) 0%, rgba(7,16,58,0) 60%)",
          mixBlendMode: "screen"
        }} />

      <style>{`
        .cube-callouts {
          position: absolute;
          inset: 0;
          pointer-events: none;
          z-index: 2;
        }
        .cube-callout {
          position: absolute;
          width: 1px;
          height: 1px;
        }
        .cube-callout.top-left { left: 32%; top: 27%; }
        .cube-callout.top-right { left: 66%; top: 27%; }
        .cube-callout.bottom-left { left: 33%; top: 75%; }
        .cube-callout.bottom-right { left: 66%; top: 73%; }
        .cube-feature {
          position: absolute;
          display: inline-grid;
          grid-template-columns: auto 1fr;
          align-items: center;
          gap: 8px;
          white-space: nowrap;
          padding: 8px 11px 8px 8px;
          background: rgba(0,29,107,0.55);
          border: 1px solid rgba(151,194,247,0.28);
          color: var(--sky-100);
          font-family: var(--font-mono);
          font-size: 10.5px;
          line-height: 1.35;
          letter-spacing: 0.08em;
          text-transform: uppercase;
          backdrop-filter: blur(12px);
          box-shadow: 0 14px 34px -22px rgba(0,0,0,0.8);
          opacity: 0;
          transform: translateY(10px) scale(0.94);
        }
        .cube-callout:nth-child(1) .cube-feature { animation: cubeTagOne 8s var(--ease) infinite; }
        .cube-callout:nth-child(2) .cube-feature { animation: cubeTagTwo 8s var(--ease) infinite; }
        .cube-callout:nth-child(3) .cube-feature { animation: cubeTagThree 8s var(--ease) infinite; }
        .cube-callout:nth-child(4) .cube-feature { animation: cubeTagFour 8s var(--ease) infinite; }
        .cube-feature-icon {
          width: 24px;
          height: 24px;
          display: grid;
          place-items: center;
          background: rgba(151,194,247,0.16);
          border: 1px solid rgba(151,194,247,0.30);
          color: var(--sky-400);
        }
        .cube-callout.top-left .cube-feature { right: 72px; top: 13px; }
        .cube-callout.top-right .cube-feature { left: 72px; top: 13px; }
        .cube-callout.bottom-left .cube-feature { right: 72px; bottom: 13px; }
        .cube-callout.bottom-right .cube-feature { left: 72px; bottom: 13px; }
        @keyframes cubeTagOne {
          0% { opacity: 0; transform: translateY(10px) scale(0.94); }
          8% { opacity: 1; transform: translateY(0) scale(1); }
          84% { opacity: 1; transform: translateY(0) scale(1); }
          92% { opacity: 0; transform: translateY(-6px) scale(0.98); }
          100% { opacity: 0; transform: translateY(-6px) scale(0.98); }
        }
        @keyframes cubeTagTwo {
          0%, 8% { opacity: 0; transform: translateY(10px) scale(0.94); }
          16% { opacity: 1; transform: translateY(0) scale(1); }
          84% { opacity: 1; transform: translateY(0) scale(1); }
          92% { opacity: 0; transform: translateY(-6px) scale(0.98); }
          100% { opacity: 0; transform: translateY(-6px) scale(0.98); }
        }
        @keyframes cubeTagThree {
          0%, 16% { opacity: 0; transform: translateY(10px) scale(0.94); }
          24% { opacity: 1; transform: translateY(0) scale(1); }
          84% { opacity: 1; transform: translateY(0) scale(1); }
          92% { opacity: 0; transform: translateY(-6px) scale(0.98); }
          100% { opacity: 0; transform: translateY(-6px) scale(0.98); }
        }
        @keyframes cubeTagFour {
          0%, 24% { opacity: 0; transform: translateY(10px) scale(0.94); }
          32% { opacity: 1; transform: translateY(0) scale(1); }
          84% { opacity: 1; transform: translateY(0) scale(1); }
          92% { opacity: 0; transform: translateY(-6px) scale(0.98); }
          100% { opacity: 0; transform: translateY(-6px) scale(0.98); }
        }
        @media (max-width: 1024px) {
          .cube-callout.top-left { left: 34%; top: 27%; }
          .cube-callout.top-right { left: 63%; top: 27%; }
          .cube-callout.bottom-left { left: 34%; top: 75%; }
          .cube-callout.bottom-right { left: 63%; top: 73%; }
          .cube-feature { font-size: 9.5px; padding: 6px 8px; }
          .cube-callout.top-left .cube-feature,
          .cube-callout.bottom-left .cube-feature { right: 12px; }
          .cube-callout.top-right .cube-feature,
          .cube-callout.bottom-right .cube-feature { left: 12px; }
        }
        @media (max-width: 640px) {
          .cube-callouts { display: none; }
        }
        @media (prefers-reduced-motion: reduce) {
          .cube-feature {
            animation: none !important;
            opacity: 1 !important;
            transform: none !important;
          }
        }
      `}</style>
      
    </div>);

}

Object.assign(window, { ParticleCube });
