/**
 * Test realistic chain performance with actual graph relations
 */

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('measures realistic chain QPS with actual relations', async () => {
  if (process.env.TEST_DEBUG === '1') console.log('🎯 Testing realistic chain QPS with actual relations...');
  
  const generator = new BigGraphGenerator();
  const graphData = generator.generateGraph('enterprise');
  const arbiter = generator.loadIntoArbiter(graphData);
  
  const rule = new ChainRule(arbiter);
  
  // Create a realistic chain rule using actual relations
  const chainRule = {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'can_read', direction: 'out' }
    ],
    collectValues: false,
    valueAggregation: 'sum'
  };
  
  // Find realistic chain paths using actual relations
  if (process.env.TEST_DEBUG === '1') console.log('   Finding realistic chain paths...');
  
  // Get users with member_of relations
  const usersWithMemberships = graphData.relations
    .filter(r => r.relation === 'member_of' && r.src.startsWith('user:'))
    .map(r => r.src);
  
  // Get objects with can_read relations  
  const objectsWithReadPermissions = graphData.relations
    .filter(r => r.relation === 'can_read' && r.dst.startsWith('doc:'))
    .map(r => r.dst);
  
  if (process.env.TEST_DEBUG === '1') console.log(`   Found ${usersWithMemberships.length} users with memberships`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Found ${objectsWithReadPermissions.length} objects with read permissions`);
  
  // Create realistic test pairs
  const realisticPairs = [];
  for (let i = 0; i < Math.min(10, usersWithMemberships.length, objectsWithReadPermissions.length); i++) {
    realisticPairs.push({
      user: usersWithMemberships[i],
      object: objectsWithReadPermissions[i]
    });
  }
  
  if (process.env.TEST_DEBUG === '1') console.log(`   Testing with ${realisticPairs.length} realistic pairs...`);
  
  // Test 1: Without caching (clear cache between queries)
  if (process.env.TEST_DEBUG === '1') console.log('   Test 1: Without caching...');
  const start1 = Date.now();
  let queryCount1 = 0;
  let positiveResults1 = 0;
  const end1 = start1 + 3000; // 3 seconds
  
  while (Date.now() < end1) {
    for (const pair of realisticPairs) {
      // Clear cache to simulate no caching
      rule.chainResultCache.clear();
      rule.chainPathCache.clear();
      
      const result = rule.evaluate(
        arbiter.nodeIdByKey.get(pair.user),
        pair.user,
        arbiter.nodeIdByKey.get(pair.object),
        pair.object,
        chainRule,
        new Set(),
        'can_read_via_membership',
        {}
      );
      
      if (result.possibility > 0) {
        positiveResults1++;
      }
      queryCount1++;
    }
  }
  
  const duration1 = Date.now() - start1;
  const qps1 = (queryCount1 / duration1) * 1000;
  const positiveRate1 = (positiveResults1 / queryCount1) * 100;
  
  if (process.env.TEST_DEBUG === '1') console.log(`   Without caching: ${qps1.toFixed(2)} QPS (${queryCount1} queries)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Positive results: ${positiveResults1} (${positiveRate1.toFixed(1)}%)`);
  
  // Test 2: With caching (let cache accumulate)
  if (process.env.TEST_DEBUG === '1') console.log('   Test 2: With caching...');
  rule.chainResultCache.clear();
  rule.chainPathCache.clear();
  
  const start2 = Date.now();
  let queryCount2 = 0;
  let positiveResults2 = 0;
  const end2 = start2 + 3000; // 3 seconds
  
  while (Date.now() < end2) {
    for (const pair of realisticPairs) {
      const result = rule.evaluate(
        arbiter.nodeIdByKey.get(pair.user),
        pair.user,
        arbiter.nodeIdByKey.get(pair.object),
        pair.object,
        chainRule,
        new Set(),
        'can_read_via_membership',
        {}
      );
      
      if (result.possibility > 0) {
        positiveResults2++;
      }
      queryCount2++;
    }
  }
  
  const duration2 = Date.now() - start2;
  const qps2 = (queryCount2 / duration2) * 1000;
  const positiveRate2 = (positiveResults2 / queryCount2) * 100;
  
  if (process.env.TEST_DEBUG === '1') console.log(`   With caching: ${qps2.toFixed(2)} QPS (${queryCount2} queries)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Positive results: ${positiveResults2} (${positiveRate2.toFixed(1)}%)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Cache size: ${rule.chainResultCache.size} results, ${rule.chainPathCache.size} paths`);
  
  // Calculate improvement
  const improvement = qps2 / qps1;
  if (process.env.TEST_DEBUG === '1') console.log(`   QPS improvement: ${improvement.toFixed(2)}x faster`);
  
  // Verify improvement
  assert.ok(improvement > 1, `Caching should improve QPS (${improvement.toFixed(2)}x)`);
  assert.ok(positiveRate1 > 0, 'Should have some positive results');
  assert.ok(positiveRate2 > 0, 'Should have some positive results');
  
  if (process.env.TEST_DEBUG === '1') console.log('   ✅ Realistic chain caching improves QPS');
});

test('measures chain QPS with role inheritance', async () => {
  if (process.env.TEST_DEBUG === '1') console.log('🔗 Testing chain QPS with role inheritance...');
  
  const generator = new BigGraphGenerator();
  const graphData = generator.generateGraph('enterprise');
  const arbiter = generator.loadIntoArbiter(graphData);
  
  const rule = new ChainRule(arbiter);
  
  // Create a chain rule for role inheritance
  const chainRule = {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'inherits_from', direction: 'out' }
    ],
    collectValues: false,
    valueAggregation: 'sum'
  };
  
  // Find actual role inheritance chains
  if (process.env.TEST_DEBUG === '1') console.log('   Finding role inheritance chains...');
  
  const roleChains = [];
  
  // Look for users with role memberships
  const roleMemberships = graphData.relations.filter(r => r.relation === 'member_of' && r.dst.startsWith('role:'));
  
  for (const membership of roleMemberships.slice(0, 5)) {
    const userId = membership.src;
    const roleId = membership.dst;
    
    // Find inheritance chains for this role
    const inheritanceChains = graphData.relations.filter(r => 
      r.relation === 'inherits_from' && r.src === roleId
    );
    
    for (const inheritance of inheritanceChains) {
      roleChains.push({
        user: userId,
        role: roleId,
        inheritedRole: inheritance.dst
      });
    }
  }
  
  if (process.env.TEST_DEBUG === '1') console.log(`   Found ${roleChains.length} role inheritance chains`);
  
  if (roleChains.length === 0) {
    if (process.env.TEST_DEBUG === '1') console.log('   No role inheritance chains found, skipping test');
    return;
  }
  
  // Test with role inheritance chains
  if (process.env.TEST_DEBUG === '1') console.log('   Testing with role inheritance chains...');
  
  // Test 1: Without caching
  if (process.env.TEST_DEBUG === '1') console.log('   Test 1: Without caching...');
  const start1 = Date.now();
  let queryCount1 = 0;
  let successfulChains1 = 0;
  const end1 = start1 + 2000; // 2 seconds
  
  while (Date.now() < end1) {
    for (const chain of roleChains) {
      // Clear cache to simulate no caching
      rule.chainResultCache.clear();
      rule.chainPathCache.clear();
      
      const result = rule.evaluate(
        arbiter.nodeIdByKey.get(chain.user),
        chain.user,
        arbiter.nodeIdByKey.get(chain.inheritedRole),
        chain.inheritedRole,
        chainRule,
        new Set(),
        'inherits_via_membership',
        {}
      );
      
      if (result.possibility > 0) {
        successfulChains1++;
      }
      queryCount2++;
    }
  }
  
  const duration1 = Date.now() - start1;
  const qps1 = (queryCount1 / duration1) * 1000;
  const successRate1 = (successfulChains1 / queryCount1) * 100;
  
  if (process.env.TEST_DEBUG === '1') console.log(`   Without caching: ${qps1.toFixed(2)} QPS (${queryCount1} queries)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Successful chains: ${successfulChains1} (${successRate1.toFixed(1)}%)`);
  
  // Test 2: With caching
  if (process.env.TEST_DEBUG === '1') console.log('   Test 2: With caching...');
  rule.chainResultCache.clear();
  rule.chainPathCache.clear();
  
  const start2 = Date.now();
  let queryCount2 = 0;
  let successfulChains2 = 0;
  const end2 = start2 + 2000; // 2 seconds
  
  while (Date.now() < end2) {
    for (const chain of roleChains) {
      const result = rule.evaluate(
        arbiter.nodeIdByKey.get(chain.user),
        chain.user,
        arbiter.nodeIdByKey.get(chain.inheritedRole),
        chain.inheritedRole,
        chainRule,
        new Set(),
        'inherits_via_membership',
        {}
      );
      
      if (result.possibility > 0) {
        successfulChains2++;
      }
      queryCount2++;
    }
  }
  
  const duration2 = Date.now() - start2;
  const qps2 = (queryCount2 / duration2) * 1000;
  const successRate2 = (successfulChains2 / queryCount2) * 100;
  
  if (process.env.TEST_DEBUG === '1') console.log(`   With caching: ${qps2.toFixed(2)} QPS (${queryCount2} queries)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Successful chains: ${successfulChains2} (${successRate2.toFixed(1)}%)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Cache size: ${rule.chainResultCache.size} results, ${rule.chainPathCache.size} paths`);
  
  // Calculate improvement
  const improvement = qps2 / qps1;
  if (process.env.TEST_DEBUG === '1') console.log(`   QPS improvement: ${improvement.toFixed(2)}x faster`);
  
  // Verify improvement
  assert.ok(improvement > 1, `Caching should improve QPS (${improvement.toFixed(2)}x)`);
  assert.ok(successRate1 > 0, 'Should have some successful chains');
  assert.ok(successRate2 > 0, 'Should have some successful chains');
  
  if (process.env.TEST_DEBUG === '1') console.log('   ✅ Role inheritance chain caching improves QPS');
});

test('measures chain QPS with realistic workload', async () => {
  if (process.env.TEST_DEBUG === '1') console.log('📊 Testing chain QPS with realistic workload...');
  
  const generator = new BigGraphGenerator();
  const graphData = generator.generateGraph('enterprise');
  const arbiter = generator.loadIntoArbiter(graphData);
  
  const rule = new ChainRule(arbiter);
  
  // Create a realistic chain rule
  const chainRule = {
    type: 'chain',
    steps: [
      { relation: 'member_of', direction: 'out' },
      { relation: 'can_read', direction: 'out' }
    ],
    collectValues: false,
    valueAggregation: 'sum'
  };
  
  // Create a realistic workload: 70% positive, 30% negative
  if (process.env.TEST_DEBUG === '1') console.log('   Creating realistic workload (70% positive, 30% negative)...');
  
  const workload = [];
  
  // 70% positive queries (should succeed)
  const positiveQueries = [];
  const memberOfRelations = graphData.relations.filter(r => r.relation === 'member_of');
  const canReadRelations = graphData.relations.filter(r => r.relation === 'can_read');
  
  for (let i = 0; i < Math.min(7, memberOfRelations.length, canReadRelations.length); i++) {
    positiveQueries.push({
      user: memberOfRelations[i].src,
      object: canReadRelations[i].dst,
      expected: 'positive'
    });
  }
  
  // 30% negative queries (should fail)
  const negativeQueries = [];
  const users = graphData.users.slice(0, 3);
  const objects = graphData.documents.slice(0, 3);
  
  for (let i = 0; i < 3; i++) {
    negativeQueries.push({
      user: users[i].key,
      object: objects[i].key,
      expected: 'negative'
    });
  }
  
  workload.push(...positiveQueries, ...negativeQueries);
  
  if (process.env.TEST_DEBUG === '1') console.log(`   Workload: ${positiveQueries.length} positive, ${negativeQueries.length} negative queries`);
  
  // Test 1: Without caching
  if (process.env.TEST_DEBUG === '1') console.log('   Test 1: Without caching...');
  const start1 = Date.now();
  let queryCount1 = 0;
  let positiveCount1 = 0;
  const end1 = start1 + 3000; // 3 seconds
  
  while (Date.now() < end1) {
    for (const query of workload) {
      // Clear cache to simulate no caching
      rule.chainResultCache.clear();
      rule.chainPathCache.clear();
      
      const result = rule.evaluate(
        arbiter.nodeIdByKey.get(query.user),
        query.user,
        arbiter.nodeIdByKey.get(query.object),
        query.object,
        chainRule,
        new Set(),
        'can_read_via_membership',
        {}
      );
      
      if (result.possibility > 0) {
        positiveCount1++;
      }
      queryCount1++;
    }
  }
  
  const duration1 = Date.now() - start1;
  const qps1 = (queryCount1 / duration1) * 1000;
  const positiveRate1 = (positiveCount1 / queryCount1) * 100;
  
  if (process.env.TEST_DEBUG === '1') console.log(`   Without caching: ${qps1.toFixed(2)} QPS (${queryCount1} queries)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Positive rate: ${positiveRate1.toFixed(1)}%`);
  
  // Test 2: With caching
  if (process.env.TEST_DEBUG === '1') console.log('   Test 2: With caching...');
  rule.chainResultCache.clear();
  rule.chainPathCache.clear();
  
  const start2 = Date.now();
  let queryCount2 = 0;
  let positiveCount2 = 0;
  const end2 = start2 + 3000; // 3 seconds
  
  while (Date.now() < end2) {
    for (const query of workload) {
      const result = rule.evaluate(
        arbiter.nodeIdByKey.get(query.user),
        query.user,
        arbiter.nodeIdByKey.get(query.object),
        query.object,
        chainRule,
        new Set(),
        'can_read_via_membership',
        {}
      );
      
      if (result.possibility > 0) {
        positiveCount2++;
      }
      queryCount2++;
    }
  }
  
  const duration2 = Date.now() - start2;
  const qps2 = (queryCount2 / duration2) * 1000;
  const positiveRate2 = (positiveCount2 / queryCount2) * 100;
  
  if (process.env.TEST_DEBUG === '1') console.log(`   With caching: ${qps2.toFixed(2)} QPS (${queryCount2} queries)`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Positive rate: ${positiveRate2.toFixed(1)}%`);
  if (process.env.TEST_DEBUG === '1') console.log(`   Cache size: ${rule.chainResultCache.size} results, ${rule.chainPathCache.size} paths`);
  
  // Calculate improvement
  const improvement = qps2 / qps1;
  if (process.env.TEST_DEBUG === '1') console.log(`   QPS improvement: ${improvement.toFixed(2)}x faster`);
  
  // Verify improvement
  assert.ok(improvement > 1, `Caching should improve QPS (${improvement.toFixed(2)}x)`);
  assert.ok(positiveRate1 > 0, 'Should have some positive results');
  assert.ok(positiveRate2 > 0, 'Should have some positive results');
  
  if (process.env.TEST_DEBUG === '1') console.log('   ✅ Realistic workload caching improves QPS');
});
