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

describe('Rule result cache stats', () => {
  test('records cache hits on repeated aggregate checks', () => {
    const arbiter = new Arbiter({ enableRuleResultCache: true, ruleResultCacheTTL: 60000 });
    arbiter.addNode('user:1', 'user');
    arbiter.addNode('resource:1', 'resource');

    arbiter.setRelationConfig('member', { type: 'direct' });
    arbiter.setRelationConfig('allow', {
      union: {
        rules: [{ type: 'direct', relation: 'member' }],
        aggregator: 'max'
      }
    });

    arbiter.registerDependencyIndex(new Map([['member', {
      all: new Set(['allow']),
      byLevel: {
        never: new Set(),
        always: new Set(),
        requires: new Set(),
        when: new Set(),
        unless: new Set(),
        ordinary: new Set(['allow'])
      }
    }]]));

    arbiter.addRelation('user:1', 'member', 'resource:1', 1.0);

    const userId = arbiter.resolveNodeId('user:1');
    const objectId = arbiter.resolveNodeId('resource:1');
    const config = arbiter.relationConfigs.get('allow');

    arbiter.authChecker.ruleEvaluator.evaluateRule(
      userId,
      'user:1',
      objectId,
      'resource:1',
      config,
      new Set(),
      'allow',
      { includeMeta: false, collectValues: false, cacheRuleResult: true }
    );

    const baseKey = arbiter.keyManager.createCompositeKey(userId, 'allow', objectId);
    const cacheKey = `${baseKey}|logical`;
    assert.ok(arbiter.ruleResultCache.get(cacheKey), 'cache entry created');

    arbiter.authChecker.ruleEvaluator.evaluateRule(
      userId,
      'user:1',
      objectId,
      'resource:1',
      config,
      new Set(),
      'allow',
      { includeMeta: false, collectValues: false, cacheRuleResult: true }
    );

    assert.ok(arbiter.ruleResultCacheStats.misses >= 1, 'cache miss recorded');
    assert.ok(arbiter.ruleResultCacheStats.hits >= 1, 'cache hit recorded');
  });

  test('does not prepopulate cache on config set', () => {
    const arbiter = new Arbiter({ enableRuleResultCache: true, ruleResultCacheTTL: 60000 });
    arbiter.addNode('user:1', 'user');
    arbiter.addNode('resource:1', 'resource');

    arbiter.setRelationConfig('risk_score', { type: 'direct' });
    arbiter.setRelationConfig('risk_limit', { type: 'direct' });

    arbiter.addRelation('user:1', 'risk_score', 'resource:1', 1.0, { value: 10 });
    arbiter.addRelation('resource:1', 'risk_limit', 'resource:1', 1.0, { value: 20 });

    arbiter.setRelationConfig('risk_ok_owa', {
      type: 'relational_comparator',
      comparator: '<=',
      left: {
        rule: { type: 'direct', relation: 'risk_score' },
        extractValue: true,
        valueRelation: 'risk_score',
        aggregator: 'owa',
        owaWeights: [1]
      },
      right: {
        rule: { type: 'direct', relation: 'risk_limit' },
        extractValue: true,
        valueRelation: 'risk_limit',
        evaluateFrom: 'object'
      }
    });

    const userId = arbiter.resolveNodeId('user:1');
    const objectId = arbiter.resolveNodeId('resource:1');
    const config = arbiter.relationConfigs.get('risk_ok_owa');
    const cacheKey = arbiter.authChecker.ruleEvaluator._getRuleResultCacheKey(
      userId,
      'risk_ok_owa',
      objectId,
      config
    );

    assert.ok(!arbiter.ruleResultCache.get(cacheKey), 'cache not populated on config set');

    arbiter.authChecker.ruleEvaluator.evaluateRule(
      userId,
      'user:1',
      objectId,
      'resource:1',
      config,
      new Set(),
      'risk_ok_owa',
      { includeMeta: false, collectValues: true, cacheRuleResult: true }
    );

    assert.ok(arbiter.ruleResultCache.get(cacheKey), 'cache populated after evaluation');
  });
});
