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

describe('AuthorizationChecker Basic Functionality', () => {
  let arbiter;
  let authChecker;

  test.beforeEach(() => {
    arbiter = new Arbiter();
    authChecker = arbiter.authChecker;
  });

  test('performs basic authorization checks', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.addRelation('user1', 'can_read', 'project1', 1.0);
    arbiter.setRelationConfig('can_read', { type: 'direct' });

    const result = authChecker.check('user1', 'can_read', 'project1', { includeMeta: true });

    assert.strictEqual(result.possibility, 1);
    // CI-001 fix: fast path → 'direct_match'
    assert.strictEqual(result.reason, 'direct_match');
  });

  test('handles missing nodes', () => {
    arbiter.addNode('user1', 'user');
    
    const result = authChecker.check('user1', 'can_read', 'nonexistent');
    
    assert.strictEqual(result.possibility, 0);
    assert.strictEqual(result.reason, 'missing_node');
  });

  test('handles missing relations', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.setRelationConfig('can_read', { type: 'direct' });

    const result = authChecker.check('user1', 'can_read', 'project1', { includeMeta: true });

    assert.strictEqual(result.possibility, 0);
    // CI-001 fix: fast path → 'no_relation'
    assert.strictEqual(result.reason, 'no_relation');
  });

  test('handles missing relation configuration', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.addRelation('user1', 'can_read', 'project1', 1.0);
    
    const result = authChecker.check('user1', 'can_read', 'project1', { includeMeta: true });
    
    assert.strictEqual(result.possibility, 0);
    assert.strictEqual(result.reason, 'no_config');
  });

  test('supports binary mode with high threshold', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.addRelation('user1', 'can_read', 'project1', 0.9);
    arbiter.setRelationConfig('can_read', { type: 'direct' });
    
    const result = authChecker.check('user1', 'can_read', 'project1', { 
      binary: true,
      minAllowPossibility: 0.95 
    });
    
    assert.strictEqual(result.possibility, 0.9);
    assert.strictEqual(result.allow, false);
    assert.strictEqual(result.deny, false);
  });

  test('supports binary mode with low threshold', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.addRelation('user1', 'can_read', 'project1', 0.9);
    arbiter.setRelationConfig('can_read', { type: 'direct' });
    
    const result = authChecker.check('user1', 'can_read', 'project1', { 
      binary: true,
      minAllowPossibility: 0.8 
    });
    
    assert.strictEqual(result.possibility, 0.9);
    assert.strictEqual(result.allow, true);
    assert.strictEqual(result.deny, false);
  });

  test('handles visited set for cycle prevention', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.addRelation('user1', 'can_read', 'project1', 1.0);
    arbiter.setRelationConfig('can_read', { type: 'direct' });
    
    const visited = new Set();
    visited.add({ userKey: 'user1', relation: 'can_read', objectKey: 'project1' });
    
    const result = authChecker.check('user1', 'can_read', 'project1', { 
      _visited: visited 
    });
    
    assert.strictEqual(result.possibility, 0);
    assert.strictEqual(result.reason, 'cycle');
  });

  test('handles backward compatibility with Set parameter', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.addRelation('user1', 'can_read', 'project1', 1.0);
    arbiter.setRelationConfig('can_read', { type: 'direct' });

    const visited = new Set();
    const result = authChecker.check('user1', 'can_read', 'project1', visited, 'can_read');

    assert.strictEqual(result.possibility, 1);
    // CI-001 fix: fast path → 'direct_match'
    assert.strictEqual(result.reason, 'direct_match');
  });

  test('handles edge cases gracefully', () => {
    // Test with empty strings
    const result1 = authChecker.check('', 'can_read', '');
    assert.strictEqual(result1.possibility, 0);
    assert.strictEqual(result1.reason, 'missing_node');
    
    // Test with null/undefined
    const result2 = authChecker.check(null, 'can_read', null);
    assert.strictEqual(result2.possibility, 0);
    assert.strictEqual(result2.reason, 'missing_node');
  });

  test('handles complex rule configurations', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('group1', 'group');
    arbiter.addNode('project1', 'project');
    
    arbiter.addRelation('user1', 'member_of', 'group1', 1.0);
    arbiter.addRelation('group1', 'can_access', 'project1', 1.0);
    
    arbiter.setRelationConfig('member_of', { type: 'direct' });
    arbiter.setRelationConfig('can_access', { 
      type: 'chain',
      steps: [
        { relation: 'member_of', direction: 'out' },
        { relation: 'can_access', direction: 'out' }
      ]
    });
    
    const result = authChecker.check('user1', 'can_access', 'project1');
    
    assert.strictEqual(result.possibility, 1);
    assert.strictEqual(result.reason, 'allow_rule_matched');
  });

  test('returns proper meta information', () => {
    arbiter.addNode('user1', 'user');
    arbiter.addNode('project1', 'project');
    arbiter.addRelation('user1', 'can_read', 'project1', 1.0);
    arbiter.setRelationConfig('can_read', { type: 'direct' });

    const result = authChecker.check('user1', 'can_read', 'project1', { includeMeta: true });

    assert.ok(result.meta);
    // CI-001 fix: fast path exposes meta.allow.{ruleType,reason,...}
    // instead of the slow-path meta.{maxDeny,maxAllow} shape.
    assert.strictEqual(result.meta.allow.ruleType, 'direct');
    assert.strictEqual(result.meta.allow.reason, 'direct');
  });
});
