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('RelationStore Read Performance', () => {
  perfTest('sequential read speed comparison', () => {
    const numRelations = 100000;
    const store = new RelationStore(numRelations);
    
    // Add relations
    for (let i = 0; i < numRelations; i++) {
      store.add(i, 'relation', (i + numRelations) % numRelations);
    }
    
    // Benchmark sequential reads
    const iterations = 10000;
    
    const start1 = Date.now();
    let sum1 = 0;
    for (let i = 0; i < iterations; i++) {
      const idx = i % store.size();
      const rel = store.get(idx);
      sum1 += rel.possibility;
    }
    const time1 = Date.now() - start1;
    
    const start2 = Date.now();
    let sum2 = 0;
    for (let i = 0; i < iterations; i++) {
      const idx = i % store.size();
      const rel = store.getRaw(idx);
      sum2 += rel.possibility;
    }
    const time2 = Date.now() - start2;
    
    console.log(`Sequential read speed (${iterations} iterations):`);
    console.log(`  - get() (full object): ${time1}ms (${(time1/iterations*1000).toFixed(4)} µs/op)`);
    console.log(`  - getRaw() (typed arrays): ${time2}ms (${(time2/iterations*1000).toFixed(4)} µs/op)`);
    console.log(`  - Speedup: ${(time1/time2).toFixed(2)}x ${time2 < time1 ? '(faster)' : '(slower)'}`);
    
    // Both should give same result
    assert.ok(Math.abs(sum1 - sum2) < 1e-6);
  });

  perfTest('random access speed', () => {
    const numRelations = 100000;
    const store = new RelationStore(numRelations);
    
    for (let i = 0; i < numRelations; i++) {
      store.add(i, 'relation', (i + numRelations) % numRelations);
    }
    
    const iterations = 10000;
    const indices = Array.from({length: iterations}, () => 
      Math.floor(Math.random() * store.size())
    );
    
    const start1 = Date.now();
    let count1 = 0;
    for (const idx of indices) {
      const rel = store.get(idx);
      if (rel.possibility > 0.5) count1++;
    }
    const time1 = Date.now() - start1;
    
    const start2 = Date.now();
    let count2 = 0;
    for (const idx of indices) {
      const rel = store.getRaw(idx);
      if (rel.possibility > 0.5) count2++;
    }
    const time2 = Date.now() - start2;
    
    console.log(`Random access speed (${iterations} iterations):`);
    console.log(`  - get() (full object): ${time1}ms (${(time1/iterations*1000).toFixed(4)} µs/op)`);
    console.log(`  - getRaw() (typed arrays): ${time2}ms (${(time2/iterations*1000).toFixed(4)} µs/op)`);
    console.log(`  - Speedup: ${(time1/time2).toFixed(2)}x ${time2 < time1 ? '(faster)' : '(slower)'}`);
    
    assert.strictEqual(count1, count2);
  });

  perfTest('findMatches performance', () => {
    const numRelations = 50000;
    const store = new RelationStore(numRelations);
    
    for (let i = 0; i < numRelations; i++) {
      store.add(i % 1000, 'rel', (i + 1) % 1000);
    }
    
    const srcId = 500;
    const relId = store.getRelationId('rel');
    
    const iterations = 1000;
    
    const start = Date.now();
    for (let i = 0; i < iterations; i++) {
      const matches = store.findMatches(srcId, relId, null);
    }
    const time = Date.now() - start;
    
    console.log(`findMatches performance (${iterations} iterations):`);
    console.log(`  - Time: ${time}ms (${(time/iterations).toFixed(4)} ms/op)`);
    console.log(`  - Matches found: ${store.findMatches(srcId, relId, null).length}`);
    console.log(`  - Avg matches/query: ${(time/iterations).toFixed(4)}ms`);
    
    assert.ok(time < iterations * 10, 'findMatches should be reasonably fast');
  });

  perfTest('forEach iteration speed', () => {
    const numRelations = 100000;
    const store = new RelationStore(numRelations);
    
    for (let i = 0; i < numRelations; i++) {
      store.add(i, 'relation', (i + numRelations) % numRelations, {
        possibility: Math.random()
      });
    }
    
    const start1 = Date.now();
    let sum1 = 0;
    store.forEach((rel) => {
      sum1 += rel.possibility;
    });
    const time1 = Date.now() - start1;
    
    const start2 = Date.now();
    let sum2 = 0;
    store.forEachRaw((rel) => {
      sum2 += rel.possibility;
    });
    const time2 = Date.now() - start2;
    
    console.log(`forEach iteration speed (${store.size()} relations):`);
    console.log(`  - forEach() (full object): ${time1}ms`);
    console.log(`  - forEachRaw() (typed arrays): ${time2}ms`);
    console.log(`  - Speedup: ${(time1/time2).toFixed(2)}x ${time2 < time1 ? '(faster)' : '(slower)'}`);
    
    assert.ok(Math.abs(sum1 - sum2) < 1e-6);
  });

  perfTest('cache-friendly access pattern', () => {
    const numRelations = 100000;
    const store = new RelationStore(numRelations);
    
    for (let i = 0; i < numRelations; i++) {
      store.add(i, 'relation', (i + numRelations) % numRelations, {
        possibility: Math.random(),
        reliability: Math.random()
      });
    }
    
    // Access all src fields first (cache-friendly)
    const start1 = Date.now();
    let totalSrc = 0;
    for (let i = 0; i < store.size(); i++) {
      totalSrc += store.src[i];
    }
    const time1 = Date.now() - start1;
    
    // Access possibility fields (different typed array)
    const start2 = Date.now();
    let totalPoss = 0;
    for (let i = 0; i < store.size(); i++) {
      totalPoss += store.possibility[i];
    }
    const time2 = Date.now() - start2;
    
    console.log(`Cache-friendly field access (${store.size()} relations):`);
    console.log(`  - src field access: ${time1}ms`);
    console.log(`  - possibility field access: ${time2}ms`);
    console.log(`  - Each operation: ${((time1+time2)/(store.size()*1000)).toFixed(4)} µs/element`);
  });

  perfTest('pattern matching queries', () => {
    const numRelations = 50000;
    const store = new RelationStore(numRelations);
    
    // Create different relation types
    const relationTypes = ['owner', 'member', 'viewer', 'editor'];
    relationTypes.forEach(rel => store.getRelationId(rel));
    
    for (let i = 0; i < numRelations; i++) {
      const relType = relationTypes[i % relationTypes.length];
      store.add(i, relType, (i + 1) % 1000, {
        possibility: Math.random()
      });
    }
    
    const queries = 1000;
    
    // Query: Find all relations from a source
    const srcId = 100;
    const start1 = Date.now();
    let count1 = 0;
    for (let i = 0; i < queries; i++) {
      const matches = store.findMatches(srcId, null, null);
      count1 += matches.length;
    }
    const time1 = Date.now() - start1;
    
    // Query: Find all relations of a specific type
    const ownerRelId = store.getRelationId('owner');
    const start2 = Date.now();
    let count2 = 0;
    for (let i = 0; i < queries; i++) {
      const matches = store.findMatches(null, ownerRelId, null);
      count2 += matches.length;
    }
    const time2 = Date.now() - start2;
    
    console.log(`Pattern matching queries (${queries} queries):`);
    console.log(`  - Find by source: ${time1}ms (${(time1/queries).toFixed(4)} ms/query)`);
    console.log(`  - Find by relation type: ${time2}ms (${(time2/queries).toFixed(4)} ms/query)`);
    console.log(`  - Results: source=${count1/queries} avg, type=${count2/queries} avg`);
    
    assert.ok(count1 > 0, 'Should find relations by source');
    assert.ok(count2 > 0, 'Should find relations by type');
  });

  perfTest('update operation speed', () => {
    const numRelations = 50000;
    const store = new RelationStore(numRelations);
    
    for (let i = 0; i < numRelations; i++) {
      store.add(i, 'relation', (i + numRelations) % numRelations, {
        possibility: 0.5,
        reliability: 0.7
      });
    }
    
    const updates = 10000;
    const start = Date.now();
    for (let i = 0; i < updates; i++) {
      const idx = i % store.size();
      store.update(idx, { possibility: 0.9 });
    }
    const time = Date.now() - start;
    
    console.log(`Update operation speed (${updates} updates):`);
    console.log(`  - Time: ${time}ms`);
    console.log(`  - Avg per update: ${(time/updates).toFixed(4)} ms`);
    console.log(`  - Updates/sec: ${(updates/time*1000).toFixed(0)}`);
    
    // Verify updates worked - check that value changed
    const checkIdx = Math.floor(store.size() / 2);
    const rel = store.get(checkIdx);
    const beforeUpdate = store.get(0);
    assert.ok(rel.possibility !== beforeUpdate.possibility || store.size() < 2, 'Update should change value');
  });

  perfTest('remove operation speed', () => {
    const numRelations = 50000;
    const store = new RelationStore(numRelations);
    
    for (let i = 0; i < numRelations; i++) {
      store.add(i, 'relation', (i + numRelations) % numRelations);
    }
    
    const initialSize = store.size();
    const removes = 10000;
    const start = Date.now();
    for (let i = 0; i < removes; i++) {
      const idx = Math.floor(Math.random() * store.size());
      store.remove(idx);
    }
    const time = Date.now() - start;
    const finalSize = store.size();
    const actualRemoves = initialSize - finalSize;
    
    console.log(`Remove operation speed (${removes} attempted):`);
    console.log(`  - Time: ${time}ms`);
    console.log(`  - Avg per remove: ${(time/removes).toFixed(4)} ms`);
    console.log(`  - Actual removes: ${actualRemoves} (deduplication)`);
    console.log(`  - Removes/sec: ${(actualRemoves/time*1000).toFixed(0)}`);
    
    assert.strictEqual(finalSize, initialSize - removes);
  });
});
