import { test } from 'node:test';
import assert from 'node:assert/strict';
import fc from 'fast-check';
import { reduceConflictingFacts, listSupportedReducers, getTrustScore } from '../../../src/core/partial-graph/reducers.js';

const LAYERS = [
  'token_projection',
  'workflow_overlay',
  'request_observed',
  'attested_context',
  'challenge_evidence',
  'provenance_overlay',
  'caller_declared'
];

const REDUCERS = listSupportedReducers();

const factArb = fc.record({
  possibility: fc.double({ min: 0, max: 1, noNaN: true, noDefaultInfinity: true }),
  reliability: fc.double({ min: 0, max: 1, noNaN: true, noDefaultInfinity: true }),
  value: fc.oneof(fc.constant(null), fc.integer({ min: -1000, max: 1000 })),
  updated_last_at: fc.integer({ min: 0, max: 1000000 }),
  layer_name: fc.constantFrom(...LAYERS)
}).map((x) => ({
  src: 1,
  rel: 'request_has_timestamp',
  dst: 2,
  possibility: x.possibility,
  reliability: x.reliability,
  value: x.value,
  updated_last_at: x.updated_last_at,
  changed_last_at: x.updated_last_at,
  attributes: null,
  layer_name: x.layer_name,
  source: 'partial'
}));

test('fast-check reducers: winner never comes from lower trust layer', () => {
  fc.assert(
    fc.property(
      fc.array(factArb, { minLength: 2, maxLength: 15 }),
      fc.constantFrom(...REDUCERS),
      (facts, reducer) => {
        const reduced = reduceConflictingFacts(facts, reducer);
        if (!reduced.fact) return true;
        const maxTrust = facts.reduce((m, f) => Math.max(m, getTrustScore(f.layer_name)), -Infinity);
        return getTrustScore(reduced.fact.layer_name) === maxTrust;
      }
    ),
    { numRuns: 200 }
  );
});

test('fast-check reducers: latest selects max timestamp within top trust class', () => {
  fc.assert(
    fc.property(
      fc.array(factArb, { minLength: 2, maxLength: 15 }),
      (facts) => {
        const reduced = reduceConflictingFacts(facts, 'latest');
        if (!reduced.fact) return true;
        const winnerTrust = getTrustScore(reduced.fact.layer_name);
        const top = facts.filter((f) => getTrustScore(f.layer_name) === winnerTrust);
        const expected = top.reduce((m, f) => Math.max(m, f.updated_last_at || 0), -Infinity);
        return (reduced.fact.updated_last_at || 0) === expected;
      }
    ),
    { numRuns: 200 }
  );
});
