const fs = require('fs');
const path = require('path');
const GraphStore = require('../graph.js');
const { buildSubsystems } = require('../subsystem.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}`); }
}

// Load graph and run subsystem aggregator
const graph = GraphStore.loadSnapshot(SNAPSHOT);
const result = buildSubsystems(graph, { minTraffic: 3, crossCuttingThreshold: 0.6 });
const expected = JSON.parse(fs.readFileSync(path.join(FIXTURE_DIR, 'expected-subsystems.json'), 'utf8'));

console.log('=== 7A: Subsystem Aggregator Tests ===\n');

// Test 1: Directory clustering — correct number of subsystems
console.log('Test 1: Directory clustering');
assert(result.subsystems.length === 5, `Found 5 subsystems (got ${result.subsystems.length})`);
const names = result.subsystems.map(s => s.name).sort();
assert(JSON.stringify(names) === JSON.stringify(['agents', 'channels', 'config', 'gateway', 'utils']),
  `Subsystem names match: ${names.join(', ')}`);

// Test 2: File assignment accuracy
console.log('\nTest 2: File assignment accuracy');
let correctFiles = 0;
let totalFiles = 0;
for (const expSub of expected.subsystems) {
  const actualSub = result.subsystems.find(s => s.name === expSub.name);
  assert(!!actualSub, `Subsystem "${expSub.name}" exists`);
  if (actualSub) {
    for (const f of expSub.files) {
      totalFiles++;
      if (actualSub.files.includes(f)) correctFiles++;
      else console.log(`    MISSING: ${f} in ${expSub.name}`);
    }
  }
}
const accuracy = totalFiles > 0 ? (correctFiles / totalFiles * 100).toFixed(1) : 0;
assert(correctFiles / totalFiles >= 0.9, `File assignment accuracy ≥90% (${accuracy}%, ${correctFiles}/${totalFiles})`);

// Test 3: Cross-cutting detection
console.log('\nTest 3: Cross-cutting detection');
assert(result.crossCutting.includes('utils'), 'utils detected as cross-cutting');
assert(result.crossCutting.includes('config'), 'config detected as cross-cutting');
assert(!result.crossCutting.includes('gateway'), 'gateway is NOT cross-cutting');
assert(!result.crossCutting.includes('agents'), 'agents is NOT cross-cutting');
assert(!result.crossCutting.includes('channels'), 'channels is NOT cross-cutting');

// Verify kind field matches
for (const expSub of expected.subsystems) {
  const actualSub = result.subsystems.find(s => s.name === expSub.name);
  if (actualSub) {
    assert(actualSub.kind === expSub.kind, `${expSub.name} kind="${actualSub.kind}" (expected "${expSub.kind}")`);
  }
}

// Test 4: Dependency matrix — key edges exist
console.log('\nTest 4: Dependency matrix');
const dm = result.dependencyMatrix;
assert('gateway→agents' in dm, 'gateway→agents edge exists');
assert('agents→gateway' in dm, 'agents→gateway edge exists (circular dep)');
assert('channels→gateway' in dm, 'channels→gateway edge exists');
assert('gateway→config' in dm, 'gateway→config edge exists');
assert('agents→utils' in dm, 'agents→utils edge exists');
assert('channels→utils' in dm, 'channels→utils edge exists');

// Test 5: Empty subsystem handling (channels has only 2 files, no internal CALLS)
console.log('\nTest 5: Edge cases');
const channelsSub = result.subsystems.find(s => s.name === 'channels');
assert(channelsSub && channelsSub.files.length === 2, `channels has 2 files (got ${channelsSub?.files.length})`);

// Orphan file: config/schema.ts should still be in config subsystem
const configSub = result.subsystems.find(s => s.name === 'config');
assert(configSub && configSub.files.includes('config/schema.ts'), 'Orphan config/schema.ts assigned to config');

// Test 6: Public exports populated
console.log('\nTest 6: Public exports');
const gatewaySub = result.subsystems.find(s => s.name === 'gateway');
assert(gatewaySub.publicExports.includes('handleRequest'), 'gateway exports handleRequest');
assert(gatewaySub.publicExports.includes('loadSession'), 'gateway exports loadSession');
const agentsSub = result.subsystems.find(s => s.name === 'agents');
assert(agentsSub.publicExports.includes('runAgent'), 'agents exports runAgent');

// Test 7: Run on OpenClaw full snapshot (performance)
console.log('\nTest 7: OpenClaw full snapshot');
const fullSnap = path.join(__dirname, '..', 'snapshots', 'openclaw-full.json');
if (fs.existsSync(fullSnap)) {
  const start = Date.now();
  const fullGraph = GraphStore.loadSnapshot(fullSnap);
  const fullResult = buildSubsystems(fullGraph);
  const elapsed = Date.now() - start;
  assert(fullResult.subsystems.length > 10, `OpenClaw has >10 subsystems (got ${fullResult.subsystems.length})`);
  assert(elapsed < 5000, `Completed in <5s (${elapsed}ms)`);
  assert(fullResult.crossCutting.length > 0, `Detected cross-cutting subsystems: ${fullResult.crossCutting.join(', ')}`);
  console.log(`  OpenClaw: ${fullResult.subsystems.length} subsystems, ${Object.keys(fullResult.dependencyMatrix).length} dep edges, ${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);
