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('Memory Scaling Tests', () => {
  let baselineMemory;

  before(() => {
    // Force garbage collection if available
    if (global.gc) {
      global.gc();
    }
    baselineMemory = process.memoryUsage();
  });

  after(() => {
    // Cleanup
    if (global.gc) {
      global.gc();
    }
  });

  /**
   * Get current memory usage in MB
   */
  function getMemoryUsage() {
    const memUsage = process.memoryUsage();
    return {
      heapUsed: memUsage.heapUsed / 1024 / 1024,
      heapTotal: memUsage.heapTotal / 1024 / 1024,
      external: memUsage.external / 1024 / 1024,
      rss: memUsage.rss / 1024 / 1024
    };
  }

  /**
   * Measure memory usage for an operation
   */
  function measureMemoryUsage(operation, description) {
    if (global.gc) global.gc();
    const before = getMemoryUsage();
    
    const result = operation();
    
    if (global.gc) global.gc();
    const after = getMemoryUsage();
    
    const delta = {
      heapUsed: after.heapUsed - before.heapUsed,
      heapTotal: after.heapTotal - before.heapTotal,
      external: after.external - before.external,
      rss: after.rss - before.rss
    };
    
    if (process.env.TEST_DEBUG === '1') console.log(`📊 ${description}:`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Before: ${before.heapUsed.toFixed(2)}MB heap, ${before.rss.toFixed(2)}MB RSS`);
    if (process.env.TEST_DEBUG === '1') console.log(`   After:  ${after.heapUsed.toFixed(2)}MB heap, ${after.rss.toFixed(2)}MB RSS`);
    if (process.env.TEST_DEBUG === '1') console.log(`   Delta:  ${delta.heapUsed.toFixed(2)}MB heap, ${delta.rss.toFixed(2)}MB RSS`);
    
    return { before, after, delta, result };
  }

  test('Memory scaling: Node count', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing memory scaling with node count...');
    
    const nodeCounts = [100, 500, 1000, 2000, 5000];
    const memoryResults = [];
    
    for (const nodeCount of nodeCounts) {
      const result = measureMemoryUsage(() => {
        const arbiter = new Arbiter({ fastConstructionMode: false });
        
        // Add nodes
        for (let i = 0; i < nodeCount; i++) {
          arbiter.addNode(`user:${i}`, 'user');
          arbiter.addNode(`doc:${i}`, 'document');
        }
        
        return arbiter;
      }, `Adding ${nodeCount * 2} nodes`);
      
      memoryResults.push({
        nodeCount: nodeCount * 2,
        heapUsed: result.after.heapUsed,
        heapDelta: result.delta.heapUsed,
        rss: result.after.rss,
        rssDelta: result.delta.rss
      });
    }
    
    // Analyze scaling
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Node Count Memory Scaling Analysis:');
    for (let i = 1; i < memoryResults.length; i++) {
      const prev = memoryResults[i - 1];
      const curr = memoryResults[i];
      const nodeRatio = curr.nodeCount / prev.nodeCount;
      const memoryRatio = curr.heapDelta / prev.heapDelta;
      const scalingFactor = memoryRatio / nodeRatio;
      
      if (process.env.TEST_DEBUG === '1') console.log(`   ${prev.nodeCount} → ${curr.nodeCount} nodes: ${scalingFactor.toFixed(2)}x memory scaling`);
    }
    
    // Verify reasonable scaling (should be roughly linear)
    const lastResult = memoryResults[memoryResults.length - 1];
    assert.ok(lastResult.heapUsed < 100, `Memory usage ${lastResult.heapUsed.toFixed(2)}MB too high for ${lastResult.nodeCount} nodes`);
  });

  test('Memory scaling: Relation count', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing memory scaling with relation count...');
    
    const relationCounts = [500, 1000, 2000, 5000, 10000];
    const memoryResults = [];
    
    for (const relationCount of relationCounts) {
      const result = measureMemoryUsage(() => {
        const arbiter = new Arbiter({ fastConstructionMode: false });
        
        // Add nodes first
        for (let i = 0; i < relationCount; i++) {
          arbiter.addNode(`user:${i}`, 'user');
          arbiter.addNode(`doc:${i}`, 'document');
        }
        
        // Add relations
        for (let i = 0; i < relationCount; i++) {
          arbiter.addRelation(`user:${i}`, 'can_read', `doc:${i}`, { possibility: 1.0 });
        }
        
        return arbiter;
      }, `Adding ${relationCount} relations`);
      
      memoryResults.push({
        relationCount,
        heapUsed: result.after.heapUsed,
        heapDelta: result.delta.heapUsed,
        rss: result.after.rss,
        rssDelta: result.delta.rss
      });
    }
    
    // Analyze scaling
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Relation Count Memory Scaling Analysis:');
    for (let i = 1; i < memoryResults.length; i++) {
      const prev = memoryResults[i - 1];
      const curr = memoryResults[i];
      const relationRatio = curr.relationCount / prev.relationCount;
      const memoryRatio = curr.heapDelta / prev.heapDelta;
      const scalingFactor = memoryRatio / relationRatio;
      
      if (process.env.TEST_DEBUG === '1') console.log(`   ${prev.relationCount} → ${curr.relationCount} relations: ${scalingFactor.toFixed(2)}x memory scaling`);
    }
    
    // Verify reasonable scaling
    const lastResult = memoryResults[memoryResults.length - 1];
    assert.ok(lastResult.heapUsed < 200, `Memory usage ${lastResult.heapUsed.toFixed(2)}MB too high for ${lastResult.relationCount} relations`);
  });

  test('Memory scaling: Graph complexity', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing memory scaling with graph complexity...');
    
    const scales = ['small', 'medium', 'large'];
    const memoryResults = [];
    
    for (const scale of scales) {
      const result = measureMemoryUsage(() => {
        const generator = new BigGraphGenerator({ scale, seed: 12345 });
        const graphData = generator.generateGraph('enterprise');
        const arbiter = generator.loadIntoArbiter(graphData);
        
        return { arbiter, graphData };
      }, `Loading ${scale} scale graph`);
      
      memoryResults.push({
        scale,
        userCount: result.result.graphData.users.length,
        docCount: result.result.graphData.documents.length,
        relationCount: result.result.graphData.relations.length,
        heapUsed: result.after.heapUsed,
        heapDelta: result.delta.heapUsed,
        rss: result.after.rss,
        rssDelta: result.delta.rss
      });
    }
    
    // Analyze scaling
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Graph Complexity Memory Scaling Analysis:');
    for (let i = 1; i < memoryResults.length; i++) {
      const prev = memoryResults[i - 1];
      const curr = memoryResults[i];
      const relationRatio = curr.relationCount / prev.relationCount;
      const memoryRatio = curr.heapDelta / prev.heapDelta;
      const scalingFactor = memoryRatio / relationRatio;
      
      if (process.env.TEST_DEBUG === '1') console.log(`   ${prev.scale} → ${curr.scale}: ${prev.relationCount} → ${curr.relationCount} relations`);
      if (process.env.TEST_DEBUG === '1') console.log(`   Memory scaling: ${scalingFactor.toFixed(2)}x (${prev.heapDelta.toFixed(2)}MB → ${curr.heapDelta.toFixed(2)}MB)`);
    }
    
    // Verify reasonable scaling
    const lastResult = memoryResults[memoryResults.length - 1];
    assert.ok(lastResult.heapUsed < 500, `Memory usage ${lastResult.heapUsed.toFixed(2)}MB too high for ${lastResult.scale} scale`);
  });

  test('Memory scaling: Cache growth', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing memory scaling with cache growth...');
    
    // Set up a graph
    const generator = new BigGraphGenerator({ scale: 'medium', seed: 12345 });
    const graphData = generator.generateGraph('enterprise');
    const arbiter = generator.loadIntoArbiter(graphData);
    
    const queryCounts = [100, 500, 1000, 2000, 5000];
    const memoryResults = [];
    
    for (const queryCount of queryCounts) {
      const result = measureMemoryUsage(() => {
        // Run queries to populate caches
        const relations = graphData.relations.slice(0, queryCount);
        for (const relation of relations) {
          try {
            arbiter.check(relation.src, relation.relation, relation.dst);
          } catch (error) {
            // Ignore errors
          }
        }
      }, `Running ${queryCount} queries (cache population)`);
      
      memoryResults.push({
        queryCount,
        heapUsed: result.after.heapUsed,
        heapDelta: result.delta.heapUsed,
        rss: result.after.rss,
        rssDelta: result.delta.rss
      });
    }
    
    // Analyze cache scaling
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Cache Memory Scaling Analysis:');
    for (let i = 1; i < memoryResults.length; i++) {
      const prev = memoryResults[i - 1];
      const curr = memoryResults[i];
      const queryRatio = curr.queryCount / prev.queryCount;
      const memoryRatio = curr.heapDelta / prev.heapDelta;
      const scalingFactor = memoryRatio / queryRatio;
      
      if (process.env.TEST_DEBUG === '1') console.log(`   ${prev.queryCount} → ${curr.queryCount} queries: ${scalingFactor.toFixed(2)}x memory scaling`);
    }
    
    // Verify cache doesn't grow excessively
    const lastResult = memoryResults[memoryResults.length - 1];
    assert.ok(lastResult.heapDelta < 50, `Cache memory growth ${lastResult.heapDelta.toFixed(2)}MB too high for ${lastResult.queryCount} queries`);
  });

  test('Memory scaling: Indices memory usage', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing memory scaling with indices...');
    
    const scales = ['small', 'medium'];
    const memoryResults = [];
    
    for (const scale of scales) {
      // Test without indices
      const resultWithoutIndices = measureMemoryUsage(() => {
        const arbiter = new Arbiter({ fastConstructionMode: true });
        const generator = new BigGraphGenerator({ scale, seed: 12345 });
        const graphData = generator.generateGraph('enterprise');
        
        // Add relations without building indices
        for (const relation of graphData.relations) {
          arbiter.addRelation(relation.src, relation.relation, relation.dst, { possibility: relation.possibility });
        }
        
        return arbiter;
      }, `${scale} scale without indices`);
      
      // Test with indices
      const resultWithIndices = measureMemoryUsage(() => {
        const arbiter = new Arbiter({ fastConstructionMode: false });
        const generator = new BigGraphGenerator({ scale, seed: 12345 });
        const graphData = generator.generateGraph('enterprise');
        const loadedArbiter = generator.loadIntoArbiter(graphData);
        
        return loadedArbiter;
      }, `${scale} scale with indices`);
      
      const indexMemoryOverhead = resultWithIndices.after.heapUsed - resultWithoutIndices.after.heapUsed;
      
      memoryResults.push({
        scale,
        relationCount: resultWithIndices.result.relations.length,
        withoutIndices: resultWithoutIndices.after.heapUsed,
        withIndices: resultWithIndices.after.heapUsed,
        indexOverhead: indexMemoryOverhead,
        overheadPerRelation: indexMemoryOverhead / resultWithIndices.result.relations.length
      });
    }
    
    // Analyze index memory overhead
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Index Memory Overhead Analysis:');
    for (const result of memoryResults) {
      if (process.env.TEST_DEBUG === '1') console.log(`   ${result.scale} scale (${result.relationCount} relations):`);
      if (process.env.TEST_DEBUG === '1') console.log(`     Without indices: ${result.withoutIndices.toFixed(2)}MB`);
      if (process.env.TEST_DEBUG === '1') console.log(`     With indices: ${result.withIndices.toFixed(2)}MB`);
      if (process.env.TEST_DEBUG === '1') console.log(`     Index overhead: ${result.indexOverhead.toFixed(2)}MB (${result.overheadPerRelation.toFixed(4)}MB per relation)`);
    }
    
    // Verify reasonable index overhead
    const lastResult = memoryResults[memoryResults.length - 1];
    assert.ok(lastResult.overheadPerRelation < 0.01, `Index overhead ${lastResult.overheadPerRelation.toFixed(4)}MB per relation too high`);
  });

  test('Memory scaling: Long-running operations', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing memory scaling with long-running operations...');
    
    const arbiter = new Arbiter({ fastConstructionMode: false });
    const generator = new BigGraphGenerator({ scale: 'medium', seed: 12345 });
    const graphData = generator.generateGraph('enterprise');
    const loadedArbiter = generator.loadIntoArbiter(graphData);
    
    const operationCounts = [1000, 5000, 10000, 20000];
    const memoryResults = [];
    
    for (const opCount of operationCounts) {
      const result = measureMemoryUsage(() => {
        // Mix of operations
        for (let i = 0; i < opCount; i++) {
          const opType = i % 4;
          const relation = graphData.relations[i % graphData.relations.length];
          
          switch (opType) {
            case 0: // Add relation
              loadedArbiter.addRelation(`${relation.src}-${i}`, relation.relation, `${relation.dst}-${i}`, { possibility: 1.0 });
              break;
            case 1: // Remove relation
              loadedArbiter.removeRelation(`${relation.src}-${i}`, relation.relation, `${relation.dst}-${i}`);
              break;
            case 2: // Query
              loadedArbiter.check(relation.src, relation.relation, relation.dst);
              break;
            case 3: // Update relation
              loadedArbiter.addRelation(`${relation.src}-${i}`, relation.relation, `${relation.dst}-${i}`, { possibility: 0.8 });
              break;
          }
        }
      }, `Running ${opCount} mixed operations`);
      
      memoryResults.push({
        operationCount: opCount,
        heapUsed: result.after.heapUsed,
        heapDelta: result.delta.heapUsed,
        rss: result.after.rss,
        rssDelta: result.delta.rss
      });
    }
    
    // Analyze long-running memory scaling
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Long-running Operations Memory Scaling Analysis:');
    for (let i = 1; i < memoryResults.length; i++) {
      const prev = memoryResults[i - 1];
      const curr = memoryResults[i];
      const opRatio = curr.operationCount / prev.operationCount;
      const memoryRatio = curr.heapDelta / prev.heapDelta;
      const scalingFactor = memoryRatio / opRatio;
      
      if (process.env.TEST_DEBUG === '1') console.log(`   ${prev.operationCount} → ${curr.operationCount} operations: ${scalingFactor.toFixed(2)}x memory scaling`);
    }
    
    // Verify no memory leaks
    const lastResult = memoryResults[memoryResults.length - 1];
    assert.ok(lastResult.heapDelta < 100, `Memory growth ${lastResult.heapDelta.toFixed(2)}MB too high for ${lastResult.operationCount} operations`);
  });

  test('Memory scaling: Memory pressure test', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing memory under pressure...');
    
    const pressureLevels = [1000, 5000, 10000, 20000, 50000];
    const memoryResults = [];
    
    for (const pressureLevel of pressureLevels) {
      const result = measureMemoryUsage(() => {
        const arbiter = new Arbiter({ fastConstructionMode: false });
        
        // Create memory pressure by adding many relations
        for (let i = 0; i < pressureLevel; i++) {
          arbiter.addNode(`user:${i}`, 'user');
          arbiter.addNode(`doc:${i}`, 'document');
          arbiter.addRelation(`user:${i}`, 'can_read', `doc:${i}`, { possibility: 1.0 });
          arbiter.addRelation(`user:${i}`, 'can_write', `doc:${i}`, { possibility: 0.8 });
          arbiter.addRelation(`user:${i}`, 'can_delete', `doc:${i}`, { possibility: 0.6 });
        }
        
        return arbiter;
      }, `Memory pressure with ${pressureLevel} entities`);
      
      memoryResults.push({
        entityCount: pressureLevel,
        heapUsed: result.after.heapUsed,
        heapDelta: result.delta.heapUsed,
        rss: result.after.rss,
        rssDelta: result.delta.rss,
        memoryPerEntity: result.delta.heapUsed / pressureLevel
      });
    }
    
    // Analyze memory pressure scaling
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Memory Pressure Scaling Analysis:');
    for (const result of memoryResults) {
      if (process.env.TEST_DEBUG === '1') console.log(`   ${result.entityCount} entities: ${result.heapUsed.toFixed(2)}MB total, ${result.memoryPerEntity.toFixed(4)}MB per entity`);
    }
    
    // Verify reasonable memory usage under pressure
    const lastResult = memoryResults[memoryResults.length - 1];
    assert.ok(lastResult.memoryPerEntity < 0.1, `Memory per entity ${lastResult.memoryPerEntity.toFixed(4)}MB too high`);
    assert.ok(lastResult.heapUsed < 1000, `Total memory usage ${lastResult.heapUsed.toFixed(2)}MB too high under pressure`);
  });

  test('Memory scaling: Garbage collection impact', async () => {
    if (process.env.TEST_DEBUG === '1') console.log('🧠 Testing garbage collection impact...');
    
    // Test without explicit GC
    const resultWithoutGC = measureMemoryUsage(() => {
      const arbiter = new Arbiter({ fastConstructionMode: false });
      const generator = new BigGraphGenerator({ scale: 'medium', seed: 12345 });
      const graphData = generator.generateGraph('enterprise');
      const loadedArbiter = generator.loadIntoArbiter(graphData);
      
      // Run many operations without GC
      for (let i = 0; i < 10000; i++) {
        const relation = graphData.relations[i % graphData.relations.length];
        loadedArbiter.check(relation.src, relation.relation, relation.dst);
      }
      
      return loadedArbiter;
    }, 'Operations without explicit GC');
    
    // Test with explicit GC
    const resultWithGC = measureMemoryUsage(() => {
      const arbiter = new Arbiter({ fastConstructionMode: false });
      const generator = new BigGraphGenerator({ scale: 'medium', seed: 12345 });
      const graphData = generator.generateGraph('enterprise');
      const loadedArbiter = generator.loadIntoArbiter(graphData);
      
      // Run many operations with periodic GC
      for (let i = 0; i < 10000; i++) {
        const relation = graphData.relations[i % graphData.relations.length];
        loadedArbiter.check(relation.src, relation.relation, relation.dst);
        
        if (i % 1000 === 0 && global.gc) {
          global.gc();
        }
      }
      
      return loadedArbiter;
    }, 'Operations with periodic GC');
    
    if (process.env.TEST_DEBUG === '1') console.log('\n📈 Garbage Collection Impact Analysis:');
    if (process.env.TEST_DEBUG === '1') console.log(`   Without GC: ${resultWithoutGC.after.heapUsed.toFixed(2)}MB heap, ${resultWithoutGC.after.rss.toFixed(2)}MB RSS`);
    if (process.env.TEST_DEBUG === '1') console.log(`   With GC:    ${resultWithGC.after.heapUsed.toFixed(2)}MB heap, ${resultWithGC.after.rss.toFixed(2)}MB RSS`);
    
    const gcBenefit = resultWithoutGC.after.heapUsed - resultWithGC.after.heapUsed;
    if (process.env.TEST_DEBUG === '1') console.log(`   GC Benefit: ${gcBenefit.toFixed(2)}MB heap reduction`);
    
    // Verify GC helps (if available)
    if (global.gc) {
      assert.ok(gcBenefit >= 0, 'Garbage collection should not increase memory usage');
    }
  });
});
