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

describe('Defeasible Logic - Advanced Authorization Patterns', () => {
  let arbiter;
  let ruleEvaluator;

  beforeEach(() => {
    arbiter = new Arbiter({ embeddingDimensions: 256 });
    ruleEvaluator = new RuleEvaluator(arbiter);
    
    setupDefeasibleEntities();
  });

  function setupDefeasibleEntities() {
    // Users with different roles and permissions
    ['alice', 'bob', 'charlie', 'diana', 'eve'].forEach(name => {
      arbiter.addNode(`user:${name}`, 'user');
    });
    
    // Resources and documents
    ['doc:public', 'doc:internal', 'doc:confidential', 'doc:secret'].forEach(doc => {
      arbiter.addNode(doc, 'document');
    });
    
    // Projects and teams
    ['project:alpha', 'project:beta', 'project:classified'].forEach(project => {
      arbiter.addNode(project, 'project');
    });
    ['team:engineering'].forEach(team => {
      arbiter.addNode(team, 'team');
    });
    
    // Security levels and clearance
    ['clearance:public', 'clearance:internal', 'clearance:confidential', 'clearance:secret'].forEach(clearance => {
      arbiter.addNode(clearance, 'clearance');
    });
    
    // Set up basic relations
    arbiter.setRelationConfig('member_of', { type: 'direct' });
    arbiter.setRelationConfig('has_clearance', { type: 'direct' });
    arbiter.setRelationConfig('can_access', { type: 'direct' });
    arbiter.setRelationConfig('is_blocked', { type: 'direct' });
    arbiter.setRelationConfig('is_emergency', { type: 'direct' });
    arbiter.setRelationConfig('requires_approval', { type: 'direct' });
    arbiter.setRelationConfig('is_weekend', { type: 'direct' });
    arbiter.setRelationConfig('is_business_hours', { type: 'direct' });
  }

  describe('Basic Defeasible Logic Patterns', () => {
    it('handles unless (defeater) logic - access denied when blocked', () => {
      // Set up user access
      arbiter.addRelation('user:alice', 'can_access', 'doc:internal', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Set up defeater - Alice is blocked
      arbiter.addRelation('user:alice', 'is_blocked', 'doc:internal', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure defeasible rule: can access UNLESS blocked
      arbiter.setRelationConfig('can_access_unless_blocked', {
        when: {
          intersection: [
            { type: 'direct', relation: 'can_access' }
          ]
        },
        unless: {
          union: [
            { type: 'direct', relation: 'is_blocked' }
          ]
        }
      });
      
      const result = arbiter.authChecker.check('user:alice', 'can_access_unless_blocked', 'doc:internal');
      
      // Should be denied due to defeater
      assert.strictEqual(result.possibility, 0, 'Expected denial due to defeater');
    });

    it('handles always (strict) logic - emergency access always allowed', () => {
      // Set up emergency access
      arbiter.addRelation('user:alice', 'is_emergency', 'doc:secret', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure strict rule: emergency access ALWAYS allowed
      arbiter.setRelationConfig('emergency_access', {
        always: { type: 'direct', relation: 'is_emergency' }
      });
      
      const result = arbiter.authChecker.check('user:alice', 'emergency_access', 'doc:secret');
      
      // Should be allowed due to strict rule
      assert.ok(result.possibility > 0.8, `Expected high possibility for emergency access, got ${result.possibility}`);
    });

    it('handles when (defeasible) logic - conditional access', () => {
      // Set up conditional access
      arbiter.addRelation('user:bob', 'member_of', 'team:engineering', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('team:engineering', 'can_access', 'project:alpha', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure defeasible rule: can access WHEN team member
      arbiter.setRelationConfig('team_access', {
        when: {
          intersection: [
            {
              type: 'chain',
              steps: [
                { relation: 'member_of', direction: 'out' },
                { relation: 'can_access', direction: 'out' }
              ]
            }
          ]
        }
      });
      
      const result = arbiter.authChecker.check('user:bob', 'team_access', 'project:alpha');
      
      // Should be allowed due to defeasible rule
      assert.ok(result.possibility > 0.7, `Expected high possibility for team access, got ${result.possibility}`);
    });

    it('handles requires (inverse defeater) logic - access requires approval', () => {
      // Set up user access
      arbiter.addRelation('user:charlie', 'can_access', 'doc:confidential', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Set up requirement - Charlie has approval
      arbiter.addRelation('user:charlie', 'requires_approval', 'doc:confidential', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure inverse defeater rule: can access REQUIRES approval
      arbiter.setRelationConfig('approved_access', {
        when: {
          intersection: [
            { type: 'direct', relation: 'can_access' }
          ]
        },
        requires: {
          union: [
            { type: 'direct', relation: 'requires_approval' }
          ]
        }
      });
      
      const result = arbiter.authChecker.check('user:charlie', 'approved_access', 'doc:confidential');
      
      // Should be allowed due to requirement being met
      assert.ok(result.possibility > 0.7, `Expected high possibility for approved access, got ${result.possibility}`);
    });
  });

  describe('Complex Defeasible Logic Combinations', () => {
    it('handles multiple defeaters with priority', () => {
      // Set up user with multiple potential defeaters
      arbiter.addRelation('user:diana', 'can_access', 'doc:secret', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:diana', 'is_blocked', 'doc:secret', { 
        value: 0.8, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:diana', 'is_weekend', 'doc:secret', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure complex defeasible rule with multiple defeaters
      arbiter.setRelationConfig('complex_access', {
        when: {
          intersection: [
            { type: 'direct', relation: 'can_access' }
          ]
        },
        unless: {
          union: [
            { type: 'direct', relation: 'is_blocked' },
            { type: 'direct', relation: 'is_weekend' }
          ],
          aggregator: 'max' // Either defeater can block access
        }
      });
      
      const result = arbiter.authChecker.check('user:diana', 'complex_access', 'doc:secret');
      
      // Should be denied due to weekend defeater
      assert.strictEqual(result.possibility, 0, 'Expected denial due to weekend defeater');
    });

    it('handles strict rules overriding defeaters', () => {
      // Set up user with both defeater and strict rule
      arbiter.addRelation('user:eve', 'is_blocked', 'doc:secret', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:eve', 'is_emergency', 'doc:secret', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure rule with both defeater and strict rule
      arbiter.setRelationConfig('emergency_override', {
        always: { type: 'direct', relation: 'is_emergency' },
        unless: {
          union: [
            { type: 'direct', relation: 'is_blocked' }
          ]
        }
      });
      
      const result = arbiter.authChecker.check('user:eve', 'emergency_override', 'doc:secret');
      
      // Should be allowed due to strict rule overriding defeater
      assert.strictEqual(result.possibility, 0, `Expected defeater to override strict rule, got ${result.possibility}`);
    });

    it('handles defeasible rules with requirements', () => {
      // Set up user with access, clearance, and approval
      arbiter.addRelation('user:alice', 'can_access', 'doc:confidential', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:alice', 'has_clearance', 'doc:confidential', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:alice', 'requires_approval', 'doc:confidential', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure complex rule: access WHEN cleared AND approved, UNLESS blocked
      arbiter.setRelationConfig('secure_access', {
        when: {
          intersection: [
            { type: 'direct', relation: 'can_access' },
            { type: 'direct', relation: 'has_clearance' }
          ]
        },
        requires: {
          union: [
            { type: 'direct', relation: 'requires_approval' }
          ]
        },
        unless: {
          union: [
            { type: 'direct', relation: 'is_blocked' }
          ]
        }
      });
      
      const result = arbiter.authChecker.check('user:alice', 'secure_access', 'doc:confidential');
      
      // Should be allowed due to all conditions being met
      assert.ok(result.possibility > 0.7, `Expected high possibility for secure access, got ${result.possibility}`);
    });
  });

  describe('Defeasible Logic with OWA Aggregation', () => {
    it('handles majority-based defeasible logic', () => {
      // Set up multiple approval sources
      arbiter.addRelation('user:bob', 'manager_approval', 'project:alpha', { 
        value: 0.8, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:bob', 'peer_approval', 'project:alpha', { 
        value: 0.9, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:bob', 'system_approval', 'project:alpha', { 
        value: 0.7, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure majority-based defeasible rule
      arbiter.setRelationConfig('consensus_access', {
        when: {
          intersection: [
            { type: 'direct', relation: 'manager_approval' },
            { type: 'direct', relation: 'peer_approval' },
            { type: 'direct', relation: 'system_approval' }
          ],
          aggregator: 'majority' // Require consensus
        }
      });
      
      const result = arbiter.authChecker.check('user:bob', 'consensus_access', 'project:alpha');
      
      // Should be allowed due to majority consensus
      assert.ok(result.possibility > 0.6, `Expected reasonable possibility for consensus access, got ${result.possibility}`);
    });

    it('handles priority-weighted defeasible logic', () => {
      // Set up rules with different priorities
      arbiter.addRelation('user:charlie', 'high_priority_rule', 'project:beta', { 
        value: 0.8, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:charlie', 'medium_priority_rule', 'project:beta', { 
        value: 0.6, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      arbiter.addRelation('user:charlie', 'low_priority_rule', 'project:beta', { 
        value: 0.9, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      // Configure priority-weighted defeasible rule
      arbiter.setRelationConfig('priority_access', {
        when: {
          intersection: [
            { type: 'direct', relation: 'high_priority_rule', priority: 10 },
            { type: 'direct', relation: 'medium_priority_rule', priority: 5 },
            { type: 'direct', relation: 'low_priority_rule', priority: 1 }
          ],
          aggregator: 'priority' // Weight by priority
        }
      });
      
      const result = arbiter.authChecker.check('user:charlie', 'priority_access', 'project:beta');
      
      // Should be allowed with priority weighting
      assert.ok(result.possibility > 0.5, `Expected reasonable possibility for priority access, got ${result.possibility}`);
    });
  });

  describe('Defeasible Logic Evaluation Modes', () => {
    it('handles binary mode defeasible logic', () => {
      // Set up simple defeasible rule
      arbiter.addRelation('user:diana', 'can_access', 'doc:internal', { 
        value: 1.0, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      arbiter.setRelationConfig('binary_defeasible', {
        when: {
          intersection: [
            { type: 'direct', relation: 'can_access' }
          ]
        },
        mode: 'binary'
      });
      
      const result = arbiter.authChecker.check('user:diana', 'binary_defeasible', 'doc:internal');
      
      // Should work in binary mode
      assert.ok(result.possibility > 0.8, `Expected high possibility in binary mode, got ${result.possibility}`);
    });

    it('handles threshold mode defeasible logic', () => {
      // Set up threshold-based defeasible rule
      arbiter.addRelation('user:eve', 'can_access', 'doc:confidential', { 
        value: 0.7, 
        possibility: 1.0,
        changed_last_at: Date.now()
      });
      
      arbiter.setRelationConfig('threshold_defeasible', {
        when: {
          intersection: [
            { type: 'direct', relation: 'can_access' }
          ]
        },
        mode: 'threshold'
      });
      
      const result = arbiter.authChecker.check('user:eve', 'threshold_defeasible', 'doc:confidential', {
        fastPath: true,
        minAllowPossibility: 0.8
      });
      
      // Should handle threshold mode appropriately
      assert.ok(result.possibility >= 0, `Expected valid possibility in threshold mode, got ${result.possibility}`);
    });
  });
});
