import { test, describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../../src/index.js';
import { setupChainRuleTestGraph } from './helpers.js';

describe('ChainRule Comprehensive Tests', () => {

  describe('Debugging Aggregation Issues (RelationalComparatorRule Integration)', () => {
    it('debugs the $9000 vs $4500 aggregation problem', () => {
      const arbiter = new Arbiter({ embeddingDimensions: 256 });

      // Replicate the exact scenario from RelationalComparatorRule test
      arbiter.setRelationConfig('can_debit', { type: 'direct' });
      arbiter.setRelationConfig('has_balance', { type: 'direct' });

      // Create nodes
      arbiter.addNode('user:test', 'user');
      arbiter.addNode('account:business', 'account');
      arbiter.addNode('account:personal', 'account');
      arbiter.addNode('account:restricted', 'account');
      arbiter.addNode('unit:usd', 'currency');

      // Set up account balances
      arbiter.addRelation('account:business', 'has_balance', 'unit:usd', { value: 3000 });
      arbiter.addRelation('account:personal', 'has_balance', 'unit:usd', { value: 1500 });
      arbiter.addRelation('account:restricted', 'has_balance', 'unit:usd', { value: 10000 });

      // Set up debit rights (only to business and personal)
      arbiter.addRelation('user:test', 'can_debit', 'account:business');
      arbiter.addRelation('user:test', 'can_debit', 'account:personal');

      // Configure the chain rule that's causing problems
      arbiter.setRelationConfig('balance_chain', {
        type: 'chain',
        steps: [
          { relation: 'can_debit', direction: 'out' },    // user → accounts
          { relation: 'has_balance', direction: 'out' }   // accounts → currency
        ],
        extractValues: true,
        extractFrom: 1,  // Extract from step 1 (accounts)
        extractRelation: 'has_balance',
        valueAggregation: 'sum',         // Sum all authorized balances
        evaluateFrom: 'user'
      });

      // Test the chain rule directly
      const result = arbiter.check('user:test', 'balance_chain', 'unit:usd');

      if (process.env.TEST_DEBUG === '1') console.log('\n=== CHAIN RULE AGGREGATION DEBUG ===');
      if (process.env.TEST_DEBUG === '1') console.log('Expected path: user:test → [account:business, account:personal] → unit:usd');
      if (process.env.TEST_DEBUG === '1') console.log('Expected balance sum: $3000 + $1500 = $4500');
      if (process.env.TEST_DEBUG === '1') console.log('Actual result:', JSON.stringify(result, null, 2));

      // Check if the chain rule reaches the target
      assert.equal(result.possibility, 1, 'Chain should successfully reach unit:usd');
      assert.equal(result.reason, 'allow_rule_matched');

      // The key question: what values are being collected?
      if (result.collectedValues) {
        if (process.env.TEST_DEBUG === '1') console.log('\n=== COLLECTED VALUES ANALYSIS ===');
        if (process.env.TEST_DEBUG === '1') console.log('Number of collected values:', result.collectedValues.length);
        result.collectedValues.forEach((cv, i) => {
          if (process.env.TEST_DEBUG === '1') console.log(`Value ${i + 1}:`, {
            value: cv.value,
            source: cv.source,
            metadata: cv.metadata || cv.meta
          });
        });
      }

      // Check what the RelationManager thinks about this path
      if (process.env.TEST_DEBUG === '1') console.log('\n=== RELATION MANAGER DEBUG ===');
      const userNodeId = arbiter.nodeManager.getNodeId('user:test');
      const debitRels = arbiter.relationManager.getRelationsFromSrc(userNodeId, 'can_debit');
      if (process.env.TEST_DEBUG === '1') console.log('Debit relations:', debitRels.map(r => ({
        target: arbiter.keyByNodeId.get(r.dst),
        possibility: r.possibility
      })));

      debitRels.forEach(debitRel => {
        const balanceRels = arbiter.relationManager.getRelationsFromSrc(debitRel.dst, 'has_balance');
        if (process.env.TEST_DEBUG === '1') console.log(`Balance relations from ${arbiter.keyByNodeId.get(debitRel.dst)}:`,
          balanceRels.map(r => ({
            target: arbiter.keyByNodeId.get(r.dst),
            value: r.value,
            possibility: r.possibility
          }))
        );
      });

      // This test documents the issue for investigation
      if (process.env.TEST_DEBUG === '1') console.log('\n=== ISSUE SUMMARY ===');
      if (process.env.TEST_DEBUG === '1') console.log('If chain rule aggregation is working correctly, the total should be $4500');
      if (process.env.TEST_DEBUG === '1') console.log('If we get $9000, there is likely double counting in the aggregation logic');
    });

    it('tests simpler two-account aggregation to isolate the issue', () => {
      const arbiter = new Arbiter({ embeddingDimensions: 256 });

      // Simpler test case
      arbiter.setRelationConfig('owns', { type: 'direct' });
      arbiter.setRelationConfig('worth', { type: 'direct' });

      arbiter.addNode('user:simple', 'user');
      arbiter.addNode('asset:a', 'asset');
      arbiter.addNode('asset:b', 'asset');
      arbiter.addNode('currency:usd', 'currency');

      // User owns two assets
      arbiter.addRelation('user:simple', 'owns', 'asset:a');
      arbiter.addRelation('user:simple', 'owns', 'asset:b');

      // Assets have known values
      arbiter.addRelation('asset:a', 'worth', 'currency:usd', { value: 100 });
      arbiter.addRelation('asset:b', 'worth', 'currency:usd', { value: 200 });

      arbiter.setRelationConfig('total_worth_chain', {
        type: 'chain',
        steps: [
          { relation: 'owns', direction: 'out' },
          { relation: 'worth', direction: 'out' }
        ],
        extractValues: true,
        extractFrom: 1,  // Extract from step 1 (assets)
        extractRelation: 'worth',
        valueAggregation: 'sum'
      });

      const result = arbiter.check('user:simple', 'total_worth_chain', 'currency:usd');

      if (process.env.TEST_DEBUG === '1') console.log('\n=== SIMPLE AGGREGATION TEST ===');
      if (process.env.TEST_DEBUG === '1') console.log('Expected: $100 + $200 = $300');
      if (process.env.TEST_DEBUG === '1') console.log('Result:', JSON.stringify(result, null, 2));

      assert.equal(result.possibility, 1);

      // This should help us see if the issue is specific to the can_debit scenario
      // or a general problem with chain rule aggregation
      if (result.collectedValues) {
        const totalValue = result.collectedValues.reduce((sum, cv) => {
          const val = typeof cv.value === 'number' ? cv.value :
                     (cv.value && typeof cv.value.value === 'number' ? cv.value.value : 0);
          return sum + val;
        }, 0);
        if (process.env.TEST_DEBUG === '1') console.log('Manually calculated total from collected values:', totalValue);
        if (process.env.TEST_DEBUG === '1') console.log('Expected total: 300');

        if (totalValue !== 300) {
          if (process.env.TEST_DEBUG === '1') console.log('🚨 AGGREGATION ISSUE CONFIRMED: Chain rule is not summing correctly');
        } else {
          if (process.env.TEST_DEBUG === '1') console.log('✅ Simple aggregation works correctly');
        }
      }
    });

    it('tests chain rule value extraction vs RelationalComparatorRule value extraction', () => {
      const arbiter = new Arbiter({ embeddingDimensions: 256 });

      // Set up the same scenario but test chain rule in isolation
      arbiter.setRelationConfig('can_debit', { type: 'direct' });
      arbiter.setRelationConfig('has_balance', { type: 'direct' });

      arbiter.addNode('user:compare', 'user');
      arbiter.addNode('account:x', 'account');
      arbiter.addNode('account:y', 'account');
      arbiter.addNode('currency:usd', 'currency');

      arbiter.addRelation('user:compare', 'can_debit', 'account:x');
      arbiter.addRelation('user:compare', 'can_debit', 'account:y');
      arbiter.addRelation('account:x', 'has_balance', 'currency:usd', { value: 1000 });
      arbiter.addRelation('account:y', 'has_balance', 'currency:usd', { value: 2000 });

      // Test 1: Direct ChainRule evaluation
      arbiter.setRelationConfig('direct_chain', {
        type: 'chain',
        steps: [
          { relation: 'can_debit', direction: 'out' },
          { relation: 'has_balance', direction: 'out' }
        ],
        extractValues: true,
        extractFrom: 1,
        extractRelation: 'has_balance',
        valueAggregation: 'sum'
      });

      const chainResult = arbiter.check('user:compare', 'direct_chain', 'currency:usd');

      // Test 2: RelationalComparatorRule using the same chain
      arbiter.setRelationConfig('comparator_chain', {
        type: 'relational_comparator',
        leftOperand: {
          rule: {
            type: 'chain',
            steps: [
              { relation: 'can_debit', direction: 'out' },
              { relation: 'has_balance', direction: 'out' }
            ],
            extractValues: true,
            extractFrom: 1,
            extractRelation: 'has_balance',
            valueAggregation: 'sum',
            evaluateFrom: 'user'
          },
          extractValue: true,
          evaluateFrom: 'user'
        },
        rightOperand: {
          rule: {
            type: 'direct',
            relation: 'has_balance'  // dummy
          },
          extractValue: false  // Use possibility (1.0) as value
        },
        comparator: '>='
      });

      const comparatorResult = arbiter.check('user:compare', 'comparator_chain', 'currency:usd');

      if (process.env.TEST_DEBUG === '1') console.log('\n=== CHAIN VS RELATIONAL_COMPARATOR COMPARISON ===');
      if (process.env.TEST_DEBUG === '1') console.log('Expected total: $1000 + $2000 = $3000');
      if (process.env.TEST_DEBUG === '1') console.log('\nDirect chain result:', {
        possibility: chainResult.possibility,
        collectedValues: chainResult.collectedValues?.length || 'none',
        reason: chainResult.reason
      });

      if (process.env.TEST_DEBUG === '1') console.log('\nRelationalComparatorRule result:', {
        possibility: comparatorResult.possibility,
        leftValue: comparatorResult.meta?.allow?.leftValue || comparatorResult.meta?.deny?.leftValue,
        rightValue: comparatorResult.meta?.allow?.rightValue || comparatorResult.meta?.deny?.rightValue,
        reason: comparatorResult.reason
      });

      // Both should reach the target
      assert.equal(chainResult.possibility, 1);

      if (comparatorResult.meta?.allow?.leftValue) {
        if (process.env.TEST_DEBUG === '1') console.log('\n🔍 Investigating value extraction:');
        if (process.env.TEST_DEBUG === '1') console.log('RelationalComparatorRule extracted leftValue:', comparatorResult.meta.allow.leftValue);
        if (process.env.TEST_DEBUG === '1') console.log('Expected: 3000');

        if (comparatorResult.meta.allow.leftValue === 6000) {
          if (process.env.TEST_DEBUG === '1') console.log('🚨 DOUBLE COUNTING CONFIRMED: 2x the expected value');
        } else if (comparatorResult.meta.allow.leftValue === 3000) {
          if (process.env.TEST_DEBUG === '1') console.log('✅ Correct aggregation');
        } else {
          if (process.env.TEST_DEBUG === '1') console.log('🤔 Unexpected value - needs investigation');
        }
      }
    });

    it('isolates collectedValues disappearing issue', () => {
      const arbiter = new Arbiter({ embeddingDimensions: 256 });

      // Minimal test case
      arbiter.setRelationConfig('owns', { type: 'direct' });
      arbiter.setRelationConfig('worth', { type: 'direct' });

      arbiter.addNode('user:minimal', 'user');
      arbiter.addNode('item:test', 'item');
      arbiter.addNode('currency:usd', 'currency');

      arbiter.addRelation('user:minimal', 'owns', 'item:test');
      arbiter.addRelation('item:test', 'worth', 'currency:usd', { value: 100 });

      arbiter.setRelationConfig('minimal_chain', {
        type: 'chain',
        steps: [
          { relation: 'owns', direction: 'out' },
          { relation: 'worth', direction: 'out' }
        ],
        extractValues: true,
        extractFrom: 1,
        extractRelation: 'worth',
        valueAggregation: 'sum'
      });

      const result = arbiter.check('user:minimal', 'minimal_chain', 'currency:usd');

      if (process.env.TEST_DEBUG === '1') console.log('\n=== MINIMAL TEST: COLLECTED VALUES INVESTIGATION ===');
      if (process.env.TEST_DEBUG === '1') console.log('Full result structure:', JSON.stringify(result, null, 2));
      if (process.env.TEST_DEBUG === '1') console.log('Has collectedValues field?', 'collectedValues' in result);
      if (process.env.TEST_DEBUG === '1') console.log('collectedValues type:', typeof result.collectedValues);
      if (process.env.TEST_DEBUG === '1') console.log('collectedValues value:', result.collectedValues);

      if (result.collectedValues && Array.isArray(result.collectedValues)) {
        if (process.env.TEST_DEBUG === '1') console.log('✅ collectedValues preserved:', result.collectedValues.length, 'values');
        result.collectedValues.forEach((cv, i) => {
          if (process.env.TEST_DEBUG === '1') console.log(`  Value ${i + 1}:`, {
            value: cv.value,
            possibility: cv.possibility,
            source: cv.source?.relation
          });
        });
      } else {
        if (process.env.TEST_DEBUG === '1') console.log('🚨 collectedValues NOT preserved in final result');
        if (process.env.TEST_DEBUG === '1') console.log('This explains why RelationalComparatorRule cannot extract values from ChainRule');
      }

      assert.equal(result.possibility, 1, 'Chain should reach target');
    });

    it('isolates why RelationalComparatorRule gets $9000 instead of $4500', () => {
      const arbiter = new Arbiter({ embeddingDimensions: 256 });

      // Set up the same scenario but test chain rule in isolation
      arbiter.setRelationConfig('can_debit', { type: 'direct' });
      arbiter.setRelationConfig('has_balance', { type: 'direct' });

      arbiter.addNode('user:compare', 'user');
      arbiter.addNode('account:x', 'account');
      arbiter.addNode('account:y', 'account');
      arbiter.addNode('currency:usd', 'currency');

      arbiter.addRelation('user:compare', 'can_debit', 'account:x');
      arbiter.addRelation('user:compare', 'can_debit', 'account:y');
      arbiter.addRelation('account:x', 'has_balance', 'currency:usd', { value: 1000 });
      arbiter.addRelation('account:y', 'has_balance', 'currency:usd', { value: 2000 });

      // Test 1: Direct ChainRule evaluation
      arbiter.setRelationConfig('direct_chain', {
        type: 'chain',
        steps: [
          { relation: 'can_debit', direction: 'out' },
          { relation: 'has_balance', direction: 'out' }
        ],
        extractValues: true,
        extractFrom: 1,
        extractRelation: 'has_balance',
        valueAggregation: 'sum'
      });

      const chainResult = arbiter.check('user:compare', 'direct_chain', 'currency:usd');

      // Test 2: RelationalComparatorRule using the same chain
      arbiter.setRelationConfig('comparator_chain', {
        type: 'relational_comparator',
        leftOperand: {
          rule: {
            type: 'chain',
            steps: [
              { relation: 'can_debit', direction: 'out' },
              { relation: 'has_balance', direction: 'out' }
            ],
            extractValues: true,
            extractFrom: 1,
            extractRelation: 'has_balance',
            valueAggregation: 'sum',
            evaluateFrom: 'user'
          },
          extractValue: true,
          evaluateFrom: 'user'
        },
        rightOperand: {
          rule: {
            type: 'direct',
            relation: 'has_balance'  // dummy
          },
          extractValue: false  // Use possibility (1.0) as value
        },
        comparator: '>='
      });

      const comparatorResult = arbiter.check('user:compare', 'comparator_chain', 'currency:usd');

      if (process.env.TEST_DEBUG === '1') console.log('\n=== CHAIN VS RELATIONAL_COMPARATOR COMPARISON ===');
      if (process.env.TEST_DEBUG === '1') console.log('Expected total: $1000 + $2000 = $3000');
      if (process.env.TEST_DEBUG === '1') console.log('\nDirect chain result:', {
        possibility: chainResult.possibility,
        collectedValues: chainResult.collectedValues?.length || 'none',
        reason: chainResult.reason
      });

      if (process.env.TEST_DEBUG === '1') console.log('\nRelationalComparatorRule result:', {
        possibility: comparatorResult.possibility,
        leftValue: comparatorResult.meta?.allow?.leftValue || comparatorResult.meta?.deny?.leftValue,
        rightValue: comparatorResult.meta?.allow?.rightValue || comparatorResult.meta?.deny?.rightValue,
        reason: comparatorResult.reason
      });

      // Both should reach the target
      assert.equal(chainResult.possibility, 1);

      if (comparatorResult.meta?.allow?.leftValue) {
        if (process.env.TEST_DEBUG === '1') console.log('\n🔍 Investigating value extraction:');
        if (process.env.TEST_DEBUG === '1') console.log('RelationalComparatorRule extracted leftValue:', comparatorResult.meta.allow.leftValue);
        if (process.env.TEST_DEBUG === '1') console.log('Expected: 3000');

        if (comparatorResult.meta.allow.leftValue === 6000) {
          if (process.env.TEST_DEBUG === '1') console.log('🚨 DOUBLE COUNTING CONFIRMED: 2x the expected value');
        } else if (comparatorResult.meta.allow.leftValue === 3000) {
          if (process.env.TEST_DEBUG === '1') console.log('✅ Correct aggregation');
        } else {
          if (process.env.TEST_DEBUG === '1') console.log('🤔 Unexpected value - needs investigation');
        }
      }
    });

    it('regression test: ensures collectedValues are preserved after AuthorizationChecker fix', () => {
      const arbiter = new Arbiter({ embeddingDimensions: 256 });

      // Set up a simple value collection scenario
      arbiter.setRelationConfig('owns', { type: 'direct' });
      arbiter.setRelationConfig('worth', { type: 'direct' });

      arbiter.addNode('user:collector', 'user');
      arbiter.addNode('asset:a', 'asset');
      arbiter.addNode('asset:b', 'asset');
      arbiter.addNode('currency:usd', 'currency');

      // Set up asset ownership and values
      arbiter.addRelation('user:collector', 'owns', 'asset:a');
      arbiter.addRelation('user:collector', 'owns', 'asset:b');
      arbiter.addRelation('asset:a', 'worth', 'currency:usd', { value: 500 });
      arbiter.addRelation('asset:b', 'worth', 'currency:usd', { value: 750 });

      // Configure chain rule with value collection
      arbiter.setRelationConfig('asset_values', {
        type: 'chain',
        steps: [
          { relation: 'owns', direction: 'out' },
          { relation: 'worth', direction: 'out' }
        ],
        extractValues: true,
        extractFrom: 1,
        extractRelation: 'worth',
        valueAggregation: 'sum'
      });

      const result = arbiter.check('user:collector', 'asset_values', 'currency:usd', { collectValues: true });

      if (process.env.TEST_DEBUG === '1') console.log('\n=== REGRESSION TEST: COLLECTED VALUES PRESERVATION ===');
      if (process.env.TEST_DEBUG === '1') console.log('Testing that collectedValues are preserved in final result...');
      if (process.env.TEST_DEBUG === '1') console.log('Result structure:', {
        possibility: result.possibility,
        hasCollectedValues: 'collectedValues' in result,
        collectedValuesType: typeof result.collectedValues,
        collectedValuesLength: result.collectedValues?.length || 'N/A',
        reason: result.reason
      });

      // CRITICAL REGRESSION TEST: collectedValues must be preserved
      assert.equal(result.possibility, 1, 'Chain should reach target successfully');
      assert.ok('collectedValues' in result, '🚨 REGRESSION: collectedValues field missing from result');
      assert.ok(Array.isArray(result.collectedValues), '🚨 REGRESSION: collectedValues is not an array');
      assert.ok(result.collectedValues.length > 0, '🚨 REGRESSION: collectedValues array is empty');

      // Verify the collected values have the expected structure
      const firstValue = result.collectedValues[0];
      assert.ok(firstValue.value !== undefined, 'Collected value should have a value field');
      assert.ok(firstValue.possibility !== undefined, 'Collected value should have a possibility field');

      if (process.env.TEST_DEBUG === '1') console.log('✅ REGRESSION TEST PASSED: collectedValues properly preserved');
      if (process.env.TEST_DEBUG === '1') console.log('   collectedValues count:', result.collectedValues.length);
      if (process.env.TEST_DEBUG === '1') console.log('   First collected value:', {
        value: firstValue.value,
        possibility: firstValue.possibility,
        source: firstValue.source
      });

      // Also verify it's in meta for backwards compatibility
      if (result.meta?.collectedValues) {
        if (process.env.TEST_DEBUG === '1') console.log('✅ BONUS: collectedValues also available in meta');
      }
    });
  });
});
