// Touki — graphe Shoresh (force-directed, interactif)
/* global React, TOUKI_GRAPH, TOUKI_DATA */

const { useEffect: useEffectJ, useRef: useRefJ, useState: useStateJ, useMemo: useMemoJ, useCallback: useCallbackJ } = React;

function ShoreshGraph({ width, height, onSelect, selectedId, accent = '#6EF2FF' }) {
  const svgRef = useRefJ(null);
  const simRef = useRefJ(null);
  const rafRef = useRefJ(null);
  const [, force] = useStateJ(0);
  const [transform, setTransform] = useStateJ({ x: 0, y: 0, k: 1 });
  const [hoverId, setHoverId] = useStateJ(null);
  const [dragging, setDragging] = useStateJ(null);
  const [panStart, setPanStart] = useStateJ(null);
  const [t, setT] = useStateJ(0);

  const { nodes, edges } = useMemoJ(() => TOUKI_GRAPH.buildGraph(), []);

  useEffectJ(() => {
    const sim = TOUKI_GRAPH.createSim(nodes, edges, width, height);
    simRef.current = sim;
    let ticks = 0;
    const loop = () => {
      sim.step();
      ticks++;
      setT(x => x + 1);
      if (ticks < 600) rafRef.current = requestAnimationFrame(loop);
      else rafRef.current = null;
    };
    rafRef.current = requestAnimationFrame(loop);
    return () => { if (rafRef.current) cancelAnimationFrame(rafRef.current); };
  }, [width, height]);

  // animation loop for scan / pulse
  useEffectJ(() => {
    let id;
    const tick = () => { force(x => x + 1); id = requestAnimationFrame(tick); };
    id = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(id);
  }, []);

  const restartSim = useCallbackJ(() => {
    if (rafRef.current) return;
    let ticks = 0;
    const loop = () => {
      simRef.current.step();
      ticks++;
      setT(x => x + 1);
      if (ticks < 200) rafRef.current = requestAnimationFrame(loop);
      else rafRef.current = null;
    };
    rafRef.current = requestAnimationFrame(loop);
  }, []);

  const onWheel = (e) => {
    e.preventDefault();
    const rect = svgRef.current.getBoundingClientRect();
    const mx = e.clientX - rect.left, my = e.clientY - rect.top;
    const delta = -e.deltaY * 0.0015;
    const newK = Math.max(0.4, Math.min(2.5, transform.k * (1 + delta)));
    const nx = mx - (mx - transform.x) * (newK / transform.k);
    const ny = my - (my - transform.y) * (newK / transform.k);
    setTransform({ x: nx, y: ny, k: newK });
  };

  const onBgMouseDown = (e) => {
    if (e.target.closest('[data-node]')) return;
    setPanStart({ x: e.clientX - transform.x, y: e.clientY - transform.y });
  };

  useEffectJ(() => {
    const mm = (e) => {
      if (dragging && simRef.current) {
        const rect = svgRef.current.getBoundingClientRect();
        const x = (e.clientX - rect.left - transform.x) / transform.k;
        const y = (e.clientY - rect.top - transform.y) / transform.k;
        const n = simRef.current.byId.get(dragging);
        if (n) { n.x = x; n.y = y; n.fixed = true; restartSim(); }
      } else if (panStart) {
        setTransform(t => ({ ...t, x: e.clientX - panStart.x, y: e.clientY - panStart.y }));
      }
    };
    const mu = () => {
      if (dragging && simRef.current) {
        const n = simRef.current.byId.get(dragging); if (n) n.fixed = false;
      }
      setDragging(null); setPanStart(null);
    };
    window.addEventListener('mousemove', mm);
    window.addEventListener('mouseup', mu);
    return () => { window.removeEventListener('mousemove', mm); window.removeEventListener('mouseup', mu); };
  });

  const highlightSet = useMemoJ(() => {
    const s = new Set();
    const pivot = hoverId || selectedId;
    if (!pivot) return s;
    s.add(pivot);
    for (const e of edges) {
      if (e.source === pivot) s.add(e.target);
      if (e.target === pivot) s.add(e.source);
    }
    return s;
  }, [hoverId, selectedId, edges]);

  const hasFocus = hoverId || selectedId;
  const cx = width / 2, cy = height / 2;
  const now = Date.now() / 1000;
  const scanAngle = (now * 30) % 360;

  return (
    <div style={{ position: 'relative', width, height, overflow: 'hidden' }}>
      <svg
        ref={svgRef}
        width={width} height={height}
        onWheel={onWheel}
        onMouseDown={onBgMouseDown}
        style={{ display: 'block', cursor: panStart ? 'grabbing' : 'grab', userSelect: 'none' }}
      >
        <defs>
          <radialGradient id="shoreshVignette" cx="50%" cy="50%" r="70%">
            <stop offset="0%" stopColor={accent} stopOpacity="0.08" />
            <stop offset="70%" stopColor={accent} stopOpacity="0.02" />
            <stop offset="100%" stopColor="#000" stopOpacity="0" />
          </radialGradient>
          <pattern id="shoreshGrid" width="40" height="40" patternUnits="userSpaceOnUse">
            <path d="M 40 0 L 0 0 0 40" fill="none" stroke={accent} strokeWidth="0.5" opacity="0.08" />
          </pattern>
          <pattern id="shoreshGridMinor" width="8" height="8" patternUnits="userSpaceOnUse">
            <path d="M 8 0 L 0 0 0 8" fill="none" stroke={accent} strokeWidth="0.3" opacity="0.04" />
          </pattern>
          <filter id="shoreshGlow" x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="3" result="blur" />
            <feMerge>
              <feMergeNode in="blur" />
              <feMergeNode in="SourceGraphic" />
            </feMerge>
          </filter>
        </defs>

        {/* backdrop grids */}
        <rect width="100%" height="100%" fill="url(#shoreshGridMinor)" />
        <rect width="100%" height="100%" fill="url(#shoreshGrid)" />
        <rect width="100%" height="100%" fill="url(#shoreshVignette)" />

        {/* HUD concentric rings (static, don't scale with zoom) */}
        <g opacity="0.6">
          {[90, 180, 280, 380].map((r, i) => (
            <g key={i}>
              <circle cx={cx} cy={cy} r={r} fill="none" stroke={accent} strokeWidth="0.6" opacity="0.22" />
              {/* tick marks */}
              {Array.from({ length: 48 }).map((_, j) => {
                const a = (j / 48) * Math.PI * 2;
                const r1 = r - 3, r2 = r + (j % 4 === 0 ? 6 : 3);
                return (
                  <line
                    key={j}
                    x1={cx + Math.cos(a) * r1} y1={cy + Math.sin(a) * r1}
                    x2={cx + Math.cos(a) * r2} y2={cy + Math.sin(a) * r2}
                    stroke={accent} strokeWidth="0.5" opacity={j % 4 === 0 ? 0.4 : 0.15}
                  />
                );
              })}
            </g>
          ))}
          {/* rotating arc */}
          <g transform={`rotate(${scanAngle} ${cx} ${cy})`}>
            <path
              d={`M ${cx + 280} ${cy} A 280 280 0 0 1 ${cx + 280 * Math.cos(Math.PI / 3)} ${cy + 280 * Math.sin(Math.PI / 3)}`}
              fill="none" stroke={accent} strokeWidth="1.5" opacity="0.6"
            />
          </g>
          {/* crosshair */}
          <line x1={cx - 20} y1={cy} x2={cx + 20} y2={cy} stroke={accent} strokeWidth="0.6" opacity="0.4" />
          <line x1={cx} y1={cy - 20} x2={cx} y2={cy + 20} stroke={accent} strokeWidth="0.6" opacity="0.4" />
          <circle cx={cx} cy={cy} r="3" fill="none" stroke={accent} strokeWidth="0.8" opacity="0.6" />
        </g>

        <g transform={`translate(${transform.x},${transform.y}) scale(${transform.k})`}>
          {/* edges */}
          {edges.map((e, i) => {
            const s = simRef.current?.byId.get(e.source);
            const tt = simRef.current?.byId.get(e.target);
            if (!s || !tt) return null;
            const isHi = highlightSet.has(e.source) && highlightSet.has(e.target);
            const dim = hasFocus && !isHi;
            return (
              <line
                key={i}
                x1={s.x} y1={s.y} x2={tt.x} y2={tt.y}
                stroke={accent}
                strokeWidth={e.kind === 'root' ? 1.2 : 0.8}
                strokeDasharray={e.kind === 'semantic' ? '3 5' : 'none'}
                strokeOpacity={dim ? 0.06 : (e.kind === 'root' ? (isHi ? 0.9 : 0.4) : (isHi ? 0.7 : 0.22))}
                filter={isHi ? 'url(#shoreshGlow)' : undefined}
                style={{ transition: 'stroke-opacity .25s' }}
              />
            );
          })}

          {/* nodes */}
          {nodes.map((n) => {
            const isHi = highlightSet.has(n.id);
            const dim = hasFocus && !isHi;
            const isSel = selectedId === n.id;
            const pulse = 1 + Math.sin(now * 2 + (n.x + n.y) * 0.01) * 0.04;
            return (
              <g
                key={n.id}
                data-node={n.id}
                transform={`translate(${n.x},${n.y})`}
                style={{ cursor: 'pointer', opacity: dim ? 0.22 : 1, transition: 'opacity .25s' }}
                onMouseEnter={() => setHoverId(n.id)}
                onMouseLeave={() => setHoverId(null)}
                onMouseDown={(e) => { e.stopPropagation(); setDragging(n.id); }}
                onClick={(e) => { e.stopPropagation(); onSelect && onSelect(n.id); }}
              >
                {n.kind === 'root' ? (
                  <g filter={isHi || isSel ? 'url(#shoreshGlow)' : undefined}>
                    {/* outer ring */}
                    <circle r={n.r + 14} fill="none" stroke={accent} strokeWidth="0.6" opacity="0.4" />
                    {/* rotating tick ring */}
                    <g transform={`rotate(${(scanAngle * (n.x % 3 === 0 ? 1 : -1))})`}>
                      {Array.from({ length: 12 }).map((_, j) => {
                        const a = (j / 12) * Math.PI * 2;
                        return (
                          <line
                            key={j}
                            x1={Math.cos(a) * (n.r + 10)} y1={Math.sin(a) * (n.r + 10)}
                            x2={Math.cos(a) * (n.r + 16)} y2={Math.sin(a) * (n.r + 16)}
                            stroke={accent} strokeWidth="0.8" opacity="0.6"
                          />
                        );
                      })}
                    </g>
                    <circle r={n.r * pulse} fill={accent} fillOpacity="0.1" stroke={accent} strokeWidth="1.2" />
                    <circle r={n.r - 4} fill="none" stroke={accent} strokeWidth="0.6" opacity="0.5" />
                    <text
                      textAnchor="middle" dominantBaseline="central"
                      fontFamily="'Frank Ruhl Libre', serif"
                      fontSize={22} fontWeight={600} fill={accent}
                      style={{ pointerEvents: 'none' }}
                    >{n.label}</text>
                  </g>
                ) : (
                  <g filter={isHi || isSel ? 'url(#shoreshGlow)' : undefined}>
                    <circle r={n.r + 6} fill="none" stroke={accent} strokeWidth="0.4" opacity="0.3" />
                    <circle
                      r={n.r}
                      fill="#061820"
                      fillOpacity="0.85"
                      stroke={accent}
                      strokeWidth={isSel ? 1.6 : 1}
                      strokeOpacity={isSel ? 1 : 0.7}
                    />
                    {/* corner brackets for selected */}
                    {(isSel || isHi) && (
                      <g stroke={accent} strokeWidth="1" fill="none">
                        <path d={`M ${-n.r - 6} ${-n.r + 2} L ${-n.r - 6} ${-n.r - 6} L ${-n.r + 2} ${-n.r - 6}`} />
                        <path d={`M ${n.r + 6} ${-n.r + 2} L ${n.r + 6} ${-n.r - 6} L ${n.r - 2} ${-n.r - 6}`} />
                        <path d={`M ${-n.r - 6} ${n.r - 2} L ${-n.r - 6} ${n.r + 6} L ${-n.r + 2} ${n.r + 6}`} />
                        <path d={`M ${n.r + 6} ${n.r - 2} L ${n.r + 6} ${n.r + 6} L ${n.r - 2} ${n.r + 6}`} />
                      </g>
                    )}
                    <text
                      textAnchor="middle" dominantBaseline="central"
                      fontFamily="'Frank Ruhl Libre', serif"
                      fontSize={20} fontWeight={500} fill={accent}
                      style={{ pointerEvents: 'none' }}
                    >{n.label}</text>
                  </g>
                )}
              </g>
            );
          })}
        </g>

        {/* scan line */}
        <rect
          x="0" y={((now * 80) % (height + 40)) - 20}
          width="100%" height="2"
          fill={accent} opacity="0.08"
        />
      </svg>
    </div>
  );
}

window.TOUKI_ShoreshGraph = ShoreshGraph;
