/* global React, Icon */
/* ============================================================
   AgentCanvas — canvas infinito estilo Excalidraw
   + AgentNode / McpChip reutilizáveis (window) para a visão de pipelines
   ============================================================ */
const { useState, useRef, useReducer, useImperativeHandle, forwardRef, useEffect, useCallback } = React;
const Icon = window.Icon;

const NODE_W = 300;
const ANCHOR_Y = 104;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const anchorR = (a) => ({ x: a.pos.x + NODE_W, y: a.pos.y + ANCHOR_Y });
const anchorL = (a) => ({ x: a.pos.x, y: a.pos.y + ANCHOR_Y });
function edgePath(s, e) {
  const dx = Math.max(70, Math.abs(e.x - s.x) * 0.45);
  return `M ${s.x} ${s.y} C ${s.x + dx} ${s.y}, ${e.x - dx} ${e.y}, ${e.x} ${e.y}`;
}

// ---------- MCP chip (figurinha animada) ----------
function McpChip({ m, t, dark }) {
  const S = window.AgentData.STATUS[m.status];
  return (
    <div className="mcp-chip" title={`${m.name} · ${m.status === "ok" ? "Conectado" : m.status === "warn" ? "Lento" : "Offline"} · ${m.latency}`}
      style={{ background: t.chipBg, border: `1px solid ${t.chipBorder}` }}>
      <span className={`mcp-orb fig-${m.fig}`} style={{ "--orb": dark ? "#aecbe0" : S.orb, "--glow": dark ? "rgba(174,203,224,.4)" : S.glow }}>
        <span className="mcp-orb-core" style={{ background: dark ? "rgba(255,255,255,.04)" : "#fff", color: dark ? "#aecbe0" : S.orb }}>
          <Icon name={m.icon} size={13} stroke={2} />
        </span>
        {m.fig === "orbit" && <span className="mcp-sat" style={{ background: S.orb }} />}
        {m.fig === "scan" && <span className="mcp-scanline" />}
        {m.fig === "pulse" && <span className="mcp-ring" style={{ borderColor: S.orb }} />}
      </span>
      <span className="mcp-name" style={{ color: t.ink2 }}>{m.name}</span>
    </div>
  );
}

// ---------- Agent node (canvas: flow=false / pipeline: flow=true) ----------
function AgentNode({ a, t, dark, selected, run, flow, onSelect, onClick, onDragStart, onConnectStart }) {
  const S = window.AgentData.STATUS[a.status];
  const llm = window.AgentData.LLMS[a.llm];
  const { fmtTokens, fmtNum } = window.AgentData;
  const processing = run === "processing";
  const done = run === "done";

  const max = Math.max(...a.spark);
  const pts = a.spark.map((v, i) => `${(i / (a.spark.length - 1)) * 264},${44 - (v / max) * 40}`).join(" ");
  // standalone NN: sparkline azul (#72b2e6) salvo em erro (#e07a82)
  const sparkColor = dark ? (a.status === "error" ? "#e07a82" : "#72b2e6") : a.accent;
  const barColor = dark ? S.orb : a.accent;
  const sucColor = S.orb;

  const pos = flow
    ? { position: "relative", flex: "0 0 auto" }
    : { position: "absolute", left: a.pos.x, top: a.pos.y };

  return (
    <div data-node={a.id} className={`node${selected ? " sel" : ""}${processing ? " proc" : ""}${done ? " done" : ""}`}
      onMouseDown={flow ? undefined : (e) => onDragStart(e, a)}
      onClick={() => onClick(a)}
      style={{
        ...pos, width: NODE_W,
        background: dark ? (a.status === "error" ? "linear-gradient(180deg,#161113,#100c0d)" : "linear-gradient(180deg,#141416,#0f0f11)") : t.cardBg, borderRadius: 20,
        border: `1px solid ${selected ? a.accent : (dark && a.status === "error" ? "rgba(224,122,130,.34)" : t.cardBorder)}`,
        boxShadow: `${selected ? `0 0 0 4px ${a.accent}22, ` : ""}${t.cardShadow}${dark && a.status === "error" ? ", 0 0 40px rgba(224,122,130,.12)" : ""}`,
        "--accent": a.accent, "--statusGlow": S.glow, "--statusOrb": S.orb, cursor: "pointer",
      }}>
      <div className="node-shim" />
      <div style={{ borderRadius: 20, overflow: "hidden" }}>
      <div style={{ height: 3, background: `linear-gradient(90deg, ${barColor}, transparent)` }} />

      <div style={{ padding: "18px 20px 20px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 10, letterSpacing: ".18em", color: t.ink3, textTransform: "uppercase" }}>
          <span style={{ width: 7, height: 7, borderRadius: "50%", flex: "none", background: S.orb, boxShadow: `0 0 8px ${S.orb}` }} />{a.short}
        </div>
        <h3 style={{ margin: "9px 0 0", fontSize: 16.5, fontWeight: 700, color: t.ink, lineHeight: 1.2, letterSpacing: "-.01em" }}>{a.name}</h3>
        <p style={{ margin: "7px 0 0", fontSize: 12, lineHeight: 1.5, color: t.ink2 }}>{a.role}</p>

        <div style={{ display: "flex", gap: 7, marginTop: 13, flexWrap: "wrap" }}>
          <span style={{ display: "flex", alignItems: "center", gap: 6, height: 26, padding: "0 10px", borderRadius: 200, whiteSpace: "nowrap", background: dark ? "rgba(255,255,255,.04)" : t.chipBg, border: `1px solid ${dark ? "#2a2a2e" : t.chipBorder}`, fontSize: 11.5, fontWeight: 600, color: dark ? "#e2e1df" : t.ink }}>
            <span style={{ width: 6, height: 6, borderRadius: "50%", background: llm.color }} />{llm.name}
          </span>
          <span style={{ display: "flex", alignItems: "center", gap: 6, height: 26, padding: "0 10px", borderRadius: 200, whiteSpace: "nowrap", background: dark ? S.orb + "1a" : S.bg, border: `1px solid ${dark ? S.orb + "52" : S.ring}`, fontSize: 11.5, fontWeight: 600, color: dark ? S.orb : S.fg }}>
            <span style={{ width: 6, height: 6, borderRadius: "50%", background: S.orb, boxShadow: `0 0 6px ${S.orb}` }} />{S.label}
          </span>
        </div>

        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", marginTop: 16, border: `1px solid ${dark ? "#1f1f22" : t.chipBorder}`, borderRadius: 12, overflow: "hidden" }}>
          {[["TOKENS HOJE", fmtTokens(a.metrics.tokensDay), t.ink], ["ACION./DIA", fmtNum(a.metrics.calls), t.ink], ["SUCESSO", a.metrics.success + "%", sucColor]].map(([l, v, c], i) => (
            <div key={l} style={{ padding: "10px 11px", borderLeft: i ? `1px solid ${dark ? "#1f1f22" : t.chipBorder}` : "none" }}>
              <div style={{ fontSize: 8.5, letterSpacing: ".13em", color: t.ink3 }}>{l}</div>
              <div style={{ fontSize: 15, fontWeight: 700, color: c, marginTop: 4 }}>{v}</div>
            </div>
          ))}
        </div>

        <svg width="100%" height="40" viewBox="0 0 264 44" preserveAspectRatio="none" style={{ marginTop: 12, display: "block" }}>
          <polygon points={`${pts} 264,44 0,44`} fill={sparkColor} fillOpacity="0.12" />
          <polyline points={pts} fill="none" stroke={sparkColor} strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
        </svg>

        <div style={{ height: 1, background: dark ? "#1d1d20" : t.cardBorder, margin: "16px 0 13px" }} />
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
          <span style={{ fontSize: 9, letterSpacing: ".16em", color: t.ink3 }}>MCPS ACOPLADOS</span>
          <span style={{ fontSize: 9.5, color: t.ink3 }}>{a.mcps.length} tools</span>
        </div>
        <div style={{ display: "flex", flexWrap: "wrap", gap: 7, marginTop: 11 }}>
          {a.mcps.map((m) => (
            <span key={m.id} style={{ display: "flex", alignItems: "center", gap: 7, height: 32, padding: "0 11px 0 8px", borderRadius: 9, background: dark ? "rgba(255,255,255,.03)" : t.chipBg, border: `1px solid ${dark ? "#232326" : t.chipBorder}`, fontSize: 11.5, color: t.ink }}>
              <span style={{ width: 20, height: 20, borderRadius: 6, flex: "none", background: "rgba(174,203,224,.1)", display: "flex", alignItems: "center", justifyContent: "center", color: "#aecbe0" }}><Icon name={m.icon} size={12} stroke={1.8} /></span>{m.name}
            </span>
          ))}
        </div>
      </div>
      </div>

      {!flow && <span className="port port-in" style={{ top: ANCHOR_Y - 6, background: t.cardBg, borderColor: t.cardBorder }} />}
      {!flow && <span className="port port-out" title="Arraste para conectar a outro agente" onMouseDown={(e) => onConnectStart && onConnectStart(e, a.id)} style={{ top: ANCHOR_Y - 6, background: dark ? "#0c1318" : t.cardBg, borderColor: "#aecbe0", cursor: "crosshair" }} />}
    </div>
  );
}

// ---------- Canvas ----------
const AgentCanvas = forwardRef(function AgentCanvas(
  { agents, edges, t, dark, grid = true, selectedId, nodeRun, onSelect, onMovePos, onConnect, view, setView }, ref
) {
  const wrapRef = useRef(null);
  const glowRef = useRef(null);
  const pathRefs = useRef({});
  const packetsRef = useRef([]);
  const activeEdges = useRef(new Set());
  const [, force] = useReducer((x) => x + 1, 0);

  const agentMap = Object.fromEntries(agents.map((a) => [a.id, a]));

  // ---- packets ----
  const upsertPacket = (key, data) => {
    const arr = packetsRef.current;
    const i = arr.findIndex((p) => p.key === key);
    if (i >= 0) arr[i] = { key, ...data }; else arr.push({ key, ...data });
    force();
  };
  const removePacket = (key) => {
    packetsRef.current = packetsRef.current.filter((p) => p.key !== key);
    force();
  };

  const animateEdge = (f, to) => new Promise((res) => {
    const el = pathRefs.current[`${f}-${to}`];
    if (!el) { res(); return; }
    const len = el.getTotalLength();
    if (!len) { res(); return; }
    const dur = 760, start = performance.now();
    const color = agentMap[f]?.accent || "#8fb3d4";
    const key = `${f}-${to}-${start}`;
    activeEdges.current.add(`${f}-${to}`); force();
    const tick = (now) => {
      const p = Math.min(1, (now - start) / dur);
      const e = 1 - Math.pow(1 - p, 2);
      const pt = el.getPointAtLength(e * len);
      upsertPacket(key, { x: pt.x, y: pt.y, color });
      if (p < 1) requestAnimationFrame(tick);
      else { removePacket(key); activeEdges.current.delete(`${f}-${to}`); force(); res(); }
    };
    requestAnimationFrame(tick);
  });

  useImperativeHandle(ref, () => ({
    animateEdges: (pairs) => Promise.all(pairs.map(([f, to]) => animateEdge(f, to))),
  }));

  // ---- pan / zoom / drag / connect ----
  const pan = useRef(null);
  const drag = useRef(null);
  const connect = useRef(null);
  const suppress = useRef(false);
  const viewRef = useRef(view);
  viewRef.current = view;
  const toWorld = (cx, cy) => {
    const r = wrapRef.current.getBoundingClientRect();
    const v = viewRef.current;
    return { x: (cx - r.left - v.x) / v.k, y: (cy - r.top - v.y) / v.k };
  };

  const onNodeClick = (a) => {
    if (suppress.current) { suppress.current = false; return; }
    onSelect(a.id);
  };
  const onConnectStart = (e, fromId) => {
    e.stopPropagation();
    e.preventDefault();
    const p = toWorld(e.clientX, e.clientY);
    connect.current = { from: fromId, x: p.x, y: p.y };
    suppress.current = true;
    force();
  };
  const onPointerDown = (e) => {
    if (e.target.closest("[data-node]")) return;
    pan.current = { sx: e.clientX, sy: e.clientY, vx: view.x, vy: view.y };
    wrapRef.current.style.cursor = "grabbing";
  };
  const onDragStart = (e, a) => {
    e.stopPropagation();
    e.preventDefault();
    drag.current = { id: a.id, sx: e.clientX, sy: e.clientY, ox: a.pos.x, oy: a.pos.y, moved: false };
  };
  useEffect(() => {
    const move = (e) => {
      if (connect.current) {
        const p = toWorld(e.clientX, e.clientY);
        connect.current.x = p.x; connect.current.y = p.y;
        force();
        return;
      }
      if (drag.current) {
        const d = drag.current;
        const nx = d.ox + (e.clientX - d.sx) / view.k;
        const ny = d.oy + (e.clientY - d.sy) / view.k;
        if (Math.abs(e.clientX - d.sx) + Math.abs(e.clientY - d.sy) > 3) { d.moved = true; suppress.current = true; }
        onMovePos(d.id, { x: nx, y: ny });
      } else if (pan.current) {
        const p = pan.current;
        setView((v) => ({ ...v, x: p.vx + (e.clientX - p.sx), y: p.vy + (e.clientY - p.sy) }));
      }
    };
    const up = (e) => {
      if (connect.current) {
        const el = document.elementFromPoint(e.clientX, e.clientY);
        const node = el && el.closest("[data-node]");
        const toId = node && node.getAttribute("data-node");
        if (toId && toId !== connect.current.from && onConnect) onConnect(connect.current.from, toId);
        connect.current = null; force();
      }
      drag.current = null; pan.current = null;
      if (wrapRef.current) wrapRef.current.style.cursor = "grab";
    };
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseup", up);
    return () => { window.removeEventListener("mousemove", move); window.removeEventListener("mouseup", up); };
  }, [view.k, onMovePos, onSelect, onConnect]);

  // ---- spotlight: bolinhas azuis perto do mouse ----
  const onGridMove = (e) => {
    const el = glowRef.current;
    if (!el || !wrapRef.current) return;
    const rect = wrapRef.current.getBoundingClientRect();
    const mx = e.clientX - rect.left, my = e.clientY - rect.top;
    const mask = `radial-gradient(circle 150px at ${mx}px ${my}px, #000 0%, rgba(0,0,0,.45) 50%, transparent 76%)`;
    el.style.webkitMaskImage = mask;
    el.style.maskImage = mask;
    el.style.opacity = "1";
  };
  const onGridLeave = () => {
    if (glowRef.current) glowRef.current.style.opacity = "0";
  };

  const onWheel = (e) => {
    e.preventDefault();
    const rect = wrapRef.current.getBoundingClientRect();
    const mx = e.clientX - rect.left, my = e.clientY - rect.top;
    const factor = e.deltaY < 0 ? 1.08 : 0.926;
    setView((v) => {
      const k = Math.min(2.2, Math.max(0.35, v.k * factor));
      const r = k / v.k;
      return { k, x: mx - (mx - v.x) * r, y: my - (my - v.y) * r };
    });
  };

  return (
    <div ref={wrapRef} onMouseDown={onPointerDown} onWheel={onWheel} onMouseMove={grid ? onGridMove : undefined} onMouseLeave={onGridLeave} onDragStart={(e) => e.preventDefault()}
      style={{
        position: "absolute", inset: 0, overflow: "hidden", cursor: "grab",
        background: t.pageBg,
        backgroundImage: grid ? `radial-gradient(${t.gridDot} 1.3px, transparent 1.3px)` : "none",
        backgroundSize: `${26 * view.k}px ${26 * view.k}px`,
        backgroundPosition: `${view.x}px ${view.y}px`,
      }}>
      {grid && (
        <div ref={glowRef} aria-hidden style={{
          position: "absolute", inset: 0, pointerEvents: "none", opacity: 0, transition: "opacity .3s ease",
          backgroundImage: `radial-gradient(${dark ? "rgba(114,178,230,.95)" : "rgba(58,143,220,.85)"} 1.7px, transparent 1.7px)`,
          backgroundSize: `${26 * view.k}px ${26 * view.k}px`,
          backgroundPosition: `${view.x}px ${view.y}px`,
        }} />
      )}
      {dark && <>
        <div style={{ position: "absolute", top: -120, left: "50%", width: 880, height: 620, transform: "translateX(-46%)", background: "radial-gradient(ellipse at center, rgba(58,143,220,.14), transparent 66%)", pointerEvents: "none", filter: "blur(8px)" }} />
        <div style={{ position: "absolute", bottom: -160, right: "6%", width: 560, height: 480, background: "radial-gradient(ellipse at center, rgba(95,208,166,.07), transparent 68%)", pointerEvents: "none" }} />
      </>}
      <div style={{ position: "absolute", left: 0, top: 0, transform: `translate(${view.x}px, ${view.y}px) scale(${view.k})`, transformOrigin: "0 0" }}>
        <svg style={{ position: "absolute", left: 0, top: 0, width: 4000, height: 3000, overflow: "visible", pointerEvents: "none" }}>
          <defs>
            <linearGradient id="nnConnG" x1="0" y1="0" x2="1" y2="0">
              <stop offset="0" stopColor="#3a8fdc" stopOpacity="0" />
              <stop offset="0.5" stopColor="#72b2e6" />
              <stop offset="1" stopColor="#3a8fdc" stopOpacity="0" />
            </linearGradient>
          </defs>
          {edges.map((e) => {
            const s = anchorR(agentMap[e.from]), en = anchorL(agentMap[e.to]);
            const d = edgePath(s, en);
            const key = `${e.from}-${e.to}`;
            const flowing = activeEdges.current.has(key);
            const mid = { x: (s.x + en.x) / 2, y: (s.y + en.y) / 2 };
            return (
              <g key={key}>
                <path d={d} fill="none" stroke={t.edge} strokeWidth="2" strokeLinecap="round" />
                <path ref={(el) => (pathRefs.current[key] = el)} d={d} fill="none"
                  stroke={flowing ? agentMap[e.from].accent : t.edgeDash} strokeWidth={flowing ? 2.6 : 1.6}
                  strokeLinecap="round" strokeDasharray="2 9" className={flowing ? "edge-flow" : ""}
                  style={{ opacity: flowing ? 1 : .85 }} />
                {dark && !flowing && <path d={d} fill="none" stroke="url(#nnConnG)" strokeWidth="2.2" strokeLinecap="round" strokeDasharray="14 226" className="nn-flow" />}
                <circle cx={en.x} cy={en.y} r="3.4" fill={t.edge} />
                {e.label && (
                  <g transform={`translate(${mid.x}, ${mid.y - 10})`}>
                    <rect x={-(e.label.length * 3.0 + 10)} y={-9} width={e.label.length * 6.0 + 20} height={18} rx={9}
                      fill={t.edgeLabelBg} stroke={t.cardBorder} strokeWidth="1" />
                    <text x="0" y="4" textAnchor="middle" fontSize="10.5" fontFamily="Inter, sans-serif" fontWeight="600" fill={t.ink2}>{e.label}</text>
                  </g>
                )}
              </g>
            );
          })}
          {connect.current && agentMap[connect.current.from] && (
            <path d={edgePath(anchorR(agentMap[connect.current.from]), { x: connect.current.x, y: connect.current.y })}
              fill="none" stroke="#aecbe0" strokeWidth="2.4" strokeLinecap="round" strokeDasharray="6 6" opacity="0.95"
              style={{ filter: "drop-shadow(0 0 6px rgba(174,203,224,.55))" }} />
          )}
          {packetsRef.current.map((p) => (
            <g key={p.key}>
              <circle cx={p.x} cy={p.y} r="11" fill={p.color} opacity="0.18" />
              <circle cx={p.x} cy={p.y} r="5" fill={p.color} />
              <circle cx={p.x} cy={p.y} r="2.2" fill="#fff" opacity=".9" />
            </g>
          ))}
        </svg>

        {agents.map((a) => (
          <AgentNode key={a.id} a={a} t={t} dark={dark} selected={selectedId === a.id} run={nodeRun[a.id]}
            onClick={onNodeClick} onDragStart={onDragStart} onConnectStart={onConnectStart} />
        ))}
      </div>
    </div>
  );
});

window.AgentCanvas = AgentCanvas;
window.AgentNode = AgentNode;
window.McpChip = McpChip;
