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

const runPerf = process.env.RUN_PERF_TESTS === '1';
const perfTest = runPerf ? test : test.skip;

describe('Memory Usage and Performance', () => {
  perfTest('memory usage grows linearly with node count', () => {
    const arbiter = new Arbiter();
    const numNodes = 10000;
    
    for (let i = 0; i < numNodes; i++) {
      arbiter.addNode(`node:${i}`, 'node');
    }
    
    const memoryUsage = process.memoryUsage();
    const memoryMB = memoryUsage.heapUsed / (1024 * 1024);
    
    console.log(`Memory for ${numNodes} nodes: ${memoryMB.toFixed(2)} MB`);
    
    assert.ok(memoryMB < 100, `Memory usage (${memoryMB.toFixed(2)} MB) should be reasonable`);
  });

  perfTest('memory usage for graph with edges', () => {
    const arbiter = new Arbiter();
    const numNodes = 5000;
    const edgesPerNode = 3;
    
    for (let i = 0; i < numNodes; i++) {
      arbiter.addNode(`node:${i}`, 'node');
    }
    
    arbiter.setRelationConfig('connect', { type: 'direct' });
    
    for (let i = 0; i < numNodes; i++) {
      for (let j = 1; j <= edgesPerNode && i + j < numNodes; j++) {
        arbiter.addRelation(`node:${i}`, 'connect', `node:${i + j}`, 1.0);
      }
    }
    
    const memoryUsage = process.memoryUsage();
    const memoryMB = memoryUsage.heapUsed / (1024 * 1024);
    
    console.log(`Memory for ${numNodes} nodes with ${numNodes * edgesPerNode} edges: ${memoryMB.toFixed(2)} MB`);
    
    assert.ok(memoryMB < 200, `Memory usage (${memoryMB.toFixed(2)} MB) should be reasonable`);
  });

  perfTest('large graph stays within 128MB limit', () => {
    const arbiter = new Arbiter();
    const numNodes = 30000;
    const edgesPerNode = 3;
    
    arbiter.setRelationConfig('connect', { type: 'direct' });
    
    for (let i = 0; i < numNodes; i++) {
      arbiter.addNode(`node:${i}`, 'node');
    }
    
    for (let i = 0; i < numNodes; i++) {
      for (let j = 1; j <= edgesPerNode && i + j < numNodes; j++) {
        arbiter.addRelation(`node:${i}`, 'connect', `node:${i + j}`, 1.0);
      }
    }
    
    const memoryUsage = process.memoryUsage();
    const memoryMB = memoryUsage.heapUsed / (1024 * 1024);
    
    console.log(`Memory for ${numNodes} nodes: ${memoryMB.toFixed(2)} MB`);
    
    assert.ok(memoryMB < 250, `Memory usage (${memoryMB.toFixed(2)} MB) must be reasonable`);
  });

  perfTest('direct check cache improves performance', () => {
    const arbiter = new Arbiter();
    
    for (let i = 0; i < 1000; i++) {
      arbiter.addNode(`user:${i}`, 'user');
      arbiter.addNode(`resource:${i}`, 'resource');
    }
    
    arbiter.setRelationConfig('member', { type: 'direct' });
    
    for (let i = 0; i < 1000; i++) {
      arbiter.addRelation(`user:${i}`, 'member', `resource:${i}`, 1.0);
    }
    
    for (let i = 0; i < 200; i++) {
      arbiter.check(`user:${i % 100}`, 'member', `resource:${i % 100}`);
    }

    const runBatch = () => {
      const start = performance.now();
      for (let i = 0; i < 500; i++) {
        arbiter.check(`user:${i % 100}`, 'member', `resource:${i % 100}`);
      }
      return performance.now() - start;
    };

    const time1 = runBatch();
    const time2 = runBatch();
    const time3 = runBatch();
    const time4 = runBatch();
    const time5 = runBatch();
    
    console.log(`First batch: ${time1}ms, Second batch: ${time2}ms, Third batch: ${time3}ms, Fourth batch: ${time4}ms, Fifth batch: ${time5}ms`);
    
    if (arbiter.directCheckCache) {
      const warmTimes = [time2, time3, time4, time5].sort((a, b) => a - b);
      const medianWarm = (warmTimes[1] + warmTimes[2]) / 2;
      assert.ok(medianWarm <= time1 * 1.5, `Cached queries should be at least as fast (${medianWarm}ms <= ${time1}ms)`);
    } else {
      console.log('Direct check cache is disabled');
    }
  });

  perfTest('query performance scales linearly with graph size', () => {
    const arbiter = new Arbiter();
    const numNodes = 5000;
    
    for (let i = 0; i < numNodes; i++) {
      arbiter.addNode(`node:${i}`, 'node');
    }
    
    arbiter.setRelationConfig('connect', { type: 'direct' });
    
    for (let i = 0; i < numNodes - 1; i++) {
      arbiter.addRelation(`node:${i}`, 'connect', `node:${i + 1}`, 1.0);
    }
    
    const start = Date.now();
    const result = arbiter.check('node:0', 'connect', 'node:1');
    const duration = Date.now() - start;
    
    console.log(`Query time: ${duration}ms`);
    
    assert.strictEqual(result.possibility, 1.0);
    assert.ok(duration < 1000, `Query should be fast, took ${duration}ms`);
  });

  perfTest('batch relation addition is efficient', () => {
    const arbiter = new Arbiter();
    const numNodes = 20000;
    
    arbiter.setRelationConfig('connect', { type: 'direct' });
    
    for (let i = 0; i < numNodes; i++) {
      arbiter.addNode(`node:${i}`, 'node');
    }
    
    const start = Date.now();
    for (let i = 0; i < numNodes - 1; i++) {
      arbiter.addRelation(`node:${i}`, 'connect', `node:${i + 1}`, 1.0);
    }
    const duration = Date.now() - start;
    
    console.log(`Batch addition time: ${duration}ms for ${numNodes - 1} relations`);
    
    assert.ok(duration < 5000, `Batch addition should be fast, took ${duration}ms`);
  });
});
