const TRUST_SCORES = {
  token_projection: 70,
  workflow_overlay: 60,
  request_observed: 50,
  attested_context: 45,
  challenge_evidence: 40,
  provenance_overlay: 30,
  caller_declared: 20,
  session_projection: 80,
  partial: 10
};

const SUPPORTED_REDUCERS = {
  ignore_conflict: true,
  unique: true,
  first: true,
  dedupe: true,
  union_refs: true,
  min_value: true,
  max_value: true,
  latest: true,
  strongest: true,
  weakest: true
};

function trustScoreForFact(fact) {
  const layer = (fact?.layer_name || fact?.layer || fact?.source || 'partial');
  return TRUST_SCORES[layer] || 0;
}

function stableCompare(a, b) {
  const aSrc = a.src || 0;
  const bSrc = b.src || 0;
  if (aSrc !== bSrc) return aSrc < bSrc ? -1 : 1;
  const aRel = a.rel || '';
  const bRel = b.rel || '';
  if (aRel !== bRel) return aRel < bRel ? -1 : 1;
  const aDst = a.dst || 0;
  const bDst = b.dst || 0;
  if (aDst !== bDst) return aDst < bDst ? -1 : 1;
  const aChanged = a.changed_last_at || 0;
  const bChanged = b.changed_last_at || 0;
  if (aChanged !== bChanged) return aChanged < bChanged ? -1 : 1;
  const aUpdated = a.updated_last_at || 0;
  const bUpdated = b.updated_last_at || 0;
  if (aUpdated !== bUpdated) return aUpdated < bUpdated ? -1 : 1;
  return 0;
}

function compareTrustThenStable(a, b) {
  const trustDelta = trustScoreForFact(b) - trustScoreForFact(a);
  if (trustDelta !== 0) return trustDelta;
  const aUpdated = a.updated_last_at || 0;
  const bUpdated = b.updated_last_at || 0;
  if (bUpdated !== aUpdated) return bUpdated - aUpdated;
  return stableCompare(a, b);
}

function deepEqualAttributes(a, b) {
  if (a === b) return true;
  if (!a || !b) return !a && !b;

  const aRefs = Array.isArray(a.refs) ? a.refs : null;
  const bRefs = Array.isArray(b.refs) ? b.refs : null;
  const aHashes = Array.isArray(a.hashes) ? a.hashes : null;
  const bHashes = Array.isArray(b.hashes) ? b.hashes : null;

  if ((a.ref || null) !== (b.ref || null)) return false;
  if ((a.hash || null) !== (b.hash || null)) return false;

  if (!!aRefs !== !!bRefs) return false;
  if (aRefs) {
    if (aRefs.length !== bRefs.length) return false;
    for (let i = 0; i < aRefs.length; i++) if (aRefs[i] !== bRefs[i]) return false;
  }

  if (!!aHashes !== !!bHashes) return false;
  if (aHashes) {
    if (aHashes.length !== bHashes.length) return false;
    for (let i = 0; i < aHashes.length; i++) if (aHashes[i] !== bHashes[i]) return false;
  }

  const aKeys = Object.keys(a);
  const bKeys = Object.keys(b);
  if (aKeys.length !== bKeys.length) return false;
  for (let i = 0; i < aKeys.length; i++) {
    const key = aKeys[i];
    if (key === 'refs' || key === 'hashes' || key === 'ref' || key === 'hash') continue;
    if (a[key] !== b[key]) return false;
  }
  return true;
}

function isEquivalent(a, b) {
  if (!a || !b) return false;
  if ((a.possibility ?? 1) !== (b.possibility ?? 1)) return false;
  if ((a.reliability ?? 1) !== (b.reliability ?? 1)) return false;
  if ((a.value ?? null) !== (b.value ?? null)) return false;
  return deepEqualAttributes(a.attributes || null, b.attributes || null);
}

function pickStrongest(facts, weakest = false) {
  const ordered = [...facts].sort((a, b) => {
    const trustDelta = trustScoreForFact(b) - trustScoreForFact(a);
    if (trustDelta !== 0) return weakest ? -trustDelta : trustDelta;

    const possDelta = (b.possibility ?? 1) - (a.possibility ?? 1);
    if (possDelta !== 0) return weakest ? -possDelta : possDelta;

    const relDelta = (b.reliability ?? 1) - (a.reliability ?? 1);
    if (relDelta !== 0) return weakest ? -relDelta : relDelta;

    const updatedDelta = (b.updated_last_at || 0) - (a.updated_last_at || 0);
    if (updatedDelta !== 0) return weakest ? -updatedDelta : updatedDelta;

    return stableCompare(a, b);
  });
  return ordered[0] || null;
}

function mergeRefAttributes(facts) {
  const refs = [];
  const seen = Object.create(null);
  const add = (value) => {
    if (typeof value !== 'string') return;
    if (seen[value]) return;
    seen[value] = true;
    refs.push(value);
  };
  for (let i = 0; i < facts.length; i++) {
    const attrs = facts[i].attributes || null;
    if (!attrs) continue;
    add(attrs.ref);
    add(attrs.hash);
    if (Array.isArray(attrs.refs)) {
      for (let j = 0; j < attrs.refs.length; j++) add(attrs.refs[j]);
    }
    if (Array.isArray(attrs.hashes)) {
      for (let j = 0; j < attrs.hashes.length; j++) add(attrs.hashes[j]);
    }
  }
  return refs;
}

function applyReducerToFacts(facts, reducer) {
  const sorted = [...facts].sort(compareTrustThenStable);
  const highestTrust = trustScoreForFact(sorted[0]);
  const survivors = [];
  for (let i = 0; i < sorted.length; i++) {
    if (trustScoreForFact(sorted[i]) === highestTrust) survivors.push(sorted[i]);
  }

  const reducerName = reducer || null;
  if (!reducerName) {
    if (survivors.length === 1) {
      return {
        fact: survivors[0],
        audit: { reducer: null, conflict: false, discarded_lower_trust_count: sorted.length - survivors.length }
      };
    }
    for (let i = 1; i < survivors.length; i++) {
      if (!isEquivalent(survivors[0], survivors[i])) {
        return {
          fact: survivors[0],
          audit: {
            reducer: null,
            conflict: true,
            reason_code: 'no_reducer_conflict_first_kept',
            discarded_lower_trust_count: sorted.length - survivors.length
          }
        };
      }
    }
    return {
      fact: survivors[0],
      audit: { reducer: null, conflict: false, discarded_lower_trust_count: sorted.length - survivors.length }
    };
  }

  if (!SUPPORTED_REDUCERS[reducerName]) {
    return {
      fact: survivors[0] || null,
      audit: { reducer: reducerName, conflict: true, reason_code: 'unknown_reducer' }
    };
  }

  if (reducerName === 'ignore_conflict') {
    for (let i = 1; i < survivors.length; i++) {
      if (!isEquivalent(survivors[0], survivors[i])) {
        return { fact: null, audit: { reducer: reducerName, conflict: true, reason_code: 'ignore_conflict_absent' } };
      }
    }
    return { fact: survivors[0] || null, audit: { reducer: reducerName, conflict: false } };
  }

  if (reducerName === 'unique') {
    if (survivors.length !== 1) {
      return { fact: null, audit: { reducer: reducerName, conflict: true, reason_code: 'unique_violation' } };
    }
    return { fact: survivors[0], audit: { reducer: reducerName, conflict: false } };
  }

  if (reducerName === 'first') {
    return { fact: survivors[0] || null, audit: { reducer: reducerName, conflict: survivors.length > 1 } };
  }

  if (reducerName === 'dedupe') {
    for (let i = 1; i < survivors.length; i++) {
      if (!isEquivalent(survivors[0], survivors[i])) {
        return { fact: null, audit: { reducer: reducerName, conflict: true, reason_code: 'dedupe_incompatible' } };
      }
    }
    return { fact: survivors[0] || null, audit: { reducer: reducerName, conflict: false } };
  }

  if (reducerName === 'union_refs') {
    const base = survivors[0] || null;
    if (!base) return { fact: null, audit: { reducer: reducerName, conflict: false } };
    for (let i = 0; i < survivors.length; i++) {
      const fact = survivors[i];
      if (fact.value !== undefined && fact.value !== null) {
        return { fact: null, audit: { reducer: reducerName, conflict: true, reason_code: 'union_refs_non_reference_value' } };
      }
    }
    const refs = mergeRefAttributes(survivors);
    return {
      fact: {
        ...base,
        attributes: {
          ...(base.attributes || {}),
          refs
        }
      },
      audit: { reducer: reducerName, conflict: false, merged_count: survivors.length }
    };
  }

  if (reducerName === 'min_value' || reducerName === 'max_value') {
    for (let i = 0; i < survivors.length; i++) {
      if (typeof survivors[i].value !== 'number') {
        return { fact: null, audit: { reducer: reducerName, conflict: true, reason_code: 'numeric_value_required' } };
      }
    }
    const base = survivors[0] || null;
    if (!base) return { fact: null, audit: { reducer: reducerName, conflict: false } };
    let value = survivors[0].value;
    for (let i = 1; i < survivors.length; i++) {
      value = reducerName === 'min_value' ? Math.min(value, survivors[i].value) : Math.max(value, survivors[i].value);
    }
    return { fact: { ...base, value }, audit: { reducer: reducerName, conflict: survivors.length > 1 } };
  }

  if (reducerName === 'latest') {
    const latest = [...survivors].sort((a, b) => {
      const updatedDelta = (b.updated_last_at || 0) - (a.updated_last_at || 0);
      if (updatedDelta !== 0) return updatedDelta;
      return compareTrustThenStable(a, b);
    })[0] || null;
    return { fact: latest, audit: { reducer: reducerName, conflict: survivors.length > 1 } };
  }

  if (reducerName === 'strongest') {
    return { fact: pickStrongest(survivors, false), audit: { reducer: reducerName, conflict: survivors.length > 1 } };
  }

  if (reducerName === 'weakest') {
    return { fact: pickStrongest(survivors, true), audit: { reducer: reducerName, conflict: survivors.length > 1 } };
  }

  return {
    fact: survivors[0] || null,
    audit: { reducer: reducerName, conflict: true, reason_code: 'unsupported_reducer' }
  };
}

export function listSupportedReducers() {
  return Object.keys(SUPPORTED_REDUCERS);
}

export function isSupportedReducer(name) {
  return !!SUPPORTED_REDUCERS[name];
}

export function reduceConflictingFacts(facts, reducer = null) {
  const list = [];
  if (Array.isArray(facts)) {
    for (let i = 0; i < facts.length; i++) {
      if (facts[i]) list.push(facts[i]);
    }
  }
  if (list.length === 0) return { fact: null, audit: { reducer, conflict: false, empty: true } };
  if (list.length === 1) return { fact: list[0], audit: { reducer, conflict: false, single: true } };
  return applyReducerToFacts(list, reducer);
}

export function getTrustScore(layerName) {
  return TRUST_SCORES[layerName] || 0;
}
