import { test, describe, it, beforeEach } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../../src/index.js';
import { ChainRule } from '../../../src/authorization/rules/ChainRule.js';
import { ValueContext } from '../../../src/authorization/ValueContext.js';
Arbiter.DEBUG = false;

describe('ChainRule', () => {
  let arbiter;
  let chainRule;

  beforeEach(() => {
    // Use actual Arbiter for realistic testing
    arbiter = new Arbiter({ embeddingDimensions: 256 });

    // Set up nodes
    arbiter.addNode('user1', 'user');
    arbiter.addNode('user2', 'user');  // Add user2 for the "no path" test
    arbiter.addNode('acctA', 'account');
    arbiter.addNode('acctB', 'account');
    arbiter.addNode('usd', 'currency');

    // Set up relations
    arbiter.setRelationConfig('can_debit', { type: 'direct' });
    arbiter.setRelationConfig('has_balance', { type: 'direct' });

    // Add relations with specific possibility values
    arbiter.addRelation('user1', 'can_debit', 'acctA', { possibility: 0.9 });
    arbiter.addRelation('acctA', 'has_balance', 'usd', { possibility: 0.8, value: 100 });

    chainRule = new ChainRule(arbiter);
  });

  it('returns correct possibility and collects point value for direct chain', () => {
    const rule = {
      type: 'chain',
      steps: [
        { relation: 'can_debit', direction: 'out' },
        { relation: 'has_balance', direction: 'out' }
      ]
    };
    const valueContext = new ValueContext(arbiter);
    const res = chainRule._evaluateRule('user1', 'user1', 'usd', 'usd', rule, {}, null, { valueContext, collectValues: true });
    assert.strictEqual(res.possibility, 0.8); // min(0.9, 0.8)
    assert.ok(Array.isArray(res.collectedValues));
    assert.strictEqual(res.collectedValues.length, 1);
    assert.strictEqual(res.collectedValues[0].value.min, 100);
    assert.strictEqual(res.collectedValues[0].value.max, 100);
  });

  it('returns 0 possibility if no path exists', () => {
    const rule = {
      type: 'chain',
      steps: [
        { relation: 'can_debit', direction: 'out' },
        { relation: 'has_balance', direction: 'out' }
      ]
    };
    // No relation for user2 - use numeric ID to avoid string key issues
    const user2Id = arbiter.nodeIdByKey.get('user2');
    const usdId = arbiter.nodeIdByKey.get('usd');
    const res = chainRule._evaluateRule(user2Id, user2Id, usdId, usdId, rule, {}, null, {});
    assert.strictEqual(res.possibility, 0);
    assert.ok(Array.isArray(res.collectedValues));
    assert.strictEqual(res.collectedValues.length, 0);
  });

  it('applies minPossibility threshold', () => {
    const rule = {
      type: 'chain',
      steps: [
        { relation: 'can_debit', direction: 'out' },
        { relation: 'has_balance', direction: 'out' }
      ]
    };
    // Remove the original relation and add one with lower possibility
    arbiter.removeRelation('acctA', 'has_balance', 'usd');
    arbiter.addRelation('acctA', 'has_balance', 'usd', { possibility: 0.5, value: 100 });
    const res = chainRule._evaluateRule('user1', 'user1', 'usd', 'usd', rule, {}, null, { minPossibility: 0.8, fastPath: true });
    assert.strictEqual(res.possibility, 0);
    assert.ok(Array.isArray(res.collectedValues));
    assert.strictEqual(res.collectedValues.length, 0);
  });

  it('aggregates multiple values using interval fusion', () => {
    const rule = {
      type: 'chain',
      steps: [
        { relation: 'can_debit', direction: 'out' },
        { relation: 'has_balance', direction: 'out' }
      ]
    };
    // Add second account for user1
    arbiter.addRelation('user1', 'can_debit', 'acctB', { possibility: 0.8 });
    arbiter.addRelation('acctB', 'has_balance', 'usd', { possibility: 0.7, value: 200 });

    const valueContext = new ValueContext(arbiter);
    const res = chainRule._evaluateRule('user1', 'user1', 'usd', 'usd', rule, {}, null, { valueContext, collectValues: true });
    if (process.env.TEST_DEBUG === '1') console.log('Collected values:', res.collectedValues);
    assert.strictEqual(res.possibility, 0.8); // max(min(0.9,0.8), min(0.8,0.7))
    assert.ok(Array.isArray(res.collectedValues));
    assert.strictEqual(res.collectedValues.length, 2);
    const intervals = res.collectedValues.map((cv) => cv.value).sort((a, b) => a.min - b.min);
    assert.deepStrictEqual(intervals[0], { min: 100, max: 100 });
    assert.deepStrictEqual(intervals[1], { min: 200, max: 200 });
  });

  it('filters out values outside TTL', () => {
    const rule = {
      type: 'chain',
      steps: [
        { relation: 'can_debit', direction: 'out' },
        { relation: 'has_balance', direction: 'out' }
      ]
    };
    // Remove the original relation and add one with old timestamp
    arbiter.removeRelation('acctA', 'has_balance', 'usd');
    arbiter.addRelation('acctA', 'has_balance', 'usd', {
      possibility: 0.8,
      value: 100,
      changed_last_at: Date.now() - 2 * 24 * 60 * 60 * 1000
    });
    // Configure TTL for old values - values expire after 1 day
    arbiter.valueManager.setTTL('has_balance', 24 * 60 * 60 * 1000); // 1 day TTL

    // Test with old relation (2 days old) - should be filtered out by TTL expiration
    const res = chainRule._evaluateRule('user1', 'user1', 'usd', 'usd', rule, {}, null, {});
    assert.strictEqual(res.possibility, 0.8); // Authorization still works - path exists
    assert.ok(Array.isArray(res.collectedValues));
    // Old values should be filtered out due to TTL expiration
    assert.strictEqual(res.collectedValues.length, 0);
  });
});
