/**
 * ConflictTrace — runtime-backed record of a single detected conflict.
 *
 * @module domain/types/conflict/ConflictTrace
 */
import WarpError from "../../errors/WarpError.js";
import ConflictAnchor from "./ConflictAnchor.js";
import ConflictReceiptRef from "./ConflictReceiptRef.js";
import { requireNonEmptyString, requireEnum, compareStrings } from "./validation.js";
const CTX = 'ConflictTrace';
const VALID_KINDS = new Set(['supersession', 'eventual_override', 'redundancy']);
const VALID_EVIDENCE_LEVELS = new Set(['summary', 'standard', 'full']);
/**
 * Normalizes any receipt-ref input into a ConflictReceiptRef
 * instance. Accepts either an already-constructed class or a plain
 * carrier object — the class absorbs both.
 */
function toReceiptRef(ref) {
    return ref instanceof ConflictReceiptRef
        ? ref
        : ConflictReceiptRef.from(ref);
}
/**
 * Deep-freezes the evidence object, converting each receiptRef
 * carrier into a ConflictReceiptRef instance.
 */
function freezeEvidence(evidence) {
    if (evidence === null || evidence === undefined || typeof evidence !== 'object') {
        throw new WarpError(`${CTX}: evidence must be an object`, 'E_VALIDATION');
    }
    requireEnum(evidence.level, VALID_EVIDENCE_LEVELS, { name: 'evidence.level', context: CTX });
    return Object.freeze({
        level: evidence.level,
        patchRefs: Object.freeze([...evidence.patchRefs]),
        receiptRefs: Object.freeze(evidence.receiptRefs.map(toReceiptRef)),
    });
}
/**
 * A runtime-backed record of a single conflict detected by the analyzer.
 *
 * Instances are frozen on construction. Losers, evidence, and classification notes are deep-frozen.
 */
export default class ConflictTrace {
    conflictId;
    kind;
    target;
    winner;
    losers;
    resolution;
    whyFingerprint;
    classificationNotes;
    evidence;
    /**
     * Creates a frozen ConflictTrace.
     */
    constructor({ conflictId, kind, target, winner, losers, resolution, whyFingerprint, classificationNotes, evidence }) {
        this.conflictId = requireNonEmptyString(conflictId, 'conflictId', CTX);
        this.kind = requireEnum(kind, VALID_KINDS, { name: 'kind', context: CTX });
        this.target = target;
        this.winner = winner;
        this.losers = Object.freeze([...losers]);
        this.resolution = resolution;
        this.whyFingerprint = requireNonEmptyString(whyFingerprint, 'whyFingerprint', CTX);
        this.classificationNotes = classificationNotes !== undefined && classificationNotes !== null
            ? Object.freeze([...classificationNotes])
            : undefined;
        this.evidence = freezeEvidence(evidence);
        Object.freeze(this);
    }
    /**
     * Tests whether the specified writer participated as winner or loser.
     */
    touchesWriter(writerId) {
        if (this.winner.anchor.writerId === writerId) {
            return true;
        }
        return this.losers.some((loser) => loser.anchor.writerId === writerId);
    }
    /**
     * Compares two ConflictTraces for deterministic ordering by kind, target, winner, then id.
     */
    static compare(a, b) {
        const kindCmp = compareStrings(a.kind, b.kind);
        if (kindCmp !== 0) {
            return kindCmp;
        }
        const targetCmp = compareStrings(a.target.targetDigest, b.target.targetDigest);
        if (targetCmp !== 0) {
            return targetCmp;
        }
        const winnerCmp = ConflictAnchor.compare(a.winner.anchor, b.winner.anchor);
        return winnerCmp !== 0 ? winnerCmp : compareStrings(a.conflictId, b.conflictId);
    }
}
