// brain-constellation.jsx — Canvas central animé : agents = nœuds, comms = liens pulsants

const { useRef, useEffect, useState, useMemo } = React;

function Constellation({ activeId, setActiveId, mode, speedMul = 1, hoverComms, setHoverComms }) {
  const ref = useRef(null);
  const [size, setSize] = useState({ w: 800, h: 560 });
  const [t, setT] = useState(0);
  const [activeComms, setActiveComms] = useState([]);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const ro = new ResizeObserver((entries) => {
      const r = entries[0].contentRect;
      setSize({ w: r.width, h: r.height });
    });
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  // Animation loop
  useEffect(() => {
    let raf, start = performance.now();
    const loop = (now) => {
      setT((now - start) / 1000);
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, []);

  // Cycle which comms are "firing" — every 1.4s a new burst (live data)
  useEffect(() => {
    const tick = () => {
      const comms = window.BrainStore ? window.BrainStore.getComms() : (window.COMMS || []);
      if (!comms.length) return;
      const k = 2 + Math.floor(Math.random() * 2);
      const shuffled = [...comms].sort(() => Math.random() - 0.5).slice(0, k);
      setActiveComms(shuffled.map((c, i) => ({ ...c, key: Date.now() + '_' + i, born: performance.now() })));
    };
    tick();
    const id = setInterval(tick, 1400 / speedMul);
    return () => clearInterval(id);
  }, [speedMul]);

  const nodes = useMemo(() => {
    return AGENTS.map((a) => {
      const wobble = a.id === 'brain' ? 0 : Math.sin(t * 0.6 + (a.hue / 60)) * 6;
      const wobbleY = a.id === 'brain' ? 0 : Math.cos(t * 0.5 + (a.hue / 60)) * 4;
      return {
        ...a,
        cx: a.pos.x * size.w + wobble,
        cy: a.pos.y * size.h + wobbleY,
      };
    });
  }, [t, size.w, size.h]);

  const nodeById = useMemo(() => Object.fromEntries(nodes.map(n => [n.id, n])), [nodes]);

  // Build static connections (light) for ALL pairs in COMMS
  const currentComms = window.BrainStore ? window.BrainStore.getComms() : (window.COMMS || []);
  const staticEdges = useMemo(() => currentComms.map((c, i) => {
    const a = nodeById[c.from];
    const b = nodeById[c.to];
    if (!a || !b) return null;
    return { ...c, ax: a.cx, ay: a.cy, bx: b.cx, by: b.cy, key: i };
  }).filter(Boolean), [nodeById]);

  // Active (firing) edges with traveling pulses
  const liveEdges = useMemo(() => activeComms.map((c) => {
    const a = nodeById[c.from];
    const b = nodeById[c.to];
    if (!a || !b) return null;
    return { ...c, ax: a.cx, ay: a.cy, bx: b.cx, by: b.cy };
  }).filter(Boolean), [activeComms, nodeById]);

  return (
    <div className="constellation" ref={ref}>
      <svg className="constellation-svg" width={size.w} height={size.h}>
        <defs>
          <radialGradient id="brain-glow" cx="50%" cy="50%" r="50%">
            <stop offset="0%" stopColor="rgba(139,164,152,0.45)" />
            <stop offset="60%" stopColor="rgba(139,164,152,0.08)" />
            <stop offset="100%" stopColor="rgba(139,164,152,0)" />
          </radialGradient>
          <filter id="soft-glow" x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="3" result="blur"/>
            <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
          </filter>
        </defs>

        {/* Brain glow */}
        {nodeById.brain && (
          <circle cx={nodeById.brain.cx} cy={nodeById.brain.cy} r="180" fill="url(#brain-glow)" />
        )}

        {/* Static (light) connections */}
        {staticEdges.map((e) => (
          <line
            key={'s' + e.key}
            x1={e.ax} y1={e.ay} x2={e.bx} y2={e.by}
            stroke="var(--line-static)" strokeWidth="1" strokeDasharray="2 4"
            opacity={hoverComms === e.key ? 0.55 : 0.18}
          />
        ))}

        {/* Live (firing) connections with traveling pulse */}
        {liveEdges.map((e, i) => {
          const dx = e.bx - e.ax;
          const dy = e.by - e.ay;
          const len = Math.hypot(dx, dy);
          // pulse position 0->1 along the line
          const phase = ((t * 0.8 * speedMul) + i * 0.27) % 1;
          const px = e.ax + dx * phase;
          const py = e.ay + dy * phase;
          const color = e.type === 'alert' ? 'var(--c-alert)' : e.type === 'request' ? 'var(--c-request)' : 'var(--c-data)';
          return (
            <g key={e.key}>
              <line
                x1={e.ax} y1={e.ay} x2={e.bx} y2={e.by}
                stroke={color} strokeWidth="1.4" opacity="0.55"
              />
              <circle cx={px} cy={py} r="3.5" fill={color} filter="url(#soft-glow)">
                <animate attributeName="r" values="2.5;4.5;2.5" dur="0.9s" repeatCount="indefinite"/>
              </circle>
              {/* arrowhead at destination */}
              <circle cx={e.bx} cy={e.by} r="6" fill="none" stroke={color} strokeWidth="1" opacity={0.55 * (1 - phase * 0.4)}>
                <animate attributeName="r" values="4;14;4" dur="1.2s" repeatCount="indefinite"/>
                <animate attributeName="opacity" values="0.5;0;0.5" dur="1.2s" repeatCount="indefinite"/>
              </circle>
            </g>
          );
        })}

        {/* Nodes */}
        {nodes.map((n) => {
          const isActive = activeId === n.id;
          const isBrain = n.id === 'brain';
          const r = isBrain ? 38 : 28;
          const color = `oklch(0.62 0.08 ${n.hue})`;
          const colorDim = `oklch(0.62 0.08 ${n.hue} / 0.18)`;
          const pulseR = r + 6 + Math.sin(t * 2 + n.hue / 40) * 3;
          return (
            <g key={n.id}
               className={`node ${isActive ? 'is-active' : ''} status-${n.status}`}
               onClick={() => setActiveId(n.id)}
               style={{ cursor: 'pointer' }}
            >
              {/* Halo for active status */}
              {n.status === 'active' && (
                <circle cx={n.cx} cy={n.cy} r={pulseR} fill="none" stroke={color} strokeWidth="0.6" opacity="0.35"/>
              )}
              {/* Selection ring */}
              {isActive && (
                <circle cx={n.cx} cy={n.cy} r={r + 10} fill="none" stroke="var(--accent)" strokeWidth="1.2" strokeDasharray="3 3"/>
              )}
              {/* Body */}
              <circle cx={n.cx} cy={n.cy} r={r} fill={colorDim} stroke={color} strokeWidth="1.2"/>
              {/* Inner mark */}
              {isBrain ? (
                <g>
                  <circle cx={n.cx} cy={n.cy} r={r - 8} fill="none" stroke={color} strokeWidth="0.8" opacity="0.6"/>
                  <circle cx={n.cx} cy={n.cy} r={4} fill={color}/>
                </g>
              ) : (
                <text x={n.cx} y={n.cy + 2} textAnchor="middle" dominantBaseline="middle"
                      fontFamily="'Cormorant Garamond', serif" fontStyle="italic"
                      fontSize="18" fontWeight="500" fill="var(--text)">
                  {n.name[0]}
                </text>
              )}
              {/* Status dot */}
              <StatusDot status={n.status} cx={n.cx + r * 0.72} cy={n.cy - r * 0.72} />
              {/* Name label */}
              <text x={n.cx} y={n.cy + r + 18} textAnchor="middle"
                    fontFamily="'Cormorant Garamond', serif" fontStyle="italic"
                    fontSize="16" fontWeight="500" fill="var(--text)">
                {n.name}
              </text>
              <text x={n.cx} y={n.cy + r + 32} textAnchor="middle"
                    fontFamily="'JetBrains Mono', monospace"
                    fontSize="9" fill="var(--muted)" letterSpacing="0.08em">
                {n.role.split(' · ')[0].toUpperCase()}
              </text>
            </g>
          );
        })}
      </svg>

      {/* Legend */}
      <div className="constellation-legend">
        <span className="leg leg-data">— data</span>
        <span className="leg leg-request">— request</span>
        <span className="leg leg-alert">— alert</span>
      </div>
    </div>
  );
}

function StatusDot({ status, cx, cy }) {
  const map = {
    active: { color: 'var(--success)', anim: true },
    idle: { color: 'var(--muted)', anim: false },
    pending: { color: 'var(--warning)', anim: true },
    error: { color: 'var(--danger)', anim: true },
    off: { color: 'var(--border-strong)', anim: false },
    cron: { color: 'var(--info)', anim: false },
    sandbox: { color: 'var(--accent-2)', anim: false },
  };
  const s = map[status] || map.idle;
  return (
    <g>
      <circle cx={cx} cy={cy} r="4.5" fill={s.color} stroke="var(--bg)" strokeWidth="1.2"/>
      {s.anim && <circle cx={cx} cy={cy} r="4.5" fill={s.color} opacity="0.4">
        <animate attributeName="r" values="4.5;9;4.5" dur="1.6s" repeatCount="indefinite"/>
        <animate attributeName="opacity" values="0.45;0;0.45" dur="1.6s" repeatCount="indefinite"/>
      </circle>}
    </g>
  );
}

window.Constellation = Constellation;
