import { performance } from 'node:perf_hooks';
import { Arbiter } from '../src/core/Arbiter.js';
import { validateClaimsForLayer } from '../src/core/partial-graph/layer-registry.js';

function percentile(sorted, p) {
  if (!sorted.length) return 0;
  const idx = Math.min(sorted.length - 1, Math.max(0, Math.floor(sorted.length * p) - 1));
  return sorted[idx];
}

function summarize(name, durations) {
  const sorted = [...durations].sort((a, b) => a - b);
  let total = 0;
  for (let i = 0; i < durations.length; i++) total += durations[i];
  const avg = total / durations.length;
  const p95 = percentile(sorted, 0.95);
  const p99 = percentile(sorted, 0.99);
  console.log(`${name}: avg=${avg.toFixed(4)}ms p95=${p95.toFixed(4)}ms p99=${p99.toFixed(4)}ms`);
}

function runBenchmark(name, iterations, fn) {
  const warmup = Math.min(1000, Math.max(100, Math.floor(iterations / 10)));
  for (let i = 0; i < warmup; i++) fn();

  const durations = new Array(iterations);
  for (let i = 0; i < iterations; i++) {
    const start = performance.now();
    fn();
    durations[i] = performance.now() - start;
  }
  summarize(name, durations);
}

function setupBusinessArbiter() {
  const arbiter = new Arbiter({
    partialGraphPolicy: {
      conflict_mode: 'deterministic',
      reducers: {
        request_has_timestamp: 'latest',
        caller_risk_hint: 'strongest'
      }
    }
  });

  arbiter.addNode('user:1', 'user');
  arbiter.addNode('group:1', 'group');
  arbiter.addNode('mid:1', 'intermediate');
  arbiter.addNode('doc:1', 'document');
  arbiter.addNode('feature:1', 'feature');
  arbiter.addNode('value:1', 'value');

  arbiter.setRelationConfig('request_has_timestamp', {
    type: 'direct',
    partial_graph: { reducer: 'latest' }
  });
  arbiter.setRelationConfig('caller_risk_hint', {
    type: 'direct',
    partial_graph: { reducer: 'strongest' }
  });
  arbiter.setRelationConfig('request_has_id', { type: 'direct' });

  arbiter.setRelationConfig('can_chain', {
    type: 'chain',
    steps: [
      { relation: 'request_has_timestamp', direction: 'out' },
      { relation: 'caller_risk_hint', direction: 'out' }
    ]
  });

  arbiter.setRelationConfig('can_reach', {
    type: 'multi_hop',
    relation: 'caller_risk_hint',
    maxDepth: 3
  });

  arbiter.setRelationConfig('can_access', {
    type: 'tuple_to_userset',
    tuplesetRelation: 'request_has_id',
    computedRelation: 'request_has_timestamp',
    reverse: false
  });

  arbiter.setRelationConfig('can_pay', {
    type: 'relational_comparator',
    left: {
      rule: { type: 'direct', relation: 'request_has_timestamp' },
      extractValue: true,
      aggregation: 'max',
      decayRate: 0,
      decayFunction: 'rational'
    },
    right: {
      rule: { type: 'direct', relation: 'request_has_id', evaluateFrom: 'object' },
      extractValue: true,
      aggregation: 'min',
      decayRate: 0,
      decayFunction: 'rational',
      evaluateFrom: 'object'
    },
    comparator: '>=',
    fallbackBehavior: 'deny'
  });

  arbiter.addRelation('group:1', 'caller_risk_hint', 'doc:1', 0.9);
  arbiter.addRelation('feature:1', 'request_has_id', 'feature:1', { value: 50, possibility: 1 });

  const partialGraph = {
    options: {
      reducers: {
        request_has_timestamp: 'latest',
        caller_risk_hint: 'strongest'
      }
    },
    relations: [
      {
        src: 'user:1',
        relation: 'request_has_timestamp',
        dst: 'group:1',
        possibility: 0.95,
        value: 120,
        updated_last_at: 100,
        layer_name: 'request_observed',
        source_class: 'gateway_observed'
      },
      {
        src: 'user:1',
        relation: 'request_has_timestamp',
        dst: 'group:1',
        possibility: 0.2,
        value: 20,
        updated_last_at: 200,
        layer_name: 'request_observed',
        source_class: 'gateway_observed'
      },
      {
        src: 'group:1',
        relation: 'caller_risk_hint',
        dst: 'doc:1',
        possibility: 0.8,
        layer_name: 'caller_declared',
        source_class: 'caller_input'
      },
      {
        src: 'doc:1',
        relation: 'request_has_id',
        dst: 'group:1',
        possibility: 1,
        layer_name: 'request_observed'
      },
      {
        src: 'user:1',
        relation: 'request_has_timestamp',
        dst: 'feature:1',
        value: 20,
        possibility: 1,
        updated_last_at: 200,
        layer_name: 'request_observed'
      }
    ]
  };

  return { arbiter, partialGraph };
}

function run() {
  const { arbiter, partialGraph } = setupBusinessArbiter();

  const conformantClaims = [
    { relation: 'from_ip', object: 'ip:10.0.0.1' },
    { relation: 'request_has_id', object: 'request:abc' }
  ];
  const nonConformantClaims = [
    { relation: 'delegated_authority', object: 'resource:x' }
  ];

  console.log('Business operations benchmark');
  runBenchmark('business.layer_conformance.accept', 15000, () => {
    validateClaimsForLayer('request_observed', conformantClaims);
  });
  runBenchmark('business.layer_conformance.reject', 15000, () => {
    validateClaimsForLayer('caller_declared', nonConformantClaims);
  });

  runBenchmark('business.partial_graph_policy.snapshot', 15000, () => {
    arbiter.getPartialGraphPolicySnapshot();
  });

  runBenchmark('business.auth.direct_partial', 12000, () => {
    arbiter.check('user:1', 'request_has_timestamp', 'group:1', { partialGraph });
  });
  runBenchmark('business.auth.chain_partial', 12000, () => {
    arbiter.check('user:1', 'can_chain', 'doc:1', { partialGraph });
  });
  runBenchmark('business.auth.multi_hop_partial', 12000, () => {
    arbiter.check('user:1', 'can_reach', 'doc:1', { partialGraph });
  });
  runBenchmark('business.auth.tuple_to_userset_partial', 12000, () => {
    arbiter.check('user:1', 'can_access', 'doc:1', { partialGraph });
  });
  runBenchmark('business.auth.relational_comparator_partial', 12000, () => {
    arbiter.check('user:1', 'can_pay', 'feature:1', { partialGraph });
  });

  runBenchmark('business.explain.chain_debug', 6000, () => {
    arbiter.explain('user:1', 'can_chain', 'doc:1', {
      partialGraph,
      includeMeta: true,
      collectValues: true
    });
  });
}

run();
