import { test, describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../src/index.js';

/**
 * Performance Regression Tests
 * 
 * Tests that performance optimizations don't regress and that
 * authorization remains fast even with large datasets.
 */

describe.skip('Performance Regression Tests', () => {
  let arbiter;

  beforeEach(() => {
    arbiter = new Arbiter({ 
      embeddingDimensions: 256,
      directCheckCacheSize: 1000,
      directCheckCacheTTL: 60000
    });
  });

  describe('Large Dataset Performance', () => {
    it('maintains performance with large number of nodes', () => {
      const nodeCount = 1000;
      const startTime = Date.now();
      
      // Create many nodes
      for (let i = 0; i < nodeCount; i++) {
        arbiter.addNode(`user:user-${i}`, 'user');
        arbiter.addNode(`group:group-${i}`, 'group');
        arbiter.addNode(`document:doc-${i}`, 'document');
      }
      
      const creationTime = Date.now() - startTime;
      if (process.env.TEST_DEBUG === '1') console.log(`Created ${nodeCount * 3} nodes in ${creationTime}ms`);
      
      // Set up relations
      arbiter.setRelationConfig('member_of', { type: 'direct' });
      arbiter.setRelationConfig('owner', { type: 'direct' });
      
      const relationStartTime = Date.now();
      for (let i = 0; i < nodeCount; i++) {
        arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${i}`);
        arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
      }
      
      const relationTime = Date.now() - relationStartTime;
      if (process.env.TEST_DEBUG === '1') console.log(`Created ${nodeCount * 2} relations in ${relationTime}ms`);
      
      // Test authorization performance
      const authStartTime = Date.now();
      for (let i = 0; i < 100; i++) {
        const result = arbiter.check(`user:user-${i}`, 'member_of', `group:group-${i}`);
        assert.equal(result.possibility, 1.0);
      }
      
      const authTime = Date.now() - authStartTime;
      if (process.env.TEST_DEBUG === '1') console.log(`Performed 100 authorization checks in ${authTime}ms`);
      
      // Performance assertions
      assert.ok(creationTime < 5000, `Node creation too slow: ${creationTime}ms`);
      assert.ok(relationTime < 3000, `Relation creation too slow: ${relationTime}ms`);
      assert.ok(authTime < 1000, `Authorization too slow: ${authTime}ms`);
    });

    it('maintains performance with complex tuple-to-userset rules', () => {
      const groupCount = 100;
      const userCount = 50;
      
      // Create groups and users
      for (let i = 0; i < groupCount; i++) {
        arbiter.addNode(`group:group-${i}`, 'group');
      }
      for (let i = 0; i < userCount; i++) {
        arbiter.addNode(`user:user-${i}`, 'user');
      }
      
      arbiter.setRelationConfig('member_of', { type: 'direct' });
      arbiter.setRelationConfig('owner', { type: 'direct' });
      
      // Create complex membership patterns
      for (let i = 0; i < userCount; i++) {
        // Each user is member of multiple groups
        for (let j = 0; j < 5; j++) {
          const groupIndex = (i + j) % groupCount;
          arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${groupIndex}`);
        }
      }
      
      // Create document ownership
      for (let i = 0; i < groupCount; i++) {
        arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
      }
      
      // Set up tuple-to-userset rule
      arbiter.setRelationConfig('group_access', {
        type: 'tuple_to_userset',
        tuplesetRelation: 'owner',
        computedRelation: 'member_of'
      });
      
      const startTime = Date.now();
      
      // Test authorization performance
      for (let i = 0; i < 50; i++) {
        const result = arbiter.check(`user:user-${i}`, 'group_access', `document:doc-${i}`);
        assert.ok(result.possibility >= 0);
      }
      
      const duration = Date.now() - startTime;
      if (process.env.TEST_DEBUG === '1') console.log(`Complex tuple-to-userset authorization: ${duration}ms for 50 checks`);
      
      // Should complete within reasonable time
      assert.ok(duration < 2000, `Complex authorization too slow: ${duration}ms`);
    });

    it('maintains performance with deep chain rules', () => {
      const depth = 10;
      const nodeCount = depth + 1;
      
      // Create chain: user → group1 → group2 → ... → groupN → document
      for (let i = 0; i < nodeCount; i++) {
        arbiter.addNode(`entity:entity-${i}`, 'entity');
      }
      
      arbiter.setRelationConfig('next', { type: 'direct' });
      
      // Create chain relations
      for (let i = 0; i < depth; i++) {
        arbiter.addRelation(`entity:entity-${i}`, 'next', `entity:entity-${i + 1}`);
      }
      
      // Set up deep chain rule
      const steps = [];
      for (let i = 0; i < depth; i++) {
        steps.push({ relation: 'next', direction: 'out' });
      }
      
      arbiter.setRelationConfig('deep_chain', {
        type: 'chain',
        steps: steps
      });
      
      const startTime = Date.now();
      
      // Test deep chain authorization
      const result = arbiter.check('entity:entity-0', 'deep_chain', `entity:entity-${depth}`);
      
      const duration = Date.now() - startTime;
      if (process.env.TEST_DEBUG === '1') console.log(`Deep chain authorization (depth ${depth}): ${duration}ms`);
      
      assert.equal(result.possibility, 1.0);
      assert.ok(duration < 1000, `Deep chain too slow: ${duration}ms`);
    });
  });

  describe('Cache Performance', () => {
    it('demonstrates cache effectiveness with repeated queries', () => {
      // Set up simple authorization
      arbiter.addNode('user:alice', 'user');
      arbiter.addNode('document:spec', 'document');
      arbiter.addRelation('user:alice', 'can_read', 'document:spec', { possibility: 1.0 });
      
      arbiter.setRelationConfig('can_read', { type: 'direct' });
      
      // First query (cache miss)
      const startTime1 = Date.now();
      const result1 = arbiter.check('user:alice', 'can_read', 'document:spec');
      const time1 = Date.now() - startTime1;
      
      // Second query (cache hit)
      const startTime2 = Date.now();
      const result2 = arbiter.check('user:alice', 'can_read', 'document:spec');
      const time2 = Date.now() - startTime2;
      
      if (process.env.TEST_DEBUG === '1') console.log(`First query: ${time1}ms, Second query: ${time2}ms`);
      
      assert.equal(result1.possibility, 1.0);
      assert.equal(result2.possibility, 1.0);
      
      // Cache should make second query faster (though this might be too fast to measure)
      assert.ok(time2 <= time1, 'Cache should not make queries slower');
    });

    it('handles cache eviction under memory pressure', () => {
      // Set up cache with small size
      const smallArbiter = new Arbiter({ 
        directCheckCacheSize: 10,  // Very small cache
        directCheckCacheTTL: 60000
      });
      
      smallArbiter.setRelationConfig('can_read', { type: 'direct' });
      
      // Create many users and documents
      for (let i = 0; i < 50; i++) {
        smallArbiter.addNode(`user:user-${i}`, 'user');
        smallArbiter.addNode(`document:doc-${i}`, 'document');
        smallArbiter.addRelation(`user:user-${i}`, 'can_read', `document:doc-${i}`, { possibility: 1.0 });
      }
      
      const startTime = Date.now();
      
      // Perform many authorization checks to trigger cache eviction
      for (let i = 0; i < 100; i++) {
        const userIndex = i % 50;
        const docIndex = (i + 1) % 50;
        const result = smallArbiter.check(`user:user-${userIndex}`, 'can_read', `document:doc-${docIndex}`);
        assert.ok(result.possibility >= 0);
      }
      
      const duration = Date.now() - startTime;
      if (process.env.TEST_DEBUG === '1') console.log(`Cache eviction test: ${duration}ms for 100 checks`);
      
      // Should complete within reasonable time even with cache eviction
      assert.ok(duration < 3000, `Cache eviction too slow: ${duration}ms`);
    });
  });

  describe('Binary Mode Performance', () => {
    it('demonstrates binary mode performance benefits', () => {
      // Set up authorization scenario
      arbiter.addNode('user:alice', 'user');
      arbiter.addNode('document:spec', 'document');
      arbiter.addRelation('user:alice', 'can_read', 'document:spec', { possibility: 1.0 });
      
      arbiter.setRelationConfig('can_read', { type: 'direct' });
      
      // Test normal mode
      const normalStartTime = Date.now();
      for (let i = 0; i < 1000; i++) {
        const result = arbiter.check('user:alice', 'can_read', 'document:spec');
        assert.equal(result.possibility, 1.0);
      }
      const normalTime = Date.now() - normalStartTime;
      
      // Test binary mode
      const binaryStartTime = Date.now();
      for (let i = 0; i < 1000; i++) {
        const result = arbiter.check('user:alice', 'can_read', 'document:spec', { binary: true });
        assert.equal(result.possibility, 1.0);
      }
      const binaryTime = Date.now() - binaryStartTime;
      
      if (process.env.TEST_DEBUG === '1') console.log(`Normal mode: ${normalTime}ms, Binary mode: ${binaryTime}ms`);
      
      // Binary mode should be faster for simple cases
      assert.ok(binaryTime <= normalTime, 'Binary mode should not be slower than normal mode');
    });

    it('maintains binary mode performance with complex rules', () => {
      // Set up complex tuple-to-userset scenario
      for (let i = 0; i < 20; i++) {
        arbiter.addNode(`user:user-${i}`, 'user');
        arbiter.addNode(`group:group-${i}`, 'group');
        arbiter.addNode(`document:doc-${i}`, 'document');
        
        arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${i}`);
        arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
      }
      
      arbiter.setRelationConfig('member_of', { type: 'direct' });
      arbiter.setRelationConfig('owner', { type: 'direct' });
      arbiter.setRelationConfig('group_access', {
        type: 'tuple_to_userset',
        tuplesetRelation: 'owner',
        computedRelation: 'member_of'
      });
      
      const startTime = Date.now();
      
      // Test binary mode with complex rules
      for (let i = 0; i < 100; i++) {
        const userIndex = i % 20;
        const docIndex = i % 20;
        const result = arbiter.check(`user:user-${userIndex}`, 'group_access', `document:doc-${docIndex}`, { binary: true });
        assert.ok(result.possibility >= 0);
      }
      
      const duration = Date.now() - startTime;
      if (process.env.TEST_DEBUG === '1') console.log(`Binary mode complex rules: ${duration}ms for 100 checks`);
      
      // Should complete within reasonable time
      assert.ok(duration < 2000, `Binary mode too slow: ${duration}ms`);
    });
  });

  describe('Memory Usage Performance', () => {
    it('tracks memory usage during large operations', () => {
      const initialMemory = process.memoryUsage();
      
      // Create large dataset
      const nodeCount = 500;
      for (let i = 0; i < nodeCount; i++) {
        arbiter.addNode(`user:user-${i}`, 'user');
        arbiter.addNode(`group:group-${i}`, 'group');
        arbiter.addNode(`document:doc-${i}`, 'document');
      }
      
      const afterNodesMemory = process.memoryUsage();
      
      // Add relations
      arbiter.setRelationConfig('member_of', { type: 'direct' });
      arbiter.setRelationConfig('owner', { type: 'direct' });
      
      for (let i = 0; i < nodeCount; i++) {
        arbiter.addRelation(`user:user-${i}`, 'member_of', `group:group-${i}`);
        arbiter.addRelation(`document:doc-${i}`, 'owner', `group:group-${i}`);
      }
      
      const afterRelationsMemory = process.memoryUsage();
      
      // Perform authorization checks
      for (let i = 0; i < 100; i++) {
        arbiter.check(`user:user-${i % nodeCount}`, 'member_of', `group:group-${i % nodeCount}`);
      }
      
      const finalMemory = process.memoryUsage();
      
      if (process.env.TEST_DEBUG === '1') console.log('Memory usage:');
      if (process.env.TEST_DEBUG === '1') console.log(`  Initial: ${Math.round(initialMemory.heapUsed / 1024 / 1024)}MB`);
      if (process.env.TEST_DEBUG === '1') console.log(`  After nodes: ${Math.round(afterNodesMemory.heapUsed / 1024 / 1024)}MB`);
      if (process.env.TEST_DEBUG === '1') console.log(`  After relations: ${Math.round(afterRelationsMemory.heapUsed / 1024 / 1024)}MB`);
      if (process.env.TEST_DEBUG === '1') console.log(`  Final: ${Math.round(finalMemory.heapUsed / 1024 / 1024)}MB`);
      
      // Memory usage should be reasonable
      const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed;
      const memoryIncreaseMB = memoryIncrease / 1024 / 1024;
      
      assert.ok(memoryIncreaseMB < 100, `Memory usage too high: ${memoryIncreaseMB}MB increase`);
    });
  });

  describe('Concurrent Performance', () => {
    it('handles concurrent authorization requests efficiently', async () => {
      // Set up test data
      for (let i = 0; i < 100; i++) {
        arbiter.addNode(`user:user-${i}`, 'user');
        arbiter.addNode(`document:doc-${i}`, 'document');
        arbiter.addRelation(`user:user-${i}`, 'can_read', `document:doc-${i}`, { possibility: 1.0 });
      }
      
      arbiter.setRelationConfig('can_read', { type: 'direct' });
      
      const startTime = Date.now();
      
      // Create concurrent authorization requests
      const promises = [];
      for (let i = 0; i < 50; i++) {
        promises.push(
          new Promise((resolve) => {
            const result = arbiter.check(`user:user-${i}`, 'can_read', `document:doc-${i}`);
            resolve(result);
          })
        );
      }
      
      const results = await Promise.all(promises);
      const duration = Date.now() - startTime;
      
      if (process.env.TEST_DEBUG === '1') console.log(`Concurrent authorization: ${duration}ms for 50 requests`);
      
      // All results should be successful
      for (const result of results) {
        assert.equal(result.possibility, 1.0);
      }
      
      // Should complete within reasonable time
      assert.ok(duration < 1000, `Concurrent authorization too slow: ${duration}ms`);
    });
  });
});
