import { test, describe, before, after } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../src/index.js';
import { BigGraphGenerator } from '../helpers/big-graph-generator.js';

describe.skip('Valid Path Chain Query Benchmark', () => {
  let arbiter;
  let generator;
  let testArbiter;
  let graphData;
  let validPaths = [];

  before(() => {
    arbiter = new Arbiter({ fastConstructionMode: false });
    generator = new BigGraphGenerator({ scale: 'large', seed: 12345 });
    
    // Generate a larger, more realistic graph
    graphData = generator.generateGraph('enterprise');
    testArbiter = generator.loadIntoArbiter(graphData);
    
    // Configure chain rules
    testArbiter.setRelationConfig('can_read_via_role', {
      type: 'chain',
      steps: [
        { relation: 'member_of', direction: 'out' },
        { relation: 'can_read', direction: 'out' }
      ]
    });
    
    testArbiter.setRelationConfig('can_access_multi_hop', {
      type: 'chain',
      steps: [
        { relation: 'member_of', direction: 'out' },
        { relation: 'member_of', direction: 'out' },
        { relation: 'member_of', direction: 'out' },
        { relation: 'can_read', direction: 'out' }
      ]
    });

    // Pre-find valid paths that actually exist in the graph
    if (process.env.TEST_DEBUG === '1') console.log('🔍 Finding valid chain paths...');
    validPaths = findValidChainPaths(testArbiter, graphData);
    if (process.env.TEST_DEBUG === '1') console.log(`   Found ${validPaths.length} valid paths`);
  });

  after(() => {
    arbiter = null;
    generator = null;
    testArbiter = null;
    graphData = null;
    validPaths = null;
  });

  /**
   * Find valid chain paths that actually exist in the graph
   */
  function findValidChainPaths(arbiter, graphData) {
    const validPaths = [];
    
    // Get all users and documents
    const users = graphData.users.map(u => u.id);
    const docs = graphData.documents.map(d => d.id);
    
    // Sample a reasonable number of combinations to test
    const maxTests = Math.min(1000, users.length * docs.length);
    let tested = 0;
    
    for (const user of users) {
      if (tested >= maxTests) break;
      
      for (const doc of docs) {
        if (tested >= maxTests) break;
        
        // Test if this path actually exists
        try {
          const result = arbiter.check(user, 'can_read_via_role', doc);
          if (result.possibility > 0) {
            validPaths.push({ user, doc, relation: 'can_read_via_role', result });
          }
        } catch (error) {
          // Skip invalid paths
        }
        
        tested++;
      }
    }
    
    return validPaths;
  }

  /**
   * Measure QPS for a given operation with detailed analysis
   */
  function measureQPSWithAnalysis(operation, duration = 2000, operationName = 'Operation') {
    const startTime = Date.now();
    const endTime = startTime + duration;
    let operationCount = 0;
    const latencies = [];
    let successCount = 0;
    let failureCount = 0;

    // Clear caches before starting
    if (testArbiter.relationManager && testArbiter.relationManager.chainRule) {
      testArbiter.relationManager.chainRule.chainResultCache.clear();
      testArbiter.relationManager.chainRule.chainPathCache.clear();
    }

    while (Date.now() < endTime) {
      const opStart = process.hrtime.bigint();
      
      try {
        const result = operation();
        operationCount++;
        
        if (result && result.possibility > 0) {
          successCount++;
        } else {
          failureCount++;
        }
        
        const opEnd = process.hrtime.bigint();
        const latency = Number(opEnd - opStart) / 1000000; // Convert to milliseconds
        latencies.push(latency);
      } catch (error) {
        operationCount++;
        failureCount++;
        const opEnd = process.hrtime.bigint();
        const latency = Number(opEnd - opStart) / 1000000;
        latencies.push(latency);
      }
    }

    const actualDuration = Date.now() - startTime;
    const qps = (operationCount / actualDuration) * 1000;
    const avgLatency = latencies.reduce((a, b) => a + b, 0) / latencies.length;
    
    // Sort once and reuse
    const sortedLatencies = latencies.sort((a, b) => a - b);
    const p95Latency = sortedLatencies[Math.floor(latencies.length * 0.95)];
    const p99Latency = sortedLatencies[Math.floor(latencies.length * 0.99)];

    return {
      qps,
      operationCount,
      duration: actualDuration,
      avgLatency,
      p95Latency,
      p99Latency,
      maxLatency: sortedLatencies[sortedLatencies.length - 1],
      minLatency: sortedLatencies[0],
      successCount,
      failureCount,
      successRate: successCount / operationCount
    };
  }

  test('Valid Path Chain Query - Cold Start', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Valid Path Chain Query QPS (Cold Start)...');
    
    if (validPaths.length === 0) {
      if (process.env.TEST_DEBUG === '1') console.log('   ⚠️  No valid paths found - skipping test');
      return;
    }
    
    let queryIndex = 0;
    const result = measureQPSWithAnalysis(() => {
      const path = validPaths[queryIndex % validPaths.length];
      return testArbiter.check(path.user, path.relation, path.doc);
    }, 3000, 'Valid Path Chain Query (Cold)');

    if (process.env.TEST_DEBUG === '1') console.log(`   QPS: ${result.qps.toFixed(2)}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Operations: ${result.operationCount}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Duration: ${result.duration}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Avg Latency: ${result.avgLatency.toFixed(3)}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   P95 Latency: ${result.p95Latency.toFixed(3)}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   P99 Latency: ${result.p99Latency.toFixed(3)}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Success Rate: ${(result.successRate * 100).toFixed(1)}%`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Success Count: ${result.successCount}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Failure Count: ${result.failureCount}`);

    // More realistic expectations for valid path queries
    assert.ok(result.qps > 100, `Valid path chain query QPS ${result.qps.toFixed(0)} below 100 threshold`);
    assert.ok(result.avgLatency < 50, `Valid path chain query latency ${result.avgLatency.toFixed(3)}ms too high`);
    assert.ok(result.successRate > 0.8, `Success rate ${(result.successRate * 100).toFixed(1)}% too low for valid paths`);
  });

  test('Valid Path Multi-hop Chain Query - Cold Start', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Valid Path Multi-hop Chain Query QPS (Cold Start)...');
    
    // Find valid multi-hop paths
    const multiHopPaths = [];
    const users = graphData.users.map(u => u.id);
    const docs = graphData.documents.map(d => d.id);
    
    let tested = 0;
    const maxTests = Math.min(500, users.length * docs.length);
    
    for (const user of users) {
      if (tested >= maxTests) break;
      
      for (const doc of docs) {
        if (tested >= maxTests) break;
        
        try {
          const result = testArbiter.check(user, 'can_access_multi_hop', doc);
          if (result.possibility > 0) {
            multiHopPaths.push({ user, doc, relation: 'can_access_multi_hop', result });
          }
        } catch (error) {
          // Skip invalid paths
        }
        
        tested++;
      }
    }
    
    if (process.env.TEST_DEBUG === '1') console.log(`   Found ${multiHopPaths.length} valid multi-hop paths`);
    
    if (multiHopPaths.length === 0) {
      if (process.env.TEST_DEBUG === '1') console.log('   ⚠️  No valid multi-hop paths found - skipping test');
      return;
    }
    
    let queryIndex = 0;
    const result = measureQPSWithAnalysis(() => {
      const path = multiHopPaths[queryIndex % multiHopPaths.length];
      return testArbiter.check(path.user, path.relation, path.doc);
    }, 3000, 'Valid Path Multi-hop Chain Query (Cold)');

    if (process.env.TEST_DEBUG === '1') console.log(`   QPS: ${result.qps.toFixed(2)}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Operations: ${result.operationCount}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Duration: ${result.duration}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Avg Latency: ${result.avgLatency.toFixed(3)}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   P95 Latency: ${result.p95Latency.toFixed(3)}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   P99 Latency: ${result.p99Latency.toFixed(3)}ms`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Success Rate: ${(result.successRate * 100).toFixed(1)}%`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Success Count: ${result.successCount}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Failure Count: ${result.failureCount}`);

    // Multi-hop should be slower than 2-hop
    assert.ok(result.qps > 50, `Valid path multi-hop chain query QPS ${result.qps.toFixed(0)} below 50 threshold`);
    assert.ok(result.avgLatency < 100, `Valid path multi-hop chain query latency ${result.avgLatency.toFixed(3)}ms too high`);
    assert.ok(result.successRate > 0.7, `Success rate ${(result.successRate * 100).toFixed(1)}% too low for valid paths`);
  });

  test('Graph Statistics', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Graph Statistics:');
    if (process.env.TEST_DEBUG === '1') console.log(`   Total Relations: ${graphData.relations.length}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Total Users: ${graphData.users.length}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Total Documents: ${graphData.documents.length}`);
    
    const userCount = graphData.users.length;
    const docCount = graphData.documents.length;
    const groupCount = graphData.enterprises ? graphData.enterprises.length : 0;
    
    if (process.env.TEST_DEBUG === '1') console.log(`   Users: ${userCount}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Documents: ${docCount}`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Groups: ${groupCount}`);
    
    const relationTypes = [...new Set(graphData.relations.map(r => r.relation))];
    if (process.env.TEST_DEBUG === '1') console.log(`   Relation Types: ${relationTypes.length} (${relationTypes.join(', ')})`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Valid Chain Paths Found: ${validPaths.length}`);
    
    // Verify we have a reasonable graph size
    assert.ok(graphData.relations.length > 1000, `Graph too small: ${graphData.relations.length} relations`);
    assert.ok(graphData.users.length > 50, `Graph too small: ${graphData.users.length} users`);
  });
});
