import { CondensedGraph } from '../src/core/CondensedGraph.js';
import { performance } from 'node:perf_hooks';

function parseArgs(argv) {
  const args = new Map();
  for (let i = 2; i < argv.length; i++) {
    const value = argv[i];
    if (!value.startsWith('--')) continue;
    const [key, inline] = value.slice(2).split('=');
    if (inline !== undefined) {
      args.set(key, inline);
      continue;
    }
    const next = argv[i + 1];
    if (next && !next.startsWith('--')) {
      args.set(key, next);
      i++;
    } else {
      args.set(key, true);
    }
  }
  return args;
}

const args = parseArgs(process.argv);
const edges = Number(args.get('edges') || 200000);
const users = Number(args.get('users') || 10000);
const docs = Number(args.get('docs') || 50000);
const queryCount = Number(args.get('queries') || 10000);

console.log('CondensedGraph load bench');
console.log(`  edges: ${edges}`);
console.log(`  users: ${users}`);
console.log(`  docs: ${docs}`);
console.log(`  queries: ${queryCount}`);

const graph = new CondensedGraph();
const buildStart = performance.now();
for (let i = 0; i < edges; i++) {
  graph.addEdge(
    `user:${i % users}`,
    ['owner', 'editor', 'viewer'][i % 3],
    `doc:${i % docs}`
  );
}
const buildTime = performance.now() - buildStart;

const finalizeStart = performance.now();
graph.finalizePerfectHash();
graph.finalizeWaveletAdjacency({ dropAdjacencyList: true });
const finalizeTime = performance.now() - finalizeStart;

const serializeStart = performance.now();
const buffer = graph.toBinary();
const serializeTime = performance.now() - serializeStart;

const heapBeforeLoad = process.memoryUsage().heapUsed;
const loadStart = performance.now();
const loaded = CondensedGraph.fromBinary(buffer);
const loadTime = performance.now() - loadStart;
const heapAfterLoad = process.memoryUsage().heapUsed;

const usersArr = Array.from({ length: users }, (_, i) => `user:${i}`);
const docsArr = Array.from({ length: docs }, (_, i) => `doc:${i}`);

const queryStart = performance.now();
let hits = 0;
for (let i = 0; i < queryCount; i++) {
  const user = usersArr[i % users];
  const doc = docsArr[i % docs];
  const owner = loaded.findEdge(user, 'owner', doc);
  const editor = loaded.findEdge(user, 'editor', doc);
  const viewer = loaded.findEdge(user, 'viewer', doc);
  if (owner !== null || editor !== null || viewer !== null) hits++;
}
const queryTime = performance.now() - queryStart;
const heapAfterQueries = process.memoryUsage().heapUsed;

const stats = loaded.getStats();
const bufferBytes = buffer.byteLength;
const heapDeltaLoadBytes = heapAfterLoad - heapBeforeLoad;
const heapDeltaQueryBytes = heapAfterQueries - heapAfterLoad;
const toMb = (bytes) => (bytes / 1024 / 1024).toFixed(2);

console.log('\nResults');
console.log(`  build time: ${buildTime.toFixed(2)} ms`);
console.log(`  finalize time: ${finalizeTime.toFixed(2)} ms`);
console.log(`  serialize time: ${serializeTime.toFixed(2)} ms`);
console.log(`  load time: ${loadTime.toFixed(2)} ms`);
console.log(`  snapshot size: ${(bufferBytes / 1024 / 1024).toFixed(2)} MB`);
console.log(`  heap delta (load): ${toMb(heapDeltaLoadBytes)} MB`);
console.log(`  heap delta (post-query): ${toMb(heapDeltaQueryBytes)} MB`);
console.log(`  bytes/edge (snapshot): ${(bufferBytes / edges).toFixed(2)}`);
console.log(`  stats bytes/edge: ${stats.bytesPerEdge.toFixed(2)}`);
console.log(`  query time: ${queryTime.toFixed(2)} ms`);
console.log(`  avg query: ${(queryTime / queryCount * 1000).toFixed(3)} µs`);
console.log(`  queries/sec: ${(queryCount / (queryTime / 1000)).toFixed(0)}`);
console.log(`  hits: ${hits}`);
