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

describe('ParentRule', () => {
  let arbiter;
  let parentRule;

  beforeEach(() => {
    // Minimal mock Arbiter with relationManager and inference
    arbiter = {
      relationManager: {
        getRelationsFromSrc: (id, rel) => {
          if (rel === 'parent' && id === 'child1') {
            return [{ src: 'child1', dst: 'parentA', rel: 'parent', possibility: 0.8 }];
          }
          if (rel === 'parent' && id === 'child2') {
            return [];
          }
          return [];
        },
        getRelationsToDst: (id, rel) => {
          if (rel === 'parent' && id === 'child1') {
            return [{ src: 'parentA', dst: 'child1', rel: 'parent', possibility: 0.8 }];
          }
          if (rel === 'parent' && id === 'child2') {
            return [];
          }
          return [];
        },
        getRelationsByName: (rel) => {
          if (rel === 'parent') {
            return [
              { src: 'parentA', dst: 'child1', rel: 'parent', possibility: 0.8 },
              { src: 'parentB', dst: 'child2', rel: 'parent', possibility: 0.4 }
            ];
          }
          return [];
        }
      },
      keyByNodeId: new Map([
        ['parentA', 'parentA'],
        ['parentB', 'parentB'],
        ['child1', 'child1'],
        ['child2', 'child2']
      ]),
      resolveKey: (id) => arbiter.keyByNodeId.get(id),
      indices: {
        getDirectRelation: (srcId, rel, dstId) => {
          if (srcId === 'user' && dstId === 'parentA') return { possibility: 0.8 };
          if (srcId === 'user' && dstId === 'parentB') return { possibility: 0.6 };
          return null;
        }
      },
      _getInferenceEngine: () => ({
        params: { minCaseThreshold: 1 },
        estimatePolicyElement: (src, rel, dst) => {
          if (src === 'parentB' && rel === 'parent' && dst === 'child2') {
            return { outcome: 'positive', totalCases: 2, possibility: 0.6 };
          }
          return { outcome: 'negative', totalCases: 1, possibility: 0.1 };
        }
      })
    };
    parentRule = new ParentRule(arbiter);
  });

  it('returns correct possibility and meta for direct parent', () => {
    const rule = { type: 'parent', parentRelation: 'parent' };
    const res = parentRule._evaluateRule('user', 'user', 'child1', 'child1', rule, {}, null, {});
    assert.strictEqual(res.possibility, 0.8);
    assert.ok(res.meta.parentRule);
    assert.strictEqual(res.meta.parentRule.type, 'parent_access_checked');
    assert.strictEqual(res.meta.parentRule.parentKey, 'parentA');
  });

  // it('returns correct possibility and meta for inferred parent', () => {
  //   // Mock inference result
  //   arbiter.inferencer = {
  //     inferRelation: () => ({
  //       possibility: 0.6, // Inferred possibility
  //       meta: { type: 'inferred', reliability: 0.8 }
  //     })
  //   };
  //   const res = parentRule._evaluateRule('child1', 'child1', 'grandparent', 'grandparent', ruleInferred, {}, null, {});
  //   assert.ok(res.possibility > 0.5 && res.possibility < 0.7); // Check within inferred range
  //   assert.deepStrictEqual(res.meta.type, 'inferred');
  // });

  it('returns 0 possibility if no parent and no inference', () => {
    const rule = { type: 'parent', parentRelation: 'parent', allowInference: false };
    const res = parentRule._evaluateRule('user', 'user', 'child2', 'child2', rule, {}, null, {});
    assert.strictEqual(res.possibility, 0);
    assert.ok(res.meta.parentRule);
    assert.strictEqual(res.meta.parentRule.type, 'no_parent_relationship_found');
  });

  it('applies minPossibility threshold', () => {
    const rule = { type: 'parent', parentRelation: 'parent' };
    const res = parentRule._evaluateRule('user', 'user', 'child1', 'child1', rule, {}, null, { minPossibility: 0.9, fastPath: true });
    assert.strictEqual(res.possibility, 0);
    assert.ok(res.meta.parentRule.cutoffAppliedPostFusion);
  });

  it('applies OWA aggregation for multiple parents', () => {
    // Add a second direct parent for child1
    arbiter.relationManager.getRelationsToDst = (id, rel) => {
      if (rel === 'parent' && id === 'child1') {
        return [
          { src: 'parentA', dst: 'child1', rel: 'parent', possibility: 0.8 },
          { src: 'parentB', dst: 'child1', rel: 'parent', possibility: 0.6 }
        ];
      }
      return [];
    };
    const rule = { type: 'parent', parentRelation: 'parent', aggregator: 'mean' };
    const res = parentRule._evaluateRule('user', 'user', 'child1', 'child1', rule, {}, null, {});
    assert.ok(res.possibility > 0.6 && res.possibility < 0.8);
    assert.ok(res.meta.parentRule.fusionMethod === 'mean');
    assert.strictEqual(res.meta.parentRule.pathsConsidered, 2);
  });
}); 
