/* ============================================================
   graph.jsx — connections map. Deterministic radial-cluster
   layout; click a node to focus its neighbourhood.
   ============================================================ */
const React = window.React;
const { useState, useMemo, useRef, useEffect } = React;
const Icon = window.Icon;

function buildGraph(state) {
  const nodes = [];
  const push = (id, kind, label, ref) => nodes.push({ id, kind, label, ref });
  state.courses.forEach((c) => push(c.id, "course", c.title, c));
  state.materials.forEach((m) => push(m.id, "material", m.title, m));
  state.notes.forEach((n) => push(n.id, "note", n.title, n));

  const ids = new Set(nodes.map((n) => n.id));
  const edges = [];
  const seen = new Set();
  const addEdge = (a, b) => {
    if (!ids.has(a) || !ids.has(b) || a === b) return;
    const key = [a, b].sort().join("|");
    if (seen.has(key)) return;
    seen.add(key); edges.push({ a, b });
  };
  state.materials.forEach((m) => m.links.forEach((l) => addEdge(m.id, l)));
  state.courses.forEach((c) => c.links.forEach((l) => addEdge(c.id, l)));
  state.notes.forEach((n) => n.links.forEach((l) => addEdge(n.id, l)));
  return { nodes, edges };
}

/* deterministic layout: courses anchor a radial cluster; their
   linked nodes orbit them; unconnected nodes go to a side column. */
function layout(nodes, edges, W, H) {
  const pos = {};
  const adj = {};
  nodes.forEach((n) => (adj[n.id] = []));
  edges.forEach((e) => { adj[e.a].push(e.b); adj[e.b].push(e.a); });

  const courses = nodes.filter((n) => n.kind === "course");
  const placed = new Set();
  const cx = W / 2, cy = H / 2;

  // place courses on an inner ring
  courses.forEach((c, i) => {
    const ang = (i / Math.max(courses.length, 1)) * Math.PI * 2 - Math.PI / 2;
    const rx = W * 0.26, ry = H * 0.28;
    pos[c.id] = { x: cx + Math.cos(ang) * rx, y: cy + Math.sin(ang) * ry, ang };
    placed.add(c.id);
  });

  // orbit each course's neighbours
  courses.forEach((c) => {
    const base = pos[c.id];
    const neigh = adj[c.id].filter((id) => !placed.has(id));
    neigh.forEach((id, j) => {
      const spread = (j - (neigh.length - 1) / 2) * 0.6;
      const a = base.ang + spread;
      pos[id] = { x: base.x + Math.cos(a) * W * 0.15, y: base.y + Math.sin(a) * H * 0.18 };
      placed.add(id);
    });
  });

  // remaining connected nodes: attach near a placed neighbour
  let guard = 0;
  let remaining = nodes.filter((n) => !placed.has(n.id) && adj[n.id].length > 0);
  while (remaining.length && guard++ < 50) {
    remaining.forEach((n) => {
      const anchor = adj[n.id].find((id) => placed.has(id));
      if (anchor) {
        const b = pos[anchor];
        const a = Math.random() * Math.PI * 2;
        pos[n.id] = { x: clamp(b.x + Math.cos(a) * 120, 60, W - 60), y: clamp(b.y + Math.sin(a) * 120, 60, H - 60) };
        placed.add(n.id);
      }
    });
    remaining = nodes.filter((n) => !placed.has(n.id) && adj[n.id].length > 0);
  }

  // isolated nodes: evenly spaced along the bottom band
  const isolated = nodes.filter((nd) => !placed.has(nd.id));
  const isoN = isolated.length;
  isolated.forEach((node, i) => {
    pos[node.id] = { x: clamp((W / (isoN + 1)) * (i + 1), 70, W - 70), y: H - 84, iso: true };
    placed.add(node.id);
  });
  return pos;
}
function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }

const NODE_SIZE = { course: 50, material: 40, note: 32 };

function GraphScreen({ state, nav, focus }) {
  const Cmp = window;
  const stageRef = useRef(null);
  const [dims, setDims] = useState({ w: 1000, h: 620 });
  const [active, setActive] = useState(focus || null);
  const [filter, setFilter] = useState({ material: true, course: true, note: true });

  useEffect(() => {
    const el = stageRef.current;
    if (el) setDims({ w: el.clientWidth, h: el.clientHeight });
  }, []);
  useEffect(() => { setActive(focus || null); }, [focus]);

  const { nodes, edges } = useMemo(() => buildGraph(state), [state]);
  const visNodes = nodes.filter((n) => filter[n.kind]);
  const visIds = new Set(visNodes.map((n) => n.id));
  const visEdges = edges.filter((e) => visIds.has(e.a) && visIds.has(e.b));
  const pos = useMemo(() => layout(visNodes, visEdges, dims.w, dims.h), [visNodes.length, visEdges.length, dims.w, dims.h]);

  const neighbours = useMemo(() => {
    if (!active) return null;
    const s = new Set([active]);
    visEdges.forEach((e) => { if (e.a === active) s.add(e.b); if (e.b === active) s.add(e.a); });
    return s;
  }, [active, visEdges]);

  const isDim = (id) => neighbours && !neighbours.has(id);
  const activeNode = nodes.find((n) => n.id === active);

  return (
    <div className="page page--wide screen-enter">
      <div className="page-head">
        <div className="page-head__titles">
          <div className="kicker-row"><span className="eyebrow-mono">Связи</span><span className="date-stamp">{visNodes.length} узлов · {visEdges.length} связей</span></div>
          <h1 className="page-title">Граф связей</h1>
        </div>
        <div className="page-actions">
          <div className="seg">
            {[["material", "Материалы"], ["course", "Курсы"], ["note", "Заметки"]].map(([k, l]) => (
              <button key={k} className={"seg__btn" + (filter[k] ? " seg__btn--on" : "")} onClick={() => setFilter((f) => ({ ...f, [k]: !f[k] }))}>{l}</button>
            ))}
          </div>
        </div>
      </div>

      <div className="cols" style={{ gridTemplateColumns: active ? "1fr 320px" : "1fr", alignItems: "start" }}>
        <div className="graph-stage" ref={stageRef} onClick={() => setActive(null)}>
          <svg style={{ position: "absolute", inset: 0, width: "100%", height: "100%", pointerEvents: "none" }}>
            {visEdges.map((e, i) => {
              const A = pos[e.a], B = pos[e.b];
              if (!A || !B) return null;
              const dim = neighbours && !(neighbours.has(e.a) && neighbours.has(e.b));
              const hot = active && (e.a === active || e.b === active);
              return <line key={i} x1={A.x} y1={A.y} x2={B.x} y2={B.y}
                stroke={hot ? "var(--signal)" : "var(--ink-100)"} strokeWidth={hot ? 2 : 1}
                opacity={dim ? 0.12 : hot ? 1 : 0.4} />;
            })}
          </svg>
          {visNodes.map((n) => {
            const p = pos[n.id]; if (!p) return null;
            const sz = NODE_SIZE[n.kind];
            return (
              <div key={n.id}
                className={"graph-node graph-node--" + n.kind + (active === n.id ? " graph-node--active" : "") + (isDim(n.id) ? " graph-node--dim" : "")}
                style={{ left: p.x, top: p.y }}
                onClick={(e) => { e.stopPropagation(); setActive(active === n.id ? null : n.id); }}>
                <div className="graph-node__dot" style={{ width: sz, height: sz }}>
                  {n.kind === "course" ? <Icon name="course" size={22} style={{ color: "var(--paper-00)" }} />
                    : n.kind === "note" ? <Icon name="notes" size={16} />
                    : <Cmp.TypeIcon type={n.ref.type} size={18} />}
                </div>
                <span className="graph-node__label">{n.label.length > 22 ? n.label.slice(0, 22) + "…" : n.label}</span>
              </div>
            );
          })}
          <div className="graph-legend">
            <div className="graph-legend__row"><span style={{ width: 14, height: 14, border: "2px solid var(--ink-100)" }} />Материал</div>
            <div className="graph-legend__row"><span style={{ width: 14, height: 14, background: "var(--ink-100)" }} />Курс</div>
            <div className="graph-legend__row"><span style={{ width: 14, height: 14, background: "var(--paper-10)", border: "2px solid var(--ink-100)" }} />Заметка</div>
          </div>
        </div>

        {active && activeNode && (
          <div className="task" style={{ display: "block", padding: "var(--s-5)" }}>
            <div className="e-label" style={{ marginBottom: 8 }}>{{ material: "Материал", course: "Курс", note: "Заметка" }[activeNode.kind]}</div>
            <div className="module__title" style={{ marginBottom: 12 }}>{activeNode.label}</div>
            <div className="e-label" style={{ marginBottom: 8 }}>Связи ({(neighbours?.size || 1) - 1})</div>
            <div className="row-2 wrap gap-2" style={{ marginBottom: 16 }}>
              {[...(neighbours || [])].filter((id) => id !== active).map((id) => {
                const e = window.PlannerLookup.entity(state, id);
                return e ? <Cmp.LinkChip key={id} entity={e} onClick={() => setActive(id)} /> : null;
              })}
              {(neighbours?.size || 1) - 1 === 0 && <span className="muted">Нет связей.</span>}
            </div>
            <Cmp.GButton variant="ink" icon="arrow" onClick={() => navTo3(nav, activeNode)} style={{ width: "100%", justifyContent: "center" }}>Открыть</Cmp.GButton>
          </div>
        )}
      </div>
    </div>
  );
}
function navTo3(nav, n) {
  if (n.kind === "course") nav({ screen: "course", id: n.id });
  else if (n.kind === "note") nav({ screen: "notes", id: n.id });
  else nav({ screen: "material", id: n.id });
}

window.Screens = Object.assign(window.Screens || {}, { graph: GraphScreen });
