#!/usr/bin/env node
/**
 * graph-radar (§5.7 ДР-3) — файл-уровень проверки полноты: тронул X → граф связывает
 * с Y → адресован ли Y? Чистое ядро (граф инъектируется, формат project-graph B).
 * Сосед, который сам в touchedFiles, не флагуется (уже адресован).
 */
/** F-F2: нормализация пути для сравнения — backslash → '/', strip './'-префикс. */
function normPath(p) {
  if (typeof p !== 'string') return '';
  let s = p.replace(/\\/g, '/');
  while (s.startsWith('./')) s = s.slice(2);
  return s;
}

export function buildGraphRadar({ touchedFiles, graph } = {}) {
  const touched = (Array.isArray(touchedFiles) ? touchedFiles : []).map(normPath).filter(Boolean);
  const nodes = graph && Array.isArray(graph.nodes) ? graph.nodes : [];
  const links = graph && Array.isArray(graph.links) ? graph.links : [];
  const touchedSet = new Set(touched);
  // id → узел (для source_file соседа)
  const byId = new Map(nodes.map((n) => [n && n.id, n]).filter(([id]) => id));
  // id тронутых узлов (сравнение по нормализованному пути — F-F2)
  const touchedIds = new Set(nodes.filter((n) => n && touchedSet.has(normPath(n.source_file))).map((n) => n.id));
  const seen = new Set();
  const neighbors = [];
  for (const l of links) {
    if (!l || typeof l !== 'object') continue;
    for (const [a, b] of [[l.source, l.target], [l.target, l.source]]) {
      if (!touchedIds.has(a)) continue;          // a — тронутый конец
      const nb = byId.get(b);
      if (!nb) continue;
      if (touchedSet.has(normPath(nb.source_file))) continue; // сосед сам тронут → адресован
      const key = `${b}<-${a}`;
      if (seen.has(key)) continue;
      seen.add(key);
      // F-F4 (L2-пол): элемент заморожен — машинная мутация addressed бросает TypeError;
      // отметка «адресовано» живёт ВНЕ структуры (наставник/владелец).
      neighbors.push(Object.freeze({ node: b, label: nb.label ?? null, source_file: nb.source_file ?? null, viaTouched: a, addressed: false }));
    }
  }
  // F-F1: touchedFiles (подано) рядом с touchedNodes (найдено) — слепота радара видна:
  // touchedFiles>0 ∧ touchedNodes=0 ⇒ «радар слеп» (сигнал-всегда, зеркало F-C6/F-C2-3).
  return { neighbors, summary: { touchedFiles: touched.length, touchedNodes: touchedIds.size, unaddressed: neighbors.length } };
}
