const fs = require('fs');
const path = require('path');
const GraphStore = require('../graph.js');
const { buildSupergraph, saveSupergraph } = require('../supergraph.js');

const FIXTURE_DIR = path.join(__dirname, 'fixtures/system-docs');
const SNAPSHOT = path.join(FIXTURE_DIR, 'snapshot.json');

let passed = 0;
let failed = 0;

function assert(condition, name) {
  if (condition) { passed++; console.log(`  ✓ ${name}`); }
  else { failed++; console.log(`  ✗ ${name}`); }
}

console.log('=== 7F: Supergraph (Multi-Repo Merge) Tests ===\n');

// Test 1: Merge two copies of the fixture as separate "repos"
console.log('Test 1: Basic merge');
const result = buildSupergraph([
  { repoId: 'repo-a', snapshotPath: SNAPSHOT },
  { repoId: 'repo-b', snapshotPath: SNAPSHOT },
]);
assert(result.stats.repos === 2, 'Merged 2 repos');
assert(result.stats.totalNodes > 100, `Total nodes > 100 (${result.stats.totalNodes})`);
// Each repo has same nodes, so merged should be ~2x
const singleGraph = GraphStore.loadSnapshot(SNAPSHOT);
assert(result.stats.totalNodes === singleGraph.nodes.size * 2, 'Node count is 2x single repo');

// Test 2: Node ID prefixing
console.log('\nTest 2: Node ID prefixing');
const hasRepoANode = [...result.graph.nodes.keys()].some(k => k.startsWith('repo-a::'));
const hasRepoBNode = [...result.graph.nodes.keys()].some(k => k.startsWith('repo-b::'));
assert(hasRepoANode, 'Has repo-a prefixed nodes');
assert(hasRepoBNode, 'Has repo-b prefixed nodes');
assert(!result.graph.nodes.has('gateway/server.ts:handleRequest'), 'No unprefixed nodes');

// Test 3: Edge prefixing
console.log('\nTest 3: Edge prefixing');
const prefixedEdges = result.graph.edges.filter(e => e.source.includes('::'));
assert(prefixedEdges.length === result.graph.edges.length, 'All edges have prefixed sources');

// Test 4: Cross-repo resolution (same snapshot = same names, but different repoIds)
console.log('\nTest 4: Cross-repo edge stats');
assert(result.stats.totalEdges > 0, `Has edges (${result.stats.totalEdges})`);
// With identical repos, cross-repo resolution happens when bare-name CALLS targets
// match entities in the other repo
console.log(`  Cross-repo edges: ${result.stats.crossRepoEdges}`);
console.log(`  Resolved: ${result.stats.resolvedCalls}, Unresolved: ${result.stats.unresolvedCalls}`);

// Test 5: Registry built correctly
console.log('\nTest 5: Namespace registry');
const regJson = result.registry.toJSON();
assert(Object.keys(regJson.byShortName).length > 10, `Registry has >10 names (${Object.keys(regJson.byShortName).length})`);
assert(Object.keys(regJson.byEntityId).length > 50, `Registry has >50 entity IDs (${Object.keys(regJson.byEntityId).length})`);

// Test 6: Save to disk
console.log('\nTest 6: Save supergraph');
const outDir = path.join(__dirname, 'tmp-supergraph');
if (fs.existsSync(outDir)) fs.rmSync(outDir, { recursive: true, force: true });
const paths = saveSupergraph(result, outDir);
assert(fs.existsSync(paths.graphPath), 'supergraph.json saved');
assert(fs.existsSync(paths.regPath), 'registry.json saved');
assert(fs.existsSync(paths.xrepoPath), 'cross-repo-edges.json saved');
assert(fs.existsSync(paths.statsPath), 'stats.json saved');

const savedStats = JSON.parse(fs.readFileSync(paths.statsPath, 'utf8'));
assert(savedStats.repos === 2, 'Saved stats match');

// Test 7: Single repo (no cross-repo edges expected)
console.log('\nTest 7: Single repo merge');
const single = buildSupergraph([
  { repoId: 'solo', snapshotPath: SNAPSHOT },
]);
assert(single.stats.repos === 1, 'Single repo');
assert(single.stats.crossRepoEdges === 0, 'No cross-repo edges for single repo');
assert(single.stats.totalNodes === singleGraph.nodes.size, 'Node count matches original');

// Test 8: OpenClaw full snapshot (if available)
console.log('\nTest 8: OpenClaw scale');
const fullSnap = path.join(__dirname, '..', 'snapshots', 'openclaw-full.json');
if (fs.existsSync(fullSnap)) {
  const start = Date.now();
  const fullResult = buildSupergraph([
    { repoId: 'openclaw', snapshotPath: fullSnap },
  ]);
  const elapsed = Date.now() - start;
  console.log(`  OpenClaw: ${fullResult.stats.totalNodes} nodes, ${fullResult.stats.totalEdges} edges in ${elapsed}ms`);
  assert(fullResult.stats.totalNodes > 20000, `>20k nodes (${fullResult.stats.totalNodes})`);
  assert(elapsed < 10000, `Completed in <10s (${elapsed}ms)`);
} else {
  console.log('  (skipped — openclaw-full.json not found)');
}

console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
process.exit(failed > 0 ? 1 : 0);
