import { Arbiter } from '../src/core/Arbiter.js';

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;
}

function parseSizes(value) {
  if (!value) return null;
  return value.split(',').map(item => Number(item.trim())).filter(Number.isFinite);
}

function createRng(seed) {
  let state = seed >>> 0;
  return () => {
    state = (1664525 * state + 1013904223) >>> 0;
    return state / 0x100000000;
  };
}

function randInt(rng, max) {
  return Math.floor(rng() * max);
}

function buildUsers(arbiter, count, prefix) {
  for (let i = 0; i < count; i++) {
    arbiter.addNode(`${prefix}:${i}`, prefix);
  }
}

function buildOWAUnionScenario(arbiter, size) {
  buildUsers(arbiter, size, 'user');
  buildUsers(arbiter, size, 'resource');

  for (let i = 0; i < size; i++) {
    if (i % 3 === 0) {
      arbiter.addRelation(`user:${i}`, 'viewer', `resource:${i}`, 0.6);
    }
    if (i % 5 === 0) {
      arbiter.addRelation(`user:${i}`, 'owner', `resource:${i}`, 0.9);
    }
  }

  arbiter.setRelationConfig('viewer', { type: 'direct' });
  arbiter.setRelationConfig('owner', { type: 'direct' });
  arbiter.setRelationConfig('can_view_owa', {
    union: {
      rules: [
        { type: 'direct', relation: 'viewer' },
        { type: 'direct', relation: 'owner' }
      ],
      aggregator: 'owa',
      owaWeights: [0.7, 0.3]
    }
  });
}

function buildQueries(size, rng, samples) {
  const queries = [];
  const half = Math.floor(samples / 2);
  for (let i = 0; i < half; i++) {
    const userId = randInt(rng, Math.floor(size / 2)) * 3;
    queries.push({ userKey: `user:${userId}`, objectKey: `resource:${userId}` });
  }
  for (let i = 0; i < samples - half; i++) {
    const userId = randInt(rng, Math.floor(size / 2)) * 3 + 1;
    queries.push({ userKey: `user:${userId}`, objectKey: `resource:${userId}` });
  }
  return queries;
}

function measure(label, iterations, fn) {
  const start = process.hrtime.bigint();
  for (let i = 0; i < iterations; i++) {
    fn();
  }
  const elapsedMs = Number(process.hrtime.bigint() - start) / 1e6;
  return { label, elapsedMs, qps: elapsedMs > 0 ? Math.round((iterations / elapsedMs) * 1000) : 0 };
}

const args = parseArgs(process.argv);
const config = {
  sizes: parseSizes(args.get('sizes')) || [5000, 10000, 20000, 40000],
  samples: Number(args.get('samples') || 2000),
  seed: Number(args.get('seed') || 42),
  maxSeconds: Number(args.get('max-seconds') || 60)
};

const rng = createRng(config.seed);
const startTime = Date.now();

console.log('owa_union_micro_bench');
console.log('size,samples,metric,elapsed_ms,qps');

for (const size of config.sizes) {
  if ((Date.now() - startTime) / 1000 > config.maxSeconds) break;
  const arbiter = new Arbiter();
  buildOWAUnionScenario(arbiter, size);
  const queries = buildQueries(size, rng, config.samples);

  const directViewer = measure('direct_viewer', queries.length, () => {
    const q = queries[randInt(rng, queries.length)];
    arbiter.authChecker.check(q.userKey, 'viewer', q.objectKey, { fastPath: true });
  });

  const directOwner = measure('direct_owner', queries.length, () => {
    const q = queries[randInt(rng, queries.length)];
    arbiter.authChecker.check(q.userKey, 'owner', q.objectKey, { fastPath: true });
  });

  const owaUnion = measure('owa_union', queries.length, () => {
    const q = queries[randInt(rng, queries.length)];
    arbiter.check(q.userKey, 'can_view_owa', q.objectKey, { fastPath: true });
  });

  for (const metric of [directViewer, directOwner, owaUnion]) {
    console.log([size, queries.length, metric.label, Math.round(metric.elapsedMs), metric.qps].join(','));
  }
}
