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('QPS Benchmark Tests', () => {
  let arbiter;
  let generator;
  let testNodes;
  let testRelations;

  before(() => {
    arbiter = new Arbiter({ fastConstructionMode: false });
    generator = new BigGraphGenerator({ scale: 'small', seed: 12345 });
    
    // Pre-generate test data
    testNodes = [];
    testRelations = [];
    
    // Generate test nodes
    for (let i = 0; i < 1000; i++) {
      testNodes.push({
        id: `user:test-${i}`,
        type: 'user'
      });
      testNodes.push({
        id: `doc:test-${i}`,
        type: 'document'
      });
    }
    
    // Generate test relations
    for (let i = 0; i < 1000; i++) {
      testRelations.push({
        src: `user:test-${i}`,
        relation: 'can_read',
        dst: `doc:test-${i}`,
        options: { possibility: 1.0 }
      });
    }
  });

  after(() => {
    arbiter = null;
    generator = null;
    testNodes = null;
    testRelations = null;
  });

  /**
   * Measure QPS for a given operation
   */
  function measureQPS(operation, duration = 2000) {
    const startTime = Date.now();
    const endTime = startTime + duration;
    let operationCount = 0;
    const latencies = [];

    while (Date.now() < endTime) {
      const opStart = process.hrtime.bigint();
      
      try {
        operation();
        operationCount++;
      } catch (error) {
        // Count failed operations too
        operationCount++;
      }
      
      const opEnd = process.hrtime.bigint();
      const latency = Number(opEnd - opStart) / 1000000; // Convert to milliseconds
      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]
    };
  }

  test('QPS: Node Insert Operations', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Node Insert QPS...');
    
    let nodeIndex = 0;
    const result = measureQPS(() => {
      const node = testNodes[nodeIndex % testNodes.length];
      arbiter.addNode(`${node.id}-${nodeIndex}`, node.type);
      nodeIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 1000, `Node insert QPS ${result.qps.toFixed(0)} below 1000 threshold`);
    assert.ok(result.avgLatency < 10, `Node insert latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Relation Insert Operations', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Relation Insert QPS...');
    
    let relationIndex = 0;
    const result = measureQPS(() => {
      const relation = testRelations[relationIndex % testRelations.length];
      arbiter.addRelation(`${relation.src}-${relationIndex}`, relation.relation, `${relation.dst}-${relationIndex}`, relation.options);
      relationIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 500, `Relation insert QPS ${result.qps.toFixed(0)} below 500 threshold`);
    assert.ok(result.avgLatency < 20, `Relation insert latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Relation Update Operations', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Relation Update QPS...');
    
    // First, add some relations to update
    for (let i = 0; i < 100; i++) {
      const relation = testRelations[i];
      arbiter.addRelation(`${relation.src}-update`, relation.relation, `${relation.dst}-update`, relation.options);
    }
    
    let updateIndex = 0;
    const result = measureQPS(() => {
      const relation = testRelations[updateIndex % testRelations.length];
      arbiter.addRelation(`${relation.src}-update`, relation.relation, `${relation.dst}-update`, { 
        possibility: updateIndex % 2 === 0 ? 0.8 : 0.9 
      });
      updateIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 200, `Relation update QPS ${result.qps.toFixed(0)} below 200 threshold`);
    assert.ok(result.avgLatency < 50, `Relation update latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Relation Delete Operations', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Relation Delete QPS...');
    
    // First, add some relations to delete
    for (let i = 0; i < 200; i++) {
      const relation = testRelations[i];
      arbiter.addRelation(`${relation.src}-delete`, relation.relation, `${relation.dst}-delete`, relation.options);
    }
    
    let deleteIndex = 0;
    const result = measureQPS(() => {
      const relation = testRelations[deleteIndex % testRelations.length];
      arbiter.removeRelation(`${relation.src}-delete`, relation.relation, `${relation.dst}-delete`);
      deleteIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 100, `Relation delete QPS ${result.qps.toFixed(0)} below 100 threshold`);
    assert.ok(result.avgLatency < 100, `Relation delete latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Simple Direct Queries', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Simple Direct Query QPS...');
    
    // Set up test data
    const graphData = generator.generateGraph('enterprise');
    const testArbiter = generator.loadIntoArbiter(graphData);
    
    // Get some test relations
    const directRelations = graphData.relations.filter(r => 
      r.src.startsWith('user:') && r.dst.startsWith('doc:') && 
      ['can_read', 'can_write', 'can_delete'].includes(r.relation)
    ).slice(0, 100);
    
    let queryIndex = 0;
    const result = measureQPS(() => {
      const relation = directRelations[queryIndex % directRelations.length];
      testArbiter.check(relation.src, relation.relation, relation.dst);
      queryIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 1000, `Simple query QPS ${result.qps.toFixed(0)} below 1000 threshold`);
    assert.ok(result.avgLatency < 5, `Simple query latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Complex Chain Queries', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Complex Chain Query QPS...');
    
    // Set up test data with chain rules
    const graphData = generator.generateGraph('enterprise');
    const 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' }
      ]
    });
    
    // Get some test relations for chain queries
    const chainRelations = graphData.relations.filter(r => 
      r.src.startsWith('user:') && r.dst.startsWith('doc:')
    ).slice(0, 50);
    
    let queryIndex = 0;
    const result = measureQPS(() => {
      const relation = chainRelations[queryIndex % chainRelations.length];
      testArbiter.check(relation.src, 'can_read_via_role', relation.dst);
      queryIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 100, `Chain query QPS ${result.qps.toFixed(0)} below 100 threshold`);
    assert.ok(result.avgLatency < 50, `Chain query latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Multi-hop Complex Queries', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Multi-hop Complex Query QPS...');
    
    // Set up test data with complex chain rules
    const graphData = generator.generateGraph('enterprise');
    const testArbiter = generator.loadIntoArbiter(graphData);
    
    // Configure complex multi-hop chain rules
    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' }
      ]
    });
    
    // Get some test relations for multi-hop queries
    const multiHopRelations = graphData.relations.filter(r => 
      r.src.startsWith('user:') && r.dst.startsWith('doc:')
    ).slice(0, 20);
    
    let queryIndex = 0;
    const result = measureQPS(() => {
      const relation = multiHopRelations[queryIndex % multiHopRelations.length];
      testArbiter.check(relation.src, 'can_access_multi_hop', relation.dst);
      queryIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 10, `Multi-hop query QPS ${result.qps.toFixed(0)} below 10 threshold`);
    assert.ok(result.avgLatency < 200, `Multi-hop query latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Relational Comparator Queries', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Relational Comparator Query QPS...');
    
    // Set up test data with relational comparator rules
    const graphData = generator.generateGraph('enterprise');
    const testArbiter = generator.loadIntoArbiter(graphData);
    
    // Add some value relations for comparison
    for (let i = 0; i < 10; i++) {
      testArbiter.addRelation(`user:alice-${i}`, 'has_balance', `user:alice-${i}`, { value: 1000 + i * 100 });
      testArbiter.addRelation(`feature:premium-${i}`, 'has_price', `feature:premium-${i}`, { value: 800 + i * 50 });
    }
    
    // Configure relational comparator rule
    testArbiter.setRelationConfig('balance_check', {
      type: 'relational_comparator',
      left: { 
        rule: { type: 'direct', relation: 'has_balance' },
        extractValue: true
      },
      right: { 
        evaluateFrom: 'object',
        rule: { type: 'direct', relation: 'has_price' },
        extractValue: true
      },
      comparator: '>'
    });
    
    let queryIndex = 0;
    const result = measureQPS(() => {
      const userIndex = queryIndex % 10;
      testArbiter.check(`user:alice-${userIndex}`, 'balance_check', `feature:premium-${userIndex}`);
      queryIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 50, `Relational comparator QPS ${result.qps.toFixed(0)} below 50 threshold`);
    assert.ok(result.avgLatency < 100, `Relational comparator latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Batch Operations', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Batch Operations QPS...');
    
    const testArbiter = new Arbiter({ fastConstructionMode: false });
    
    // Generate batch data
    const batchSize = 100;
    const batches = [];
    for (let i = 0; i < 10; i++) {
      const batch = [];
      for (let j = 0; j < batchSize; j++) {
        batch.push({
          src: `user:batch-${i}-${j}`,
          relation: 'can_read',
          dst: `doc:batch-${i}-${j}`,
          options: { possibility: 1.0 }
        });
      }
      batches.push(batch);
    }
    
    let batchIndex = 0;
    const result = measureQPS(() => {
      const batch = batches[batchIndex % batches.length];
      
      // Add nodes first
      for (const relation of batch) {
        testArbiter.addNode(relation.src, 'user');
        testArbiter.addNode(relation.dst, 'document');
      }
      
      // Add relations
      for (const relation of batch) {
        testArbiter.addRelation(relation.src, relation.relation, relation.dst, relation.options);
      }
      
      batchIndex++;
    }, 2000);

    if (process.env.TEST_DEBUG === '1') console.log(`   QPS: ${result.qps.toFixed(2)} (batches per second)`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Operations: ${result.operationCount} batches`);
    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 per batch`);
    if (process.env.TEST_DEBUG === '1') console.log(`   P95 Latency: ${result.p95Latency.toFixed(3)}ms per batch`);
    if (process.env.TEST_DEBUG === '1') console.log(`   P99 Latency: ${result.p99Latency.toFixed(3)}ms per batch`);

    // Verify reasonable performance
    assert.ok(result.qps > 1, `Batch operation QPS ${result.qps.toFixed(0)} below 1 threshold`);
    assert.ok(result.avgLatency < 1000, `Batch operation latency ${result.avgLatency.toFixed(3)}ms too high`);
  });

  test('QPS: Mixed Workload', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('📊 Testing Mixed Workload QPS...');
    
    const testArbiter = new Arbiter({ fastConstructionMode: false });
    const graphData = generator.generateGraph('enterprise');
    const loadedArbiter = generator.loadIntoArbiter(graphData);
    
    // Get test relations
    const testRelations = graphData.relations.slice(0, 50);
    
    let operationIndex = 0;
    const result = measureQPS(() => {
      const opType = operationIndex % 4;
      const relation = testRelations[operationIndex % testRelations.length];
      
      switch (opType) {
        case 0: // Insert
          testArbiter.addRelation(`${relation.src}-mixed`, relation.relation, `${relation.dst}-mixed`, relation.options);
          break;
        case 1: // Update
          testArbiter.addRelation(`${relation.src}-mixed`, relation.relation, `${relation.dst}-mixed`, { possibility: 0.8 });
          break;
        case 2: // Delete
          testArbiter.removeRelation(`${relation.src}-mixed`, relation.relation, `${relation.dst}-mixed`);
          break;
        case 3: // Query
          loadedArbiter.check(relation.src, relation.relation, relation.dst);
          break;
      }
      
      operationIndex++;
    }, 2000);

    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`);

    // Verify reasonable performance
    assert.ok(result.qps > 100, `Mixed workload QPS ${result.qps.toFixed(0)} below 100 threshold`);
    assert.ok(result.avgLatency < 50, `Mixed workload latency ${result.avgLatency.toFixed(3)}ms too high`);
  });
});
