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

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

describe.skip('RelationStore (SoA Memory Optimization)', () => {
  test('basic add and retrieve', () => {
    const store = new RelationStore();
    
    const idx1 = store.add(1, 'owner', 2, { possibility: 1.0 });
    const idx2 = store.add(1, 'member', 3, { possibility: 0.5 });
    
    assert.strictEqual(store.size(), 2);
    
    const rel1 = store.get(idx1);
    assert.strictEqual(rel1.rel, 'owner');
    assert.ok(Math.abs(rel1.possibility - 1.0) < 1e-6);
    
    const rel2 = store.get(idx2);
    assert.strictEqual(rel2.rel, 'member');
    assert.ok(Math.abs(rel2.possibility - 0.5) < 1e-6);
  });

  test('update relation', () => {
    const store = new RelationStore();
    
    const idx = store.add(1, 'owner', 2, { possibility: 0.5 });
    assert.strictEqual(store.possibility[idx], 0.5);
    
    store.update(idx, { possibility: 0.9 });
    assert.ok(Math.abs(store.possibility[idx] - 0.9) < 1e-6, `possibility should be ~0.9, got ${store.possibility[idx]}`);
    
    const rel = store.get(idx);
    assert.ok(Math.abs(rel.possibility - 0.9) < 1e-6);
  });

  test('remove relation', () => {
    const store = new RelationStore();
    
    const idx1 = store.add(1, 'owner', 2);
    const idx2 = store.add(1, 'member', 3);
    const idx3 = store.add(2, 'owner', 4);
    
    assert.strictEqual(store.size(), 3);
    
    store.remove(idx2);
    assert.strictEqual(store.size(), 2);
    
    const rel1 = store.get(idx1);
    assert.strictEqual(rel1.rel, 'owner');
    
    // After removal, idx2's data was moved from idx3
    const rel3 = store.get(idx3);
    assert.strictEqual(rel3, null);
    
    const remaining = store.findMatches(null, null, null);
    assert.strictEqual(remaining.length, 2);
  });

  test('find matches', () => {
    const store = new RelationStore();
    
    store.add(1, 'owner', 2);
    store.add(1, 'member', 3);
    store.add(1, 'owner', 4);
    store.add(2, 'owner', 3);
    
    const ownerFrom1 = store.findMatches(1, store.getRelationId('owner'), null);
    assert.strictEqual(ownerFrom1.length, 2);
    
    const allOwner = store.findMatches(null, store.getRelationId('owner'), null);
    assert.strictEqual(allOwner.length, 3);
  });

  test('raw access for performance', () => {
    const store = new RelationStore();
    
    const idx = store.add(1, 'owner', 2, { possibility: 0.75 });
    
    const raw = store.getRaw(idx);
    assert.ok(raw !== null, 'raw should not be null');
    assert.strictEqual(raw.src, 1);
    const relId = store.getRelationId('owner');
    assert.strictEqual(raw.relId, relId);
    assert.strictEqual(raw.dst, 2);
    assert.ok(Math.abs(raw.possibility - 0.75) < 1e-6);
    assert.strictEqual(raw.reliability, 1.0);
    assert.strictEqual('value' in raw, true);
  });

  test('metadata storage', () => {
    const store = new RelationStore();
    
    const customValue = { tier: 'premium', level: 5 };
    const decay = { rate: 0.1, interval: 3600000 };
    
    const idx = store.add(1, 'owner', 2, {
      possibility: 1.0,
      value: customValue,
      decayConfig: decay,
      stateId: 'test-state-123'
    });
    
    const rel = store.get(idx);
    assert.deepStrictEqual(rel.value, customValue);
    assert.deepStrictEqual(rel.decayConfig, decay);
    assert.strictEqual(rel.stateId, 'test-state-123');
  });

  test('capacity expansion', () => {
    const store = new RelationStore(4);
    
    assert.strictEqual(store.capacity(), 4);
    
    for (let i = 0; i < 10; i++) {
      store.add(i, 'rel', i + 1);
    }
    
    assert.strictEqual(store.size(), 10);
    assert.ok(store.capacity() >= 10);
  });

  test('forEach iteration', () => {
    const store = new RelationStore();
    
    store.add(1, 'owner', 2);
    store.add(1, 'member', 3);
    store.add(2, 'owner', 4);
    
    let count = 0;
    store.forEach((rel) => {
      count++;
      assert.ok(rel.src >= 1);
      assert.ok(rel.dst >= 2);
    });
    
    assert.strictEqual(count, 3);
  });

  perfTest('memory efficiency compared to AoS', () => {
    const numRelations = 100000;
    const store = new RelationStore(numRelations);
    
    const startMem = process.memoryUsage().heapUsed;
    
    for (let i = 0; i < numRelations; i++) {
      store.add(i % 1000, 'relation', (i + 1) % 1000, {
        possibility: Math.random(),
        reliability: Math.random()
      });
    }
    
    const endMem = process.memoryUsage().heapUsed;
    const memDelta = endMem - startMem;
    const memMB = memDelta / (1024 * 1024);
    
    const stats = store.getStats();
    console.log(`RelationStore memory for ${numRelations} relations:`);
    console.log(`  - Typed arrays: ${(stats.memoryUsage.typedArrays / 1024 / 1024).toFixed(2)} MB`);
    console.log(`  - Metadata: ${(stats.memoryUsage.metadata / 1024 / 1024).toFixed(2)} MB`);
    console.log(`  - Total heap delta: ${memMB.toFixed(2)} MB`);
    console.log(`  - Bytes per relation: ${(memDelta / numRelations).toFixed(2)}`);
    console.log(`  - Capacity: ${stats.capacity}, Utilization: ${(stats.size / stats.capacity).toFixed(2)}`);
    
    assert.strictEqual(store.size(), numRelations);
    assert.ok(memMB < 50, `Memory usage should be efficient (< 50MB), got ${memMB.toFixed(2)} MB`);
  });

  perfTest('comparison with traditional object array', () => {
    const numRelations = 50000;
    
    const start1 = process.memoryUsage().heapUsed;
    
    const traditional = [];
    for (let i = 0; i < numRelations; i++) {
      traditional.push({
        src: i % 1000,
        rel: 'relation',
        dst: (i + 1) % 1000,
        possibility: Math.random(),
        reliability: Math.random(),
        updated_last_at: Date.now(),
        changed_last_at: Date.now(),
        stateId: 'state-' + i
      });
    }
    
    const end1 = process.memoryUsage().heapUsed;
    const traditionalMem = end1 - start1;
    
    const start2 = process.memoryUsage().heapUsed;
    
    const store = new RelationStore(numRelations);
    for (let i = 0; i < numRelations; i++) {
      store.add(i % 1000, 'relation', (i + 1) % 1000, {
        possibility: Math.random(),
        reliability: Math.random()
      });
    }
    
    const end2 = process.memoryUsage().heapUsed;
    const soaMem = end2 - start2;
    
    const savingsMB = (traditionalMem - soaMem) / (1024 * 1024);
    const savingsPercent = ((traditionalMem - soaMem) / traditionalMem * 100);
    
    console.log(`Memory comparison for ${numRelations} relations:`);
    console.log(`  - Traditional AoS: ${(traditionalMem / 1024 / 1024).toFixed(2)} MB`);
    console.log(`  - SoA RelationStore: ${(soaMem / 1024 / 1024).toFixed(2)} MB`);
    console.log(`  - Savings: ${savingsMB.toFixed(2)} MB (${savingsPercent.toFixed(1)}%)`);
    console.log(`  - Bytes per relation (AoS): ${(traditionalMem / numRelations).toFixed(2)}`);
    console.log(`  - Bytes per relation (SoA): ${(soaMem / numRelations).toFixed(2)}`);
    
    assert.ok(soaMem <= traditionalMem, 'SoA should use less memory than AoS');
  });
});
