import { performance } from 'perf_hooks';
import { Arbiter } from '../src/index.js';

console.log('⛓️  ChainRule Performance Benchmark Analysis\n');

// Benchmark utilities
class ChainBenchmark {
  constructor(name) {
    this.name = name;
    this.results = [];
    this.labels = [];
  }

  async run(fn, iterations = 1000, label = '') {
    // Warmup
    for (let i = 0; i < Math.min(10, iterations / 10); i++) {
      try {
        await fn();
      } catch (e) {
        throw e;
      }
    }

    // Actual benchmark
    const times = [];
    const totalStart = performance.now();
    for (let i = 0; i < iterations; i++) {
      try {
        const start = performance.now();
        await fn();
        const end = performance.now();
        times.push(end - start);
      } catch (e) {
        throw e;
      }
    }
    const totalTime = performance.now() - totalStart;
    const avg = times.reduce((a, b) => a + b, 0) / times.length;
    const min = Math.min(...times);
    const max = Math.max(...times);
    const p50 = times.sort((a, b) => a - b)[Math.floor(times.length * 0.5)];
    const p95 = times[Math.floor(times.length * 0.95)];
    const p99 = times[Math.floor(times.length * 0.99)];
    const qps = Math.round(iterations / (totalTime / 1000));
    this.results.push({
      avg: avg.toFixed(3),
      min: min.toFixed(3),
      max: max.toFixed(3),
      p50: p50.toFixed(3),
      p95: p95.toFixed(3),
      p99: p99.toFixed(3),
      qps: qps
    });
    this.labels.push(label);
    return { avg, min, max, p50, p95, p99, qps };
  }

  report() {
    console.log('Configuration                    | Avg (ms) | P50 (ms) | P95 (ms) | P99 (ms) |     QPS | Speedup');
    console.log('---------------------------------|----------|----------|----------|----------|---------|--------');
    const baselineQPS = this.results[0].qps;
    this.results.forEach((result, i) => {
      const label = this.labels[i] || `Test ${i + 1}`;
      const speedup = i === 0 ? '1.00x' : `${(result.qps / baselineQPS).toFixed(2)}x`;
      console.log(`${label.padEnd(32)} | ${result.avg.padStart(8)} | ${result.p50.padStart(8)} | ${result.p95.padStart(8)} | ${result.p99.padStart(8)} | ${result.qps.toString().padStart(7)} | ${speedup.padStart(6)}`);
    });
  }
}

// Enterprise organizational data generator
function setupEnterpriseChainGraph(scale = 'medium') {
  const scales = {
    small: { users: 200, groups: 50, projects: 20, departments: 8, budgets: 100 },
    medium: { users: 2000, groups: 200, projects: 100, departments: 15, budgets: 500 },
    large: { users: 10000, groups: 1000, projects: 500, departments: 25, budgets: 2000 },
    xlarge: { users: 20000, groups: 5000, projects: 2000, departments: 40, budgets: 8000 },
    xxlarge: { users: 100000, groups: 10000, projects: 5000, departments: 50, budgets: 20000 }
  };
  
  const config = scales[scale];
  console.log(`🏢 Setting up ${scale} enterprise graph: ${config.users} users, ${config.groups} groups, ${config.projects} projects`);
  
  const arbiter = new Arbiter({
    fastConstructionMode: true,
    enableInference: false  // Disable by default for clean baseline measurements
  });
  
  // Create organizational hierarchy: users → groups → departments → divisions → company
  const entities = {
    users: [],
    groups: [],
    departments: [],
    divisions: ['engineering', 'sales', 'marketing', 'finance', 'operations'],
    projects: [],
    budgets: [],
    resources: [],
    facilities: []
  };
  
  // Generate users
  for (let i = 0; i < config.users; i++) {
    const userKey = `user:emp${i}`;
    entities.users.push(userKey);
    arbiter.addNode(userKey, 'user');
  }
  
  // Generate groups (teams within departments)
  for (let i = 0; i < config.groups; i++) {
    const groupKey = `group:team${i}`;
    entities.groups.push(groupKey);
    arbiter.addNode(groupKey, 'group');
  }
  
  // Generate departments
  for (let i = 0; i < config.departments; i++) {
    const deptKey = `dept:dept${i}`;
    entities.departments.push(deptKey);
    arbiter.addNode(deptKey, 'department');
  }
  
  // Generate divisions
  entities.divisions.forEach(div => {
    const divKey = `division:${div}`;
    arbiter.addNode(divKey, 'division');
  });
  
  // Add company root
  arbiter.addNode('company:acme', 'company');
  
  // Generate projects
  for (let i = 0; i < config.projects; i++) {
    const projectKey = `project:proj${i}`;
    entities.projects.push(projectKey);
    arbiter.addNode(projectKey, 'project');
  }
  
  // Generate budgets with values
  for (let i = 0; i < config.budgets; i++) {
    const budgetKey = `budget:budget${i}`;
    const value = Math.floor(Math.random() * 5000000) + 100000; // $100K to $5M
    entities.budgets.push({ key: budgetKey, value });
    arbiter.addNode(budgetKey, 'budget');
  }
  
  // Generate resources (servers, databases, etc.)
  for (let i = 0; i < config.projects / 2; i++) {
    const resourceKey = `resource:res${i}`;
    const cost = Math.floor(Math.random() * 100000) + 5000; // $5K to $100K
    entities.resources.push({ key: resourceKey, cost });
    arbiter.addNode(resourceKey, 'resource');
  }
  
  // Generate facilities
  const facilityNames = ['hq', 'east-office', 'west-office', 'remote', 'datacenter'];
  facilityNames.forEach(name => {
    const facilityKey = `facility:${name}`;
    entities.facilities.push(facilityKey);
    arbiter.addNode(facilityKey, 'facility');
  });
  
  console.log('   🔗 Building organizational chains...');
  
  // Build 5-level organizational hierarchy: user → group → department → division → company
  
  // User → Group membership
  entities.users.forEach(user => {
    // Each user belongs to 1-3 groups
    const groupCount = Math.floor(Math.random() * 3) + 1;
    for (let i = 0; i < groupCount; i++) {
      const group = entities.groups[Math.floor(Math.random() * entities.groups.length)];
      arbiter.addRelation(user, 'member_of', group);
    }
  });
  
  // Group → Department membership
  entities.groups.forEach(group => {
    const dept = entities.departments[Math.floor(Math.random() * entities.departments.length)];
    arbiter.addRelation(group, 'belongs_to', dept);
  });
  
  // Department → Division membership
  entities.departments.forEach(dept => {
    const division = entities.divisions[Math.floor(Math.random() * entities.divisions.length)];
    arbiter.addRelation(dept, 'part_of', `division:${division}`);
  });
  
  // Division → Company membership
  entities.divisions.forEach(div => {
    arbiter.addRelation(`division:${div}`, 'part_of', 'company:acme');
  });
  
  // Project chains: user → group → project → budget
  entities.projects.forEach(project => {
    // Projects owned by groups
    const ownerGroup = entities.groups[Math.floor(Math.random() * entities.groups.length)];
    arbiter.addRelation(ownerGroup, 'manages', project);
    
    // Projects have budgets
    const budget = entities.budgets[Math.floor(Math.random() * entities.budgets.length)];
    arbiter.addRelation(project, 'has_budget', budget.key, { value: budget.value });
    
    // Projects have resource costs
    if (Math.random() < 0.7) {
      const resource = entities.resources[Math.floor(Math.random() * entities.resources.length)];
      arbiter.addRelation(project, 'uses_resource', resource.key, { cost: resource.cost });
    }
  });
  
  // Facility chains: user → facility, department → facility
  entities.users.forEach(user => {
    const facility = entities.facilities[Math.floor(Math.random() * entities.facilities.length)];
    arbiter.addRelation(user, 'located_in', facility);
  });
  
  entities.departments.forEach(dept => {
    const facility = entities.facilities[Math.floor(Math.random() * entities.facilities.length)];
    arbiter.addRelation(dept, 'operates_in', facility);
  });
  
  // Configure basic relation types
  const relationTypes = [
    'member_of', 'belongs_to', 'part_of', 'manages', 'has_budget', 
    'uses_resource', 'located_in', 'operates_in', 'can_access', 'has_permission'
  ];
  
  relationTypes.forEach(rel => {
    arbiter.setRelationConfig(rel, { type: 'direct' });
  });
  
  console.log('   ✅ Enterprise chain graph ready');
  
  // Initialize PLTC for reachability checks
  console.log('   ⚡ Initializing PLTC indices...');
  const initStart = Date.now();
  arbiter.graphManager.initializeReachabilityChecker();
  const initTime = Date.now() - initStart;
  console.log(`   ✅ PLTC initialized in ${initTime}ms`);
  
  return { arbiter, ...entities };
}

// Benchmark 1: Chain Length Performance
async function benchmarkChainLength() {
  console.log('📏 Benchmark 1: Chain Length Performance Impact\n');
  
  const graph = setupEnterpriseChainGraph('medium');
  const benchmark = new ChainBenchmark('Chain Length Performance');
  
  // Baseline: Direct access (no chain)
  graph.arbiter.setRelationConfig('direct_access', {
    type: 'direct',
    relation: 'can_access'
  });
  
  // Create a substantial number of direct access relations for realistic testing
  // Instead of 100 random relations, create relations for every user to some projects
  const directAccessPairs = [];
  graph.users.forEach((user, i) => {
    // Each user has direct access to 1-3 projects
    const numProjects = Math.floor(Math.random() * 3) + 1;
    for (let j = 0; j < numProjects; j++) {
      const project = graph.projects[(i * 7 + j) % graph.projects.length]; // Deterministic but spread out
      graph.arbiter.addRelation(user, 'can_access', project);
      directAccessPairs.push({ user, project });
    }
  });
  
  console.log(`   ✅ Created ${directAccessPairs.length} direct access relations (${(directAccessPairs.length / (graph.users.length * graph.projects.length) * 100).toFixed(1)}% coverage)`);
  
  await benchmark.run(() => {
    // Test EXISTING relations for accurate performance measurement
    const pair = directAccessPairs[Math.floor(Math.random() * directAccessPairs.length)];
    return graph.arbiter.check(pair.user, 'direct_access', pair.project, { noInfer: true });
  }, 1000, 'Direct Access (baseline)');
  
  // 2-step chain: user → group → project
  graph.arbiter.setRelationConfig('chain_2_step', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' }
    ]
  });
  
  // Build collection of valid 2-step chain paths for testing
  const validChain2Paths = [];
  graph.users.forEach(user => {
    // Find groups this user belongs to
    const userGroups = graph.arbiter.relationManager.getRelationsFromSrc(user, 'member_of');
    userGroups.forEach(groupRel => {
      // Find projects this group manages
      const groupProjects = graph.arbiter.relationManager.getRelationsFromSrc(groupRel.object, 'manages');
      groupProjects.forEach(projectRel => {
        validChain2Paths.push({ user, project: projectRel.object });
      });
    });
  });
  
  console.log(`   ✅ Found ${validChain2Paths.length} valid 2-step chain paths (user→group→project)`);
  
  await benchmark.run(() => {
    // Test EXISTING chain paths for accurate performance measurement
    if (validChain2Paths.length === 0) {
      // Fallback to random if no valid paths found
      const user = graph.users[Math.floor(Math.random() * graph.users.length)];
      const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
      return graph.arbiter.check(user, 'chain_2_step', project, { noInfer: true });
    } else {
      const path = validChain2Paths[Math.floor(Math.random() * validChain2Paths.length)];
      return graph.arbiter.check(path.user, 'chain_2_step', path.project, { noInfer: true });
    }
  }, 1000, '2-Step Chain (user→group→project)');
  
  // 3-step chain: user → group → department → division
  graph.arbiter.setRelationConfig('chain_3_step', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'belongs_to', direction: 'out' },
      { relation: 'part_of', direction: 'out' }
    ]
  });
  
  // Build collection of valid 3-step chain paths for testing
  const validChain3Paths = [];
  graph.users.forEach(user => {
    // Find groups this user belongs to
    const userGroups = graph.arbiter.relationManager.getRelationsFromSrc(user, 'member_of');
    userGroups.forEach(groupRel => {
      // Find departments this group belongs to
      const groupDepts = graph.arbiter.relationManager.getRelationsFromSrc(groupRel.object, 'belongs_to');
      groupDepts.forEach(deptRel => {
        // Find divisions this department is part of
        const deptDivisions = graph.arbiter.relationManager.getRelationsFromSrc(deptRel.object, 'part_of');
        deptDivisions.forEach(divisionRel => {
          validChain3Paths.push({ user, division: divisionRel.object });
        });
      });
    });
  });
  
  console.log(`   ✅ Found ${validChain3Paths.length} valid 3-step chain paths (user→group→dept→division)`);

  await benchmark.run(() => {
    // Test EXISTING chain paths for accurate performance measurement
    if (validChain3Paths.length === 0) {
      // Fallback to random if no valid paths found
      const user = graph.users[Math.floor(Math.random() * graph.users.length)];
      const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)];
      return graph.arbiter.check(user, 'chain_3_step', `division:${division}`, { noInfer: true });
    } else {
      const path = validChain3Paths[Math.floor(Math.random() * validChain3Paths.length)];
      return graph.arbiter.check(path.user, 'chain_3_step', path.division, { noInfer: true });
    }
  }, 1000, '3-Step Chain (user→group→dept→division)');
  
  // 4-step chain: user → group → project → budget → facility (create a valid 4-step path)
  // First add budget→facility relationships to create valid 4-step chains
  graph.budgets.forEach(budget => {
    const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)];
    graph.arbiter.addRelation(budget.key, 'allocated_to', facility);
  });
  
  graph.arbiter.setRelationConfig('allocated_to', { type: 'direct' });
  graph.arbiter.setRelationConfig('chain_4_step', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },     // user → group
      { relation: 'manages', direction: 'out' },       // group → project  
      { relation: 'has_budget', direction: 'out' },    // project → budget
      { relation: 'allocated_to', direction: 'out' }   // budget → facility
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)];
    return graph.arbiter.check(user, 'chain_4_step', facility, { noInfer: true });
  }, 1000, '4-Step Chain (user→group→project→budget→facility)');
  
  // 5-step chain: user → group → department → division → company → facility
  // Add company→facility relationship for valid 5-step chain
  graph.facilities.forEach(facility => {
    graph.arbiter.addRelation('company:acme', 'operates', facility);
  });
  
  graph.arbiter.setRelationConfig('operates', { type: 'direct' });
  graph.arbiter.setRelationConfig('chain_5_step', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },     // user → group
      { relation: 'belongs_to', direction: 'out' },    // group → department
      { relation: 'part_of', direction: 'out' },       // department → division
      { relation: 'part_of', direction: 'out' },       // division → company
      { relation: 'operates', direction: 'out' }       // company → facility
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)];
    return graph.arbiter.check(user, 'chain_5_step', facility, { noInfer: true });
  }, 1000, '5-Step Chain (user→group→dept→div→company→facility)');
  
  benchmark.report();
}

// Benchmark 2: ChainRule vs Traditional Approaches
async function benchmarkVsTraditional() {
  console.log('⚔️  Benchmark 2: ChainRule vs Traditional Authorization\n');
  
  const graph = setupEnterpriseChainGraph('medium');
  const benchmark = new ChainBenchmark('ChainRule vs Traditional');
  
  // Traditional ParentRule approach
  graph.arbiter.setRelationConfig('traditional_parent', {
    type: 'parent',
    parentRelation: 'manages',
    relation: 'member_of'
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'traditional_parent', project, { noInfer: true });
  }, 1000, 'ParentRule (baseline)');
  
  // Equivalent ChainRule approach
  graph.arbiter.setRelationConfig('chain_equivalent', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' }
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'chain_equivalent', project, { noInfer: true });
  }, 1000, 'ChainRule (equivalent logic)');
  
  // MultiHopRule approach
  graph.arbiter.setRelationConfig('multihop_approach', {
    type: 'multi_hop',
    relation: 'member_of',
    maxDepth: 3,
    pathAggregation: 'max'
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'multihop_approach', project, { noInfer: true });
  }, 1000, 'MultiHopRule (flexible paths)');
  
  // ChainRule with reverse direction
  graph.arbiter.setRelationConfig('chain_reverse', {
    type: 'chain',
    steps: [
      { relation: 'manages', direction: 'in' },
      { relation: 'member_of', direction: 'in' }
    ]
  });
  
  await benchmark.run(() => {
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    return graph.arbiter.check(project, 'chain_reverse', user, { noInfer: true });
  }, 1000, 'ChainRule (reverse direction)');
  
  benchmark.report();
}

// Benchmark 3: Value Extraction Performance
async function benchmarkValueExtraction() {
  console.log('💰 Benchmark 3: Value Extraction Performance\n');
  
  const graph = setupEnterpriseChainGraph('medium');
  const benchmark = new ChainBenchmark('Value Extraction Performance');
  
  // Chain without value extraction (baseline)
  graph.arbiter.setRelationConfig('chain_no_values', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' },
      { relation: 'has_budget', direction: 'out' }
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)];
    return graph.arbiter.check(user, 'chain_no_values', budget.key);
  }, 1000, 'Chain (no value extraction)');
  
  // Chain with value extraction - SUM aggregation
  graph.arbiter.setRelationConfig('chain_sum_values', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' },
      { relation: 'has_budget', direction: 'out' }
    ],
    extractValues: true,
    extractFrom: 2,
    extractRelation: 'has_budget',
    valueAggregation: 'sum'
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)];
    return graph.arbiter.check(user, 'chain_sum_values', budget.key);
  }, 1000, 'Chain with SUM aggregation');
  
  // Chain with value extraction - MAX aggregation
  graph.arbiter.setRelationConfig('chain_max_values', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' },
      { relation: 'has_budget', direction: 'out' }
    ],
    extractValues: true,
    extractFrom: 2,
    extractRelation: 'has_budget',
    valueAggregation: 'max'
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)];
    return graph.arbiter.check(user, 'chain_max_values', budget.key);
  }, 1000, 'Chain with MAX aggregation');
  
  // Chain with value extraction - MIN aggregation
  graph.arbiter.setRelationConfig('chain_min_values', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' },
      { relation: 'has_budget', direction: 'out' }
    ],
    extractValues: true,
    extractFrom: 2,
    extractRelation: 'has_budget',
    valueAggregation: 'min'
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)];
    return graph.arbiter.check(user, 'chain_min_values', budget.key);
  }, 1000, 'Chain with MIN aggregation');
  
  // Chain with OWA fusion aggregation
  graph.arbiter.setRelationConfig('chain_owa_values', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' },
      { relation: 'has_budget', direction: 'out' }
    ],
    extractValues: true,
    extractFrom: 2,
    extractRelation: 'has_budget',
    valueAggregation: 'optimistic',
    owaWeights: [0.7, 0.5, 0.3, 0.1]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const budget = graph.budgets[Math.floor(Math.random() * graph.budgets.length)];
    return graph.arbiter.check(user, 'chain_owa_values', budget.key);
  }, 1000, 'Chain with OWA aggregation');
  
  benchmark.report();
}

// Benchmark 4: Direction and Pattern Performance
async function benchmarkDirectionPatterns() {
  console.log('🧭 Benchmark 4: Chain Direction and Pattern Performance\n');
  
  const graph = setupEnterpriseChainGraph('medium');
  const benchmark = new ChainBenchmark('Direction and Pattern Performance');
  
  // Forward chain (baseline)
  graph.arbiter.setRelationConfig('forward_chain', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'belongs_to', direction: 'out' },
      { relation: 'part_of', direction: 'out' }
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)];
    return graph.arbiter.check(user, 'forward_chain', `division:${division}`);
  }, 1000, 'Forward Chain (baseline)');
  
  // Backward chain
  graph.arbiter.setRelationConfig('backward_chain', {
    type: 'chain',
    steps: [
      { relation: 'part_of', direction: 'in' },
      { relation: 'belongs_to', direction: 'in' },
      { relation: 'member_of', direction: 'in' }
    ]
  });
  
  await benchmark.run(() => {
    const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)];
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    return graph.arbiter.check(`division:${division}`, 'backward_chain', user);
  }, 1000, 'Backward Chain');
  
  // Mixed direction chain
  graph.arbiter.setRelationConfig('mixed_chain', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },    // user → group
      { relation: 'belongs_to', direction: 'in' },    // group ← department
      { relation: 'operates_in', direction: 'out' }   // department → facility
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)];
    return graph.arbiter.check(user, 'mixed_chain', facility);
  }, 1000, 'Mixed Direction Chain');
  
  // Parallel chains pattern (testing multiple chain endpoints)
  graph.arbiter.setRelationConfig('parallel_chains', {
    union: [
      {
        type: 'chain',
        steps: [
          { relation: 'member_of', direction: 'out' },
          { relation: 'manages', direction: 'out' }
        ]
      },
      {
        type: 'chain',
        steps: [
          { relation: 'located_in', direction: 'out' },
          { relation: 'operates_in', direction: 'in' },
          { relation: 'belongs_to', direction: 'in' },
          { relation: 'manages', direction: 'out' }
        ]
      }
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'parallel_chains', project);
  }, 1000, 'Parallel Chains (Union)');
  
  benchmark.report();
}

// Benchmark 5: ChainRule with Inference
async function benchmarkChainInference() {
  console.log('🧠 Benchmark 5: ChainRule with Inference Integration\n');
  
  const graph = setupEnterpriseChainGraph('small'); // Smaller graph for inference
  const benchmark = new ChainBenchmark('ChainRule Inference Performance');
  
  // Remove some relationships to create inference opportunities
  const relationships = [];
  for (let i = 0; i < 50; i++) {
    const user = graph.users[i];
    const group = graph.groups[Math.floor(Math.random() * Math.min(20, graph.groups.length))];
    relationships.push({ user, group });
    graph.arbiter.relationManager.removeRelation(user, 'member_of', group);
  }
  
  // Chain without inference (baseline)
  graph.arbiter.setRelationConfig('chain_no_inference', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' }
    ],
    allowInference: false
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'chain_no_inference', project, { noInfer: true });
  }, 500, 'Chain (no inference)');
  
  // Chain with basic inference
  graph.arbiter.setRelationConfig('chain_basic_inference', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' }
    ],
    allowInference: true
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'chain_basic_inference', project);
  }, 500, 'Chain (basic inference)');
  
  // Chain with inference and reliability threshold
  graph.arbiter.setRelationConfig('chain_reliable_inference', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' }
    ],
    allowInference: true,
    minReliability: 0.7
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'chain_reliable_inference', project);
  }, 500, 'Chain (high reliability inference)');
  
  benchmark.report();
}


// Benchmark 7: Real-World Enterprise Scenarios
async function benchmarkEnterpriseScenarios() {
  console.log('🏢 Benchmark 7: Real-World Enterprise Authorization Scenarios\n');
  
  const graph = setupEnterpriseChainGraph('medium');
  const benchmark = new ChainBenchmark('Enterprise Scenarios');
  
  // Scenario 1: Budget Authorization (user → group → project → budget >= threshold)
  graph.arbiter.setRelationConfig('budget_authorization', {
    type: 'relational_comparator',
    leftOperand: {
      rule: {
        type: 'chain',
        steps: [
          { relation: 'member_of', direction: 'out' },
          { relation: 'manages', direction: 'out' },
          { relation: 'has_budget', direction: 'out' }
        ],
        extractValues: true,
        extractFrom: 2,
        extractRelation: 'has_budget',
        valueAggregation: 'sum'
      },
      extractValue: true
    },
    rightOperand: {
      rule: { type: 'direct', relation: 'has_value' },
      extractValue: true
    },
    comparator: '>=',
    fallbackBehavior: 'deny'
  });
  
  // Add budget thresholds
  for (let i = 0; i < 50; i++) {
    const threshold = Math.floor(Math.random() * 1000000) + 50000;
    graph.arbiter.addRelation(`threshold:${i}`, 'has_value', `value:${threshold}`, { value: threshold });
  }
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const threshold = `threshold:${Math.floor(Math.random() * 50)}`;
    return graph.arbiter.check(user, 'budget_authorization', threshold);
  }, 500, 'Budget Authorization Chain');
  
  // Scenario 2: Facility Access (user → department → facility)
  graph.arbiter.setRelationConfig('facility_access', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'belongs_to', direction: 'out' },
      { relation: 'operates_in', direction: 'out' }
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const facility = graph.facilities[Math.floor(Math.random() * graph.facilities.length)];
    return graph.arbiter.check(user, 'facility_access', facility);
  }, 1000, 'Facility Access Chain');
  
  // Scenario 3: Resource Approval (user → group → project → resource)
  graph.arbiter.setRelationConfig('resource_approval', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' },
      { relation: 'uses_resource', direction: 'out' }
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const resource = graph.resources[Math.floor(Math.random() * graph.resources.length)];
    return graph.arbiter.check(user, 'resource_approval', resource.key);
  }, 1000, 'Resource Approval Chain');
  
  // Scenario 4: Cross-Division Access (complex multi-step authorization)
  graph.arbiter.setRelationConfig('cross_division_access', {
    union: [
      {
        type: 'chain',
        steps: [
          { relation: 'member_of', direction: 'out' },
          { relation: 'belongs_to', direction: 'out' },
          { relation: 'part_of', direction: 'out' }
        ]
      },
      {
        type: 'chain',
        steps: [
          { relation: 'located_in', direction: 'out' },
          { relation: 'operates_in', direction: 'in' },
          { relation: 'part_of', direction: 'out' }
        ]
      }
    ]
  });
  
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const division = graph.divisions[Math.floor(Math.random() * graph.divisions.length)];
    return graph.arbiter.check(user, 'cross_division_access', `division:${division}`);
  }, 1000, 'Cross-Division Access (Union)');
  
  benchmark.report();
}

// Main benchmark runner
async function runChainRuleBenchmarks() {
  console.log('🚀 Starting ChainRule Performance Benchmarks...\n');
  
  try {
    await benchmarkChainLength();
    await benchmarkVsTraditional();
    await benchmarkValueExtraction();
    await benchmarkDirectionPatterns();
    await benchmarkChainInference();
    await benchmarkEnterpriseScenarios();
    
    console.log('\n✅ All ChainRule benchmarks completed successfully!');
    
    console.log('\n🚀 X-LARGE SCALE BENCHMARKS (xlarge)');
    await benchmarkChainLengthScale('xlarge', 100);
    await benchmarkVsTraditionalScale('xlarge', 100);
    console.log('\n🚀 XX-LARGE SCALE BENCHMARKS (xxlarge)');
    await benchmarkChainLengthScale('xxlarge', 50);
    await benchmarkVsTraditionalScale('xxlarge', 50);
    
  } catch (error) {
    console.error('❌ Benchmark failed:', error);
    process.exit(1);
  }
}

// Run if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
  runChainRuleBenchmarks();
}

export { runChainRuleBenchmarks };

// Add helper functions for large scale runs:
async function benchmarkChainLengthScale(scale, iterations) {
  console.log(`\n📏 [${scale.toUpperCase()}] Chain Length Performance Impact`);
  const graph = setupEnterpriseChainGraph(scale);
  const benchmark = new ChainBenchmark(`Chain Length Performance (${scale})`);
  // Baseline: Direct access (no chain)
  graph.arbiter.setRelationConfig('direct_access', {
    type: 'direct',
    relation: 'can_access'
  });
  const directAccessPairs = [];
  graph.users.forEach((user, i) => {
    const numProjects = Math.floor(Math.random() * 3) + 1;
    for (let j = 0; j < numProjects; j++) {
      const project = graph.projects[(i * 7 + j) % graph.projects.length];
      graph.arbiter.addRelation(user, 'can_access', project);
      directAccessPairs.push({ user, project });
    }
  });
  await benchmark.run(() => {
    const pair = directAccessPairs[Math.floor(Math.random() * directAccessPairs.length)];
    return graph.arbiter.check(pair.user, 'direct_access', pair.project, { noInfer: true });
  }, iterations, 'Direct Access (baseline)');
  // 2-step chain
  graph.arbiter.setRelationConfig('chain_2_step', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' }
    ]
  });
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'chain_2_step', project, { noInfer: true });
  }, iterations, '2-Step Chain (user→group→project)');
  benchmark.report();
}
async function benchmarkVsTraditionalScale(scale, iterations) {
  console.log(`\n⚔️  [${scale.toUpperCase()}] ChainRule vs Traditional Authorization`);
  const graph = setupEnterpriseChainGraph(scale);
  const benchmark = new ChainBenchmark(`ChainRule vs Traditional (${scale})`);
  // Traditional ParentRule approach
  graph.arbiter.setRelationConfig('traditional_parent', {
    type: 'parent',
    parentRelation: 'manages',
    relation: 'member_of'
  });
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'traditional_parent', project, { noInfer: true });
  }, iterations, 'ParentRule (baseline)');
  // Equivalent ChainRule approach
  graph.arbiter.setRelationConfig('chain_equivalent', {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'manages', direction: 'out' }
    ]
  });
  await benchmark.run(() => {
    const user = graph.users[Math.floor(Math.random() * graph.users.length)];
    const project = graph.projects[Math.floor(Math.random() * graph.projects.length)];
    return graph.arbiter.check(user, 'chain_equivalent', project, { noInfer: true });
  }, iterations, 'ChainRule (equivalent logic)');
  benchmark.report();
} 