/* global React */
const { useEffect, useRef, useState } = React;

// Knowledge Graph — 3D rotatable network rendered inside a tablet bezel.
// Drag to rotate, auto-rotates when idle.
function KnowledgeGraph() {
  const canvasRef = useRef(null);
  const wrapRef = useRef(null);
  const rafRef = useRef(0);
  const rotRef = useRef({ x: 0.25, y: 0.4 });
  const velRef = useRef({ x: 0, y: 0.003 });
  const draggingRef = useRef(false);
  const lastRef = useRef({ x: 0, y: 0 });
  const idleRef = useRef(0);
  const [hovered, setHovered] = useState(null);
  const [isDragging, setIsDragging] = useState(false);

  // Generate 3D nodes on a sphere shell
  const nodesRef = useRef(null);
  const edgesRef = useRef(null);
  const tilesRef = useRef(null);
  if (!nodesRef.current) {
    const N = 260;
    const arr = [];
    let seed = 7;
    const rnd = () => { seed = (seed * 9301 + 49297) % 233280; return seed / 233280; };

    // Concept tile anchors (highlighted nodes)
    const tileDefs = [
      { id: 'sops',   code: '01', label: 'SOPs & manuals',             href: '#knowledge' },
      { id: 'senior', code: '02', label: 'Senior operator videos',     href: '#knowledge' },
      { id: 'golden', code: '03', label: 'Golden recipes',             href: '#data' },
      { id: 'alarms', code: '04', label: 'Machine alarms',             href: '#monitoring' },
      { id: 'vision', code: '05', label: 'Vision system recipes',      href: '#knowledge' },
      { id: 'speed',  code: '06', label: 'Speed loss',                 href: '#data' },
      { id: 'micro',  code: '07', label: 'Micro stops',                href: '#data' },
      { id: 'change', code: '08', label: 'Changeover data',            href: '#data' },
      { id: 'cbm',    code: '09', label: 'Condition-based maintenance', href: '#maintenance' },
    ];

    // Distribute tile nodes evenly on sphere via fibonacci-ish
    const tiles = tileDefs.map((t, i) => {
      const phi = Math.acos(1 - 2 * (i + 0.5) / tileDefs.length);
      const theta = Math.PI * (1 + Math.sqrt(5)) * (i + 0.5);
      const r = 1.0;
      return {
        ...t,
        x: r * Math.sin(phi) * Math.cos(theta),
        y: r * Math.sin(phi) * Math.sin(theta),
        z: r * Math.cos(phi),
        size: 5,
        hub: true,
      };
    });
    tilesRef.current = tiles;

    // Background nodes (small dots) on shell + slight inner cloud
    for (let i = 0; i < N; i++) {
      // uniform sphere
      const u = rnd(), v = rnd();
      const theta = 2 * Math.PI * u;
      const phi = Math.acos(2 * v - 1);
      const r = 0.85 + rnd() * 0.18;
      arr.push({
        x: r * Math.sin(phi) * Math.cos(theta),
        y: r * Math.sin(phi) * Math.sin(theta),
        z: r * Math.cos(phi),
        size: 0.9 + rnd() * 1.6,
        hue: rnd(),
        hub: false,
      });
    }
    // Concat hubs at end so they draw on top
    nodesRef.current = arr.concat(tiles.map((t) => ({ ...t })));

    // Edges: connect each background node to its 2 nearest neighbors,
    // plus connect each hub to a few nearby hubs / nodes
    const all = nodesRef.current;
    const e = [];
    for (let i = 0; i < arr.length; i++) {
      const dists = [];
      for (let j = 0; j < arr.length; j++) {
        if (j === i) continue;
        const dx = arr[i].x - arr[j].x;
        const dy = arr[i].y - arr[j].y;
        const dz = arr[i].z - arr[j].z;
        dists.push([j, dx*dx + dy*dy + dz*dz]);
      }
      dists.sort((a, b) => a[1] - b[1]);
      const k = 2;
      for (let m = 0; m < k; m++) {
        const j = dists[m][0];
        if (j > i) e.push([i, j, false]);
      }
    }
    // Hub-to-hub bright edges
    const hubOffset = arr.length;
    for (let i = 0; i < tiles.length; i++) {
      for (let j = i + 1; j < tiles.length; j++) {
        const a = tiles[i], b = tiles[j];
        const dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z;
        const d2 = dx*dx + dy*dy + dz*dz;
        if (d2 < 1.6) e.push([hubOffset + i, hubOffset + j, true]);
      }
    }
    // Hub-to-nearby-bg-node
    for (let i = 0; i < tiles.length; i++) {
      const t = tiles[i];
      const dists = [];
      for (let j = 0; j < arr.length; j++) {
        const dx = t.x - arr[j].x, dy = t.y - arr[j].y, dz = t.z - arr[j].z;
        dists.push([j, dx*dx + dy*dy + dz*dz]);
      }
      dists.sort((a, b) => a[1] - b[1]);
      for (let m = 0; m < 3; m++) {
        e.push([hubOffset + i, dists[m][0], true]);
      }
    }
    edgesRef.current = e;
  }

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');

    let cssW = 0, cssH = 0;

    const resize = () => {
      const wrap = wrapRef.current;
      if (!wrap) return;
      const rect = wrap.getBoundingClientRect();
      cssW = rect.width;
      cssH = rect.height;
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      canvas.width = cssW * dpr;
      canvas.height = cssH * dpr;
      canvas.style.width = cssW + 'px';
      canvas.style.height = cssH + 'px';
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(wrapRef.current);

    const project = (x, y, z, cx, cy, scale) => {
      // perspective: depth = focal/(focal-z)
      const focal = 2.4;
      const d = focal / (focal - z);
      return {
        sx: cx + x * scale * d,
        sy: cy + y * scale * d,
        depth: d,
        z,
      };
    };

    const draw = () => {
      const t = performance.now();
      ctx.clearRect(0, 0, cssW, cssH);

      // Idle auto-rotate (slow)
      if (!draggingRef.current) {
        idleRef.current += 16;
        // ease back to default tilt slowly, but keep auto-spin on Y
        velRef.current.x *= 0.94;
        velRef.current.y = velRef.current.y * 0.95 + 0.0028 * 0.05;
      } else {
        idleRef.current = 0;
      }
      rotRef.current.x += velRef.current.x;
      rotRef.current.y += velRef.current.y;
      // clamp X
      const lim = Math.PI / 2 - 0.05;
      if (rotRef.current.x > lim) { rotRef.current.x = lim; velRef.current.x = 0; }
      if (rotRef.current.x < -lim) { rotRef.current.x = -lim; velRef.current.x = 0; }

      const cosY = Math.cos(rotRef.current.y);
      const sinY = Math.sin(rotRef.current.y);
      const cosX = Math.cos(rotRef.current.x);
      const sinX = Math.sin(rotRef.current.x);

      const cx = cssW / 2;
      const cy = cssH / 2;
      const scale = Math.min(cssW, cssH) * 0.34;

      // Rotate + project all nodes
      const nodes = nodesRef.current;
      const projected = new Array(nodes.length);
      for (let i = 0; i < nodes.length; i++) {
        const n = nodes[i];
        // gentle breathing on radius
        const breathe = 1 + Math.sin(t * 0.0008 + i * 0.13) * 0.012;
        let x = n.x * breathe;
        let y = n.y * breathe;
        let z = n.z * breathe;
        // Y axis
        let x1 = x * cosY + z * sinY;
        let z1 = -x * sinY + z * cosY;
        // X axis
        let y1 = y * cosX - z1 * sinX;
        let z2 = y * sinX + z1 * cosX;
        projected[i] = project(x1, y1, z2, cx, cy, scale);
      }

      // Draw edges (sorted by avg depth, back-to-front)
      const edges = edgesRef.current;
      const edgeOrder = edges
        .map((e, idx) => [idx, (projected[e[0]].z + projected[e[1]].z) / 2])
        .sort((a, b) => a[1] - b[1]);

      for (const [idx] of edgeOrder) {
        const [a, b, hub] = edges[idx];
        const pa = projected[a];
        const pb = projected[b];
        const avgD = (pa.depth + pb.depth) / 2;
        const front = (pa.z + pb.z) / 2 > 0;
        const opacity = front
          ? (hub ? 0.55 : 0.32) * Math.min(1, avgD)
          : (hub ? 0.18 : 0.08);

        const grad = ctx.createLinearGradient(pa.sx, pa.sy, pb.sx, pb.sy);
        grad.addColorStop(0, `rgba(123,106,243,${opacity})`);
        grad.addColorStop(1, `rgba(228,121,133,${opacity * 0.85})`);
        ctx.strokeStyle = grad;
        ctx.lineWidth = hub ? 1.0 : 0.5;
        ctx.beginPath();
        ctx.moveTo(pa.sx, pa.sy);
        ctx.lineTo(pb.sx, pb.sy);
        ctx.stroke();
      }

      // Draw nodes back-to-front
      const order = projected
        .map((p, i) => [i, p.z])
        .sort((a, b) => a[1] - b[1]);

      for (const [i] of order) {
        const n = nodes[i];
        const p = projected[i];
        const front = p.z > -0.1;
        if (n.hub) continue; // hubs drawn via tiles
        const r = n.size * (0.7 + p.depth * 0.5);
        const fade = front ? 0.85 : 0.25;
        ctx.fillStyle = n.hue > 0.7
          ? `rgba(228,121,133,${fade})`
          : `rgba(196,181,253,${fade})`;
        ctx.beginPath();
        ctx.arc(p.sx, p.sy, r, 0, Math.PI * 2);
        ctx.fill();
      }

      // Central pulse
      const pulseR = 3 + Math.sin(t * 0.003) * 1.5;
      const pulseGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, 24);
      pulseGrad.addColorStop(0, 'rgba(255,255,255,0.9)');
      pulseGrad.addColorStop(0.4, 'rgba(196,181,253,0.4)');
      pulseGrad.addColorStop(1, 'rgba(196,181,253,0)');
      ctx.fillStyle = pulseGrad;
      ctx.beginPath();
      ctx.arc(cx, cy, 24, 0, Math.PI * 2);
      ctx.fill();
      ctx.fillStyle = '#fff';
      ctx.beginPath();
      ctx.arc(cx, cy, pulseR, 0, Math.PI * 2);
      ctx.fill();

      // Outer ring
      ctx.strokeStyle = 'rgba(255,255,255,0.04)';
      ctx.setLineDash([2, 6]);
      ctx.beginPath();
      ctx.arc(cx, cy, scale * 1.05, 0, Math.PI * 2);
      ctx.stroke();
      ctx.setLineDash([]);

      // Project tile positions for React layer
      const tilePositions = tilesRef.current.map((tile, i) => {
        const idx = nodes.length - tilesRef.current.length + i;
        const p = projected[idx];
        return { ...tile, sx: p.sx, sy: p.sy, depth: p.depth, z: p.z };
      });

      // expose tile screen positions via DOM dataset for React update
      if (canvas.__tilesUpdate) canvas.__tilesUpdate(tilePositions);

      rafRef.current = requestAnimationFrame(draw);
    };
    rafRef.current = requestAnimationFrame(draw);

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

  // Tile positions live state, updated by canvas tick
  const [tilePositions, setTilePositions] = useState(
    () => tilesRef.current.map((t) => ({ ...t, sx: 0, sy: 0, depth: 1, z: 0 }))
  );
  useEffect(() => {
    const c = canvasRef.current;
    if (!c) return;
    let last = 0;
    c.__tilesUpdate = (positions) => {
      const now = performance.now();
      if (now - last < 33) return; // throttle ~30fps
      last = now;
      setTilePositions(positions);
    };
    return () => { if (c) c.__tilesUpdate = null; };
  }, []);

  // Drag handlers
  const onPointerDown = (e) => {
    draggingRef.current = true;
    setIsDragging(true);
    lastRef.current = { x: e.clientX, y: e.clientY };
    velRef.current = { x: 0, y: 0 };
    e.target.setPointerCapture?.(e.pointerId);
  };
  const onPointerMove = (e) => {
    if (!draggingRef.current) return;
    const dx = e.clientX - lastRef.current.x;
    const dy = e.clientY - lastRef.current.y;
    lastRef.current = { x: e.clientX, y: e.clientY };
    rotRef.current.y += dx * 0.008;
    rotRef.current.x += dy * 0.008;
    velRef.current = { x: dy * 0.0008, y: dx * 0.0008 };
  };
  const onPointerUp = (e) => {
    draggingRef.current = false;
    setIsDragging(false);
    e.target.releasePointerCapture?.(e.pointerId);
  };

  return (
    <div style={{ position: 'relative', width: '160%', maxWidth: 'none', margin: '-40px -60% -40px -9%' }}>
      {/* Tablet bezel */}
      <div
        style={{
          position: 'relative',
          aspectRatio: '16 / 11',
          borderRadius: 36,
          padding: 16,
          background: 'linear-gradient(180deg, #181428 0%, #0b0818 100%)',
          border: '1px solid rgba(255,255,255,0.08)',
          boxShadow:
            '0 40px 80px -20px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.04), inset 0 1px 0 rgba(255,255,255,0.06)',
        }}
      >
        {/* Front-facing camera dot */}
        <div
          style={{
            position: 'absolute',
            top: 16,
            left: '50%',
            transform: 'translateX(-50%)',
            width: 6,
            height: 6,
            borderRadius: 999,
            background: 'rgba(255,255,255,0.12)',
            boxShadow: 'inset 0 0 0 1px rgba(0,0,0,0.5)',
            zIndex: 4,
          }}
        />

        {/* Screen */}
        <div
          ref={wrapRef}
          onPointerDown={onPointerDown}
          onPointerMove={onPointerMove}
          onPointerUp={onPointerUp}
          onPointerCancel={onPointerUp}
          style={{
            position: 'relative',
            width: '100%',
            height: '100%',
            borderRadius: 24,
            background:
              'radial-gradient(ellipse at 30% 30%, rgba(123,106,243,0.18) 0%, rgba(228,121,133,0.08) 40%, #07050f 75%)',
            overflow: 'hidden',
            cursor: isDragging ? 'grabbing' : 'grab',
            touchAction: 'none',
            userSelect: 'none',
          }}
        >
          {/* Soft glow */}
          <div
            style={{
              position: 'absolute',
              inset: 0,
              background:
                'radial-gradient(circle at center, rgba(123,106,243,0.22) 0%, transparent 50%)',
              pointerEvents: 'none',
            }}
          />

          {/* Canvas */}
          <canvas ref={canvasRef} style={{ display: 'block', position: 'absolute', inset: 0 }} />

          {/* Tile labels overlay */}
          {tilePositions.map((t) => {
            const isHovered = hovered === t.id;
            const front = t.z > -0.2;
            const fade = front ? Math.min(1, 0.4 + t.depth * 0.7) : 0.25;
            return (
              <div
                key={t.id}
                onPointerEnter={() => setHovered(t.id)}
                onPointerLeave={() => setHovered(null)}
                style={{
                  position: 'absolute',
                  left: t.sx,
                  top: t.sy,
                  transform: `translate(-50%, -50%) scale(${0.85 + t.depth * 0.2})`,
                  pointerEvents: front ? 'auto' : 'none',
                  zIndex: isHovered ? 10 : Math.round(t.depth * 10),
                  opacity: fade,
                  transition: 'opacity 0.2s',
                }}
              >
                <a
                  href={t.href}
                  onClick={(e) => {
                    e.preventDefault();
                    const target = document.querySelector(t.href);
                    if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });
                  }}
                  style={{
                    display: 'inline-flex',
                    alignItems: 'center',
                    gap: 8,
                    padding: '6px 12px 6px 8px',
                    borderRadius: 999,
                    textDecoration: 'none',
                    background: isHovered
                      ? 'linear-gradient(135deg, rgba(123,106,243,0.95) 0%, rgba(228,121,133,0.9) 100%)'
                      : 'linear-gradient(180deg, rgba(20,18,42,0.92), rgba(13,10,30,0.92))',
                    border: isHovered
                      ? '1px solid rgba(255,255,255,0.4)'
                      : '1px solid rgba(123,106,243,0.4)',
                    boxShadow: isHovered
                      ? '0 18px 50px -10px rgba(123,106,243,0.7), 0 0 0 4px rgba(123,106,243,0.18)'
                      : '0 12px 32px -10px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.06)',
                    backdropFilter: 'blur(10px)',
                    WebkitBackdropFilter: 'blur(10px)',
                    color: '#fff',
                    fontSize: 12,
                    fontWeight: 500,
                    letterSpacing: '-0.01em',
                    fontFamily: 'var(--sans)',
                    whiteSpace: 'nowrap',
                    cursor: 'pointer',
                    transition: 'background 0.2s, box-shadow 0.2s, border-color 0.2s',
                  }}
                >
                  <span
                    style={{
                      fontFamily: 'var(--mono)',
                      fontSize: 9,
                      letterSpacing: '0.08em',
                      padding: '2px 6px',
                      borderRadius: 999,
                      background: isHovered ? 'rgba(10,8,20,0.25)' : 'rgba(123,106,243,0.22)',
                      color: isHovered ? '#fff' : '#c4b5fd',
                      fontWeight: 600,
                    }}
                  >
                    {t.code}
                  </span>
                  {t.label}
                </a>
              </div>
            );
          })}

          {/* HUD: top-left status */}
          <div
            style={{
              position: 'absolute',
              top: 14,
              left: 16,
              fontFamily: 'var(--mono)',
              fontSize: 10,
              color: 'rgba(255,255,255,0.55)',
              letterSpacing: '0.1em',
              textTransform: 'uppercase',
              display: 'flex',
              gap: 8,
              alignItems: 'center',
              pointerEvents: 'none',
            }}
          >
            <span
              style={{
                width: 6,
                height: 6,
                borderRadius: 999,
                background: '#E47985',
                boxShadow: '0 0 8px #E47985',
              }}
            />
            12,847 nodes · 38,210 edges
          </div>

          {/* HUD: bottom-right caption */}
          <div
            style={{
              position: 'absolute',
              bottom: 14,
              right: 16,
              fontFamily: 'var(--mono)',
              fontSize: 10,
              color: 'rgba(255,255,255,0.55)',
              letterSpacing: '0.1em',
              textTransform: 'uppercase',
              pointerEvents: 'none',
            }}
          >
            Knowledge graph · live
          </div>

          {/* Drag hint (fades when user has interacted) */}
          <div
            style={{
              position: 'absolute',
              bottom: 14,
              left: 16,
              fontFamily: 'var(--mono)',
              fontSize: 10,
              color: 'rgba(255,255,255,0.4)',
              letterSpacing: '0.08em',
              textTransform: 'uppercase',
              display: 'flex',
              alignItems: 'center',
              gap: 6,
              pointerEvents: 'none',
              opacity: isDragging ? 0 : 1,
              transition: 'opacity 0.3s',
            }}
          >
            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M12 2v6m0 0l-3-3m3 3l3-3M2 12h6m0 0l-3-3m3 3l-3 3M22 12h-6m0 0l3-3m-3 3l3 3M12 22v-6m0 0l-3 3m3-3l3 3" />
            </svg>
            drag to rotate
          </div>
        </div>
      </div>
    </div>
  );
}

window.KnowledgeGraph = KnowledgeGraph;
