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

// Create scenario matching owa_comparator_nested
const arbiter = new Arbiter();
const SIZE = 5000;

// Build nodes
for (let i = 0; i < SIZE; i++) {
  arbiter.addNode(`user:${i}`, 'user');
  arbiter.addNode(`resource:${i}`, 'resource');
}

// Build relations (same as owa-comparator-micro-bench)
for (let i = 0; i < SIZE; i++) {
  const isSafe = i % 2 === 0;
  const score = isSafe ? 20 : 80;
  const bonus = isSafe ? 5 : 15;
  const noise = isSafe ? 0 : 10;
  arbiter.addRelation(`user:${i}`, 'risk_score', `resource:${i}`, 1.0, { value: score });
  arbiter.addRelation(`user:${i}`, 'risk_bonus', `resource:${i}`, 1.0, { value: bonus });
  arbiter.addRelation(`user:${i}`, 'risk_noise', `resource:${i}`, 1.0, { value: noise });
  arbiter.addRelation(`resource:${i}`, 'risk_limit', `resource:${i}`, 1.0, { value: 40 });
  arbiter.addRelation(`resource:${i}`, 'risk_cap', `resource:${i}`, 1.0, { value: 45 });
}

// Set relation configs
arbiter.setRelationConfig('risk_score', { type: 'direct' });
arbiter.setRelationConfig('risk_bonus', { type: 'direct' });
arbiter.setRelationConfig('risk_noise', { type: 'direct' });
arbiter.setRelationConfig('risk_limit', { type: 'direct' });
arbiter.setRelationConfig('risk_cap', { type: 'direct' });

arbiter.setRelationConfig('risk_ok_owa', {
  type: 'relational_comparator',
  comparator: '<=',
  fallbackBehavior: 'deny',
  left: {
    rule: {
      union: {
        rules: [
          { type: 'direct', relation: 'risk_score' },
          { type: 'direct', relation: 'risk_bonus' },
          { type: 'direct', relation: 'risk_noise' }
        ],
        aggregator: 'owa',
        owaWeights: [0.5, 0.3, 0.2]
      }
    },
    extractValue: true,
    valueRelation: 'risk_score',
    aggregator: 'owa',
    owaWeights: [0.5, 0.3, 0.2]
  },
  right: {
    rule: {
      union: {
        rules: [
          { type: 'direct', relation: 'risk_limit' },
          { type: 'direct', relation: 'risk_cap' }
        ],
        aggregator: 'owa',
        owaWeights: [0.6, 0.4]
      }
    },
    extractValue: true,
    valueRelation: 'risk_limit',
    evaluateFrom: 'object',
    aggregator: 'owa',
    owaWeights: [0.6, 0.4]
  }
});

// Warm up
for (let i = 0; i < 100; i++) {
  arbiter.check(`user:${i}`, 'risk_ok_owa', `resource:${i}`, { fastPath: true });
}

// Profile with timing at each step
const ITERATIONS = 1000;
let totalMs = 0;

// Detailed timing breakdown
const timings = {
  total: 0,
  nodeIdLookup: 0,
  configGet: 0,
  visitedCheck: 0,
  ruleEval: 0
};

const start = process.hrtime.bigint();
for (let i = 0; i < ITERATIONS; i++) {
  const idx = i % SIZE;
  arbiter.check(`user:${idx}`, 'risk_ok_owa', `resource:${idx}`, { fastPath: true });
}
totalMs = Number(process.hrtime.bigint() - start) / 1e6;
const qps = Math.round((ITERATIONS / totalMs) * 1000);

console.log(`Total: ${totalMs.toFixed(2)}ms for ${ITERATIONS} checks = ${qps} QPS`);
console.log(`Per check: ${(totalMs / ITERATIONS).toFixed(4)}ms`);

// Now let's profile object allocations
console.log('\n--- Checking allocation patterns ---');

// Check visited set behavior
const visited = new Set();
const visitKey1 = { userKey: 'user:0', relation: 'test', objectKey: 'resource:0' };
const visitKey2 = { userKey: 'user:0', relation: 'test', objectKey: 'resource:0' };
visited.add(visitKey1);
console.log(`Same keys, different objects in Set: ${visited.has(visitKey2)}`);  // false - reference equality

// Count how many allocations happen per check
console.log('\n--- Checking comparison: direct vs comparator ---');

// Profile direct check
const directStart = process.hrtime.bigint();
for (let i = 0; i < ITERATIONS; i++) {
  const idx = i % SIZE;
  arbiter.check(`user:${idx}`, 'risk_score', `resource:${idx}`, { fastPath: true });
}
const directMs = Number(process.hrtime.bigint() - directStart) / 1e6;
console.log(`Direct: ${directMs.toFixed(2)}ms = ${Math.round((ITERATIONS / directMs) * 1000)} QPS`);

// Profile union eval only
const unionRule = {
  union: {
    rules: [
      { type: 'direct', relation: 'risk_score' },
      { type: 'direct', relation: 'risk_bonus' },
      { type: 'direct', relation: 'risk_noise' }
    ],
    aggregator: 'owa',
    owaWeights: [0.5, 0.3, 0.2]
  }
};
const unionStart = process.hrtime.bigint();
for (let i = 0; i < ITERATIONS; i++) {
  const idx = i % SIZE;
  const userId = arbiter.nodeIdByKey.get(`user:${idx}`);
  const objectId = arbiter.nodeIdByKey.get(`resource:${idx}`);
  arbiter.authChecker.ruleEvaluator.evaluateRule(
    userId, `user:${idx}`, objectId, `resource:${idx}`,
    unionRule, new Set(), 'risk_ok_owa', { fastPath: true }
  );
}
const unionMs = Number(process.hrtime.bigint() - unionStart) / 1e6;
console.log(`Union eval: ${unionMs.toFixed(2)}ms = ${Math.round((ITERATIONS / unionMs) * 1000)} QPS`);

// What's the overhead of authChecker.check vs direct ruleEvaluator call?
const checkStart = process.hrtime.bigint();
for (let i = 0; i < ITERATIONS; i++) {
  const idx = i % SIZE;
  arbiter.authChecker.check(`user:${idx}`, 'risk_ok_owa', `resource:${idx}`, { fastPath: true });
}
const checkMs = Number(process.hrtime.bigint() - checkStart) / 1e6;
console.log(`authChecker.check: ${checkMs.toFixed(2)}ms = ${Math.round((ITERATIONS / checkMs) * 1000)} QPS`);

// Direct to evaluateRule for relational_comparator
const comparatorRule = arbiter.relationConfigs.get('risk_ok_owa');
const comparatorStart = process.hrtime.bigint();
for (let i = 0; i < ITERATIONS; i++) {
  const idx = i % SIZE;
  const userId = arbiter.nodeIdByKey.get(`user:${idx}`);
  const objectId = arbiter.nodeIdByKey.get(`resource:${idx}`);
  arbiter.authChecker.ruleEvaluator.evaluateRule(
    userId, `user:${idx}`, objectId, `resource:${idx}`,
    comparatorRule, new Set(), 'risk_ok_owa', { fastPath: true }
  );
}
const comparatorMs = Number(process.hrtime.bigint() - comparatorStart) / 1e6;
console.log(`ruleEvaluator.evaluateRule (comparator): ${comparatorMs.toFixed(2)}ms = ${Math.round((ITERATIONS / comparatorMs) * 1000)} QPS`);

console.log('\n--- Summary ---');
console.log(`Direct check baseline: ${Math.round((ITERATIONS / directMs) * 1000)} QPS`);
console.log(`Union eval: ${Math.round((ITERATIONS / unionMs) * 1000)} QPS (${(unionMs/directMs).toFixed(1)}x slower than direct)`);
console.log(`Full comparator via ruleEvaluator: ${Math.round((ITERATIONS / comparatorMs) * 1000)} QPS (${(comparatorMs/directMs).toFixed(1)}x slower than direct)`);
console.log(`Full comparator via authChecker: ${Math.round((ITERATIONS / checkMs) * 1000)} QPS (${(checkMs/directMs).toFixed(1)}x slower than direct)`);
