/**
 * Test performance of different reachability indexing strategies
 */

import { test as _test } from 'node:test';
const test = process.env.RUN_PERF_TESTS === '1' ? _test : _test.skip;
import assert from 'node:assert/strict';
import { BigGraphGenerator } from '../helpers/big-graph-generator.js';
import { ChainRule } from '../../src/authorization/rules/ChainRule.js';

test('compares reachability strategy performance', async () => {
  if (process.env.TEST_DEBUG === '1') console.log('🔍 Comparing reachability strategy performance...');
  
  const generator = new BigGraphGenerator();
  const graphData = generator.generateGraph('enterprise');
  
  // Test different strategies
  const strategies = ['auto', 'twohop', 'treecover', 'hybrid'];
  const results = {};
  
  for (const strategy of strategies) {
    if (process.env.TEST_DEBUG === '1') console.log(`   Testing strategy: ${strategy}`);
    
    // Create new arbiter for each strategy
    const arbiter = generator.loadIntoArbiter(graphData);
    
    // Initialize reachability checker with specific strategy
    await arbiter.reachabilityChecker.initialize({
      strategy: strategy,
      twoHopOptions: { maxNodes: 1000 },
      treeCoverOptions: { maxTrees: 3 }
    });
    
    const rule = new ChainRule(arbiter);
    
    // Create a chain rule
    const chainRule = {
      type: 'chain',
      steps: [
        { relation: 'member_of', direction: 'out' },
        { relation: 'can_read', direction: 'out' }
      ],
      collectValues: false,
      valueAggregation: 'sum'
    };
    
    // Find actual chain paths
    const actualChains = [];
    const roleMemberships = graphData.relations.filter(r => 
      r.relation === 'member_of' && r.dst.startsWith('role:')
    );
    
    for (const membership of roleMemberships) {
      const userId = membership.src;
      const roleId = membership.dst;
      
      const roleReadPermissions = graphData.relations.filter(r => 
        r.relation === 'can_read' && r.src === roleId
      );
      
      for (const permission of roleReadPermissions) {
        actualChains.push({
          user: userId,
          object: permission.dst
        });
      }
    }
    
    if (process.env.TEST_DEBUG === '1') console.log(`     Found ${actualChains.length} chain paths`);
    
    // Test performance
    const start = Date.now();
    let queryCount = 0;
    let positiveResults = 0;
    const end = start + 2000; // 2 seconds
    
    while (Date.now() < end) {
      for (const chain of actualChains) {
        const result = rule.evaluate(
          arbiter.nodeIdByKey.get(chain.user),
          chain.user,
          arbiter.nodeIdByKey.get(chain.object),
          chain.object,
          chainRule,
          new Set(),
          'can_read_via_role',
          {}
        );
        
        if (result.possibility > 0) {
          positiveResults++;
        }
        queryCount++;
      }
    }
    
    const duration = Date.now() - start;
    const qps = (queryCount / duration) * 1000;
    const positiveRate = (positiveResults / queryCount) * 100;
    
    results[strategy] = {
      qps: qps,
      queryCount: queryCount,
      positiveRate: positiveRate,
      duration: duration,
      cacheSize: rule.chainResultCache.size
    };
    
    if (process.env.TEST_DEBUG === '1') console.log(`     ${strategy}: ${qps.toFixed(2)} QPS (${queryCount} queries, ${positiveRate.toFixed(1)}% positive)`);
    if (process.env.TEST_DEBUG === '1') console.log(`     Cache size: ${rule.chainResultCache.size} results`);
    
    // Get reachability stats
    const reachabilityStats = arbiter.reachabilityChecker.getStats();
    if (process.env.TEST_DEBUG === '1') console.log(`     Reachability stats:`, reachabilityStats);
  }
  
  // Compare results
  if (process.env.TEST_DEBUG === '1') console.log('   📊 Strategy comparison:');
  const sortedResults = Object.entries(results).sort((a, b) => b[1].qps - a[1].qps);
  
  sortedResults.forEach(([strategy, result], index) => {
    const rank = index + 1;
    const improvement = index === 0 ? '🏆' : `-${((sortedResults[0][1].qps - result.qps) / sortedResults[0][1].qps * 100).toFixed(1)}%`;
    if (process.env.TEST_DEBUG === '1') console.log(`     ${rank}. ${strategy}: ${result.qps.toFixed(2)} QPS ${improvement}`);
  });
  
  // Verify that we have results
  assert.ok(Object.keys(results).length > 0, 'Should have results for all strategies');
  
  if (process.env.TEST_DEBUG === '1') console.log('   ✅ Reachability strategy comparison completed');
});

test('measures strategy performance with different graph sizes', async () => {
  if (process.env.TEST_DEBUG === '1') console.log('📈 Testing strategy performance with different graph sizes...');
  
  const graphSizes = ['small', 'medium', 'enterprise'];
  const strategies = ['auto', 'twohop', 'treecover', 'hybrid'];
  const results = {};
  
  for (const size of graphSizes) {
    if (process.env.TEST_DEBUG === '1') console.log(`   Testing ${size} graph...`);
    results[size] = {};
    
    for (const strategy of strategies) {
      if (process.env.TEST_DEBUG === '1') console.log(`     Strategy: ${strategy}`);
      
      const generator = new BigGraphGenerator();
      const graphData = generator.generateGraph(size);
      const arbiter = generator.loadIntoArbiter(graphData);
      
      // Initialize reachability checker
      await arbiter.reachabilityChecker.initialize({
        strategy: strategy,
        twoHopOptions: { maxNodes: 1000 },
        treeCoverOptions: { maxTrees: 3 }
      });
      
      const rule = new ChainRule(arbiter);
      
      // Create a chain rule
      const chainRule = {
        type: 'chain',
        steps: [
          { relation: 'member_of', direction: 'out' },
          { relation: 'can_read', direction: 'out' }
        ],
        collectValues: false,
        valueAggregation: 'sum'
      };
      
      // Find actual chain paths
      const actualChains = [];
      const roleMemberships = graphData.relations.filter(r => 
        r.relation === 'member_of' && r.dst.startsWith('role:')
      );
      
      for (const membership of roleMemberships) {
        const userId = membership.src;
        const roleId = membership.dst;
        
        const roleReadPermissions = graphData.relations.filter(r => 
          r.relation === 'can_read' && r.src === roleId
        );
        
        for (const permission of roleReadPermissions) {
          actualChains.push({
            user: userId,
            object: permission.dst
          });
        }
      }
      
      // Test performance
      const start = Date.now();
      let queryCount = 0;
      let positiveResults = 0;
      const end = start + 1000; // 1 second
      
      while (Date.now() < end) {
        for (const chain of actualChains) {
          const result = rule.evaluate(
            arbiter.nodeIdByKey.get(chain.user),
            chain.user,
            arbiter.nodeIdByKey.get(chain.object),
            chain.object,
            chainRule,
            new Set(),
            'can_read_via_role',
            {}
          );
          
          if (result.possibility > 0) {
            positiveResults++;
          }
          queryCount++;
        }
      }
      
      const duration = Date.now() - start;
      const qps = (queryCount / duration) * 1000;
      const positiveRate = (positiveResults / queryCount) * 100;
      
      results[size][strategy] = {
        qps: qps,
        queryCount: queryCount,
        positiveRate: positiveRate,
        chainCount: actualChains.length
      };
      
      if (process.env.TEST_DEBUG === '1') console.log(`       ${strategy}: ${qps.toFixed(2)} QPS (${actualChains.length} chains)`);
    }
  }
  
  // Compare results across graph sizes
  if (process.env.TEST_DEBUG === '1') console.log('   📊 Results by graph size:');
  for (const size of graphSizes) {
    if (process.env.TEST_DEBUG === '1') console.log(`   ${size}:`);
    const sortedResults = Object.entries(results[size]).sort((a, b) => b[1].qps - a[1].qps);
    sortedResults.forEach(([strategy, result], index) => {
      const rank = index + 1;
      if (process.env.TEST_DEBUG === '1') console.log(`     ${rank}. ${strategy}: ${result.qps.toFixed(2)} QPS (${result.chainCount} chains)`);
    });
  }
  
  if (process.env.TEST_DEBUG === '1') console.log('   ✅ Graph size strategy comparison completed');
});

test('measures strategy performance with different chain lengths', async () => {
  if (process.env.TEST_DEBUG === '1') console.log('🔗 Testing strategy performance with different chain lengths...');
  
  const generator = new BigGraphGenerator();
  const graphData = generator.generateGraph('enterprise');
  
  const strategies = ['auto', 'twohop', 'treecover', 'hybrid'];
  const chainLengths = [2, 3, 4];
  const results = {};
  
  for (const length of chainLengths) {
    if (process.env.TEST_DEBUG === '1') console.log(`   Testing chain length: ${length}`);
    results[length] = {};
    
    for (const strategy of strategies) {
      if (process.env.TEST_DEBUG === '1') console.log(`     Strategy: ${strategy}`);
      
      const arbiter = generator.loadIntoArbiter(graphData);
      
      // Initialize reachability checker
      await arbiter.reachabilityChecker.initialize({
        strategy: strategy,
        twoHopOptions: { maxNodes: 1000 },
        treeCoverOptions: { maxTrees: 3 }
      });
      
      const rule = new ChainRule(arbiter);
      
      // Create a chain rule with specified length
      const chainRule = {
        type: 'chain',
        steps: Array.from({ length }, (_, i) => ({
          relation: i === 0 ? 'member_of' : 'can_read',
          direction: 'out'
        })),
        collectValues: false,
        valueAggregation: 'sum'
      };
      
      // Find actual chain paths
      const actualChains = [];
      const roleMemberships = graphData.relations.filter(r => 
        r.relation === 'member_of' && r.dst.startsWith('role:')
      );
      
      for (const membership of roleMemberships) {
        const userId = membership.src;
        const roleId = membership.dst;
        
        const roleReadPermissions = graphData.relations.filter(r => 
          r.relation === 'can_read' && r.src === roleId
        );
        
        for (const permission of roleReadPermissions) {
          actualChains.push({
            user: userId,
            object: permission.dst
          });
        }
      }
      
      // Test performance
      const start = Date.now();
      let queryCount = 0;
      let positiveResults = 0;
      const end = start + 1000; // 1 second
      
      while (Date.now() < end) {
        for (const chain of actualChains) {
          const result = rule.evaluate(
            arbiter.nodeIdByKey.get(chain.user),
            chain.user,
            arbiter.nodeIdByKey.get(chain.object),
            chain.object,
            chainRule,
            new Set(),
            'can_read_via_role',
            {}
          );
          
          if (result.possibility > 0) {
            positiveResults++;
          }
          queryCount++;
        }
      }
      
      const duration = Date.now() - start;
      const qps = (queryCount / duration) * 1000;
      const positiveRate = (positiveResults / queryCount) * 100;
      
      results[length][strategy] = {
        qps: qps,
        queryCount: queryCount,
        positiveRate: positiveRate,
        chainCount: actualChains.length
      };
      
      if (process.env.TEST_DEBUG === '1') console.log(`       ${strategy}: ${qps.toFixed(2)} QPS (${actualChains.length} chains)`);
    }
  }
  
  // Compare results across chain lengths
  if (process.env.TEST_DEBUG === '1') console.log('   📊 Results by chain length:');
  for (const length of chainLengths) {
    if (process.env.TEST_DEBUG === '1') console.log(`   Length ${length}:`);
    const sortedResults = Object.entries(results[length]).sort((a, b) => b[1].qps - a[1].qps);
    sortedResults.forEach(([strategy, result], index) => {
      const rank = index + 1;
      if (process.env.TEST_DEBUG === '1') console.log(`     ${rank}. ${strategy}: ${result.qps.toFixed(2)} QPS (${result.chainCount} chains)`);
    });
  }
  
  if (process.env.TEST_DEBUG === '1') console.log('   ✅ Chain length strategy comparison completed');
});
