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

const graph = GraphStore.loadSnapshot(SNAPSHOT);
const subsystemMap = buildSubsystems(graph, { minTraffic: 3, crossCuttingThreshold: 0.6 });
const index = buildFlowIndex(graph, subsystemMap);

console.log('=== 7C: Flow Tracer Tests ===\n');

// Test 1: Simple linear flow across subsystems
console.log('Test 1: Linear flow');
const t1 = traceFlow('channels/telegram.ts:onTelegramMessage', index);
assert(t1.subsystemSequence.includes('channels'), 'Started in channels');
assert(t1.subsystemSequence.includes('gateway'), 'Crossed to gateway');
assert(t1.flow.some(f => f.entity === 'gateway/server.ts:handleRequest' && f.crossedVia === 'CALLS'), 'Recorded CALLS crossing');

// Test 2: Cycle detection (gateway <-> agents)
console.log('\nTest 2: Cycle detection');
const t2 = traceFlow('gateway/session.ts:refreshSession', index);
assert(t2.subsystemSequence.includes('gateway'), 'Started in gateway');
assert(t2.subsystemSequence.includes('agents'), 'Crossed to agents');
assert(t2.flow.length < 20, 'Trace terminated without infinite loop');

// Test 3: God object exclusion
console.log('\nTest 3: God object exclusion');
const indexLowThreshold = buildFlowIndex(graph, subsystemMap, { godThreshold: 2 });
const t3 = traceFlow('channels/telegram.ts:onTelegramMessage', indexLowThreshold);
assert(t3.excludedNodes.includes('utils/logger.ts:log'), 'logger.ts:log was excluded as god object');
assert(!t3.flow.some(f => f.entity === 'utils/logger.ts:log'), 'logger does not appear in flow');

// Test 4: Depth limit
console.log('\nTest 4: Depth limit');
const t4 = traceFlow('channels/telegram.ts:onTelegramMessage', index, { maxDepth: 1.2 });
assert(t4.flow.some(f => f.entity === 'gateway/server.ts:handleRequest'), 'Included depth 1 node');
assert(!t4.flow.some(f => f.entity === 'gateway/session.ts:loadSession'), 'Excluded depth 1.5 node');

// Test 5: Empty trace
console.log('\nTest 5: Empty trace');
const t5 = traceFlow('utils/crypto.ts:hashString', index);
assert(t5.flow.length === 1, 'Only entry point in flow');
assert(t5.subsystemSequence.length === 1, 'Only one subsystem');

// Test 6: Invalid entry point
console.log('\nTest 6: Invalid entry point');
const t6 = traceFlow('nonexistent/file.ts:foo', index);
assert(t6.error !== undefined, 'Returns error for invalid entry point');
assert(t6.flow.length === 0, 'Empty flow for invalid entry');

// Test 7: Multiple traces reuse same index (performance)
console.log('\nTest 7: Index reuse + OpenClaw performance');
const fullSnap = path.join(__dirname, '..', 'snapshots', 'openclaw-full.json');
if (fs.existsSync(fullSnap)) {
  const fullGraph = GraphStore.loadSnapshot(fullSnap);
  const fullSubs = buildSubsystems(fullGraph);

  const indexStart = Date.now();
  const fullIndex = buildFlowIndex(fullGraph, fullSubs, { godThreshold: 50 });
  const indexTime = Date.now() - indexStart;
  console.log(`  Index built in ${indexTime}ms`);

  // Run 3 traces on the same index
  const entries = ['cli/route.ts:tryRouteCli', 'gateway/session-utils.ts', 'pairing/pairing-store.ts'];
  let totalTraceTime = 0;
  for (const ep of entries) {
    const start = Date.now();
    const t = traceFlow(ep, fullIndex, { maxDepth: 8 });
    const elapsed = Date.now() - start;
    totalTraceTime += elapsed;
    console.log(`  Trace "${ep}": ${t.flow.length} steps, ${t.subsystemSequence.length} subsystems, ${elapsed}ms`);
  }

  assert(indexTime < 3000, `Index built in <3s (${indexTime}ms)`);
  assert(totalTraceTime < 5000, `All 3 traces completed in <5s (${totalTraceTime}ms)`);
} else {
  console.log('  (skipped — openclaw-full.json not found)');
}

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