/**
 * ConflictAnchor — runtime-backed identity coordinate for an operation in a patch.
 *
 * Identifies a specific operation within the conflict analysis pipeline by its
 * patch SHA, writer ID, lamport clock, and operation index. Optional receipt
 * fields track the receipt-time coordinates when the operation was applied.
 *
 * @module domain/types/conflict/ConflictAnchor
 */
import WarpError from "../../errors/WarpError.js";
const VALID_SHA_RE = /^[0-9a-f]{4,64}$/;
/**
 * Validates that a string value is non-empty.
 */
function requireNonEmptyString(value, name) {
    if (typeof value !== 'string' || value.length === 0) {
        throw new WarpError(`ConflictAnchor: ${name} must be a non-empty string`, 'E_VALIDATION');
    }
    return value;
}
/**
 * Validates that a numeric value is a non-negative integer.
 */
function requireNonNegativeInt(value, name) {
    if (!Number.isInteger(value) || value < 0) {
        throw new WarpError(`ConflictAnchor: ${name} must be a non-negative integer`, 'E_VALIDATION');
    }
    return value;
}
/**
 * Validates an optional hex SHA string.
 */
function optionalSha(value, name) {
    if (value === undefined || value === null) {
        return undefined;
    }
    if (typeof value !== 'string' || !VALID_SHA_RE.test(value)) {
        throw new WarpError(`ConflictAnchor: ${name} must be a hex SHA string (4-64 chars) when provided`, 'E_VALIDATION');
    }
    return value;
}
/**
 * Validates an optional non-negative integer.
 */
function optionalNonNegativeInt(value, name) {
    if (value === undefined || value === null) {
        return undefined;
    }
    return requireNonNegativeInt(value, name);
}
/**
 * Compares two strings lexicographically.
 */
function compareStrings(a, b) {
    if (a === b) {
        return 0;
    }
    return a < b ? -1 : 1;
}
/**
 * A runtime-backed identity coordinate for a single operation within a patch.
 *
 * Instances are frozen on construction. All invariants are validated eagerly.
 */
export default class ConflictAnchor {
    patchSha;
    writerId;
    lamport;
    opIndex;
    receiptPatchSha;
    receiptLamport;
    receiptOpIndex;
    /**
     * Creates a new ConflictAnchor with validated fields.
     */
    constructor({ patchSha, writerId, lamport, opIndex, receiptPatchSha, receiptLamport, receiptOpIndex }) {
        this.patchSha = requireNonEmptyString(patchSha, 'patchSha');
        this.writerId = requireNonEmptyString(writerId, 'writerId');
        this.lamport = requireNonNegativeInt(lamport, 'lamport');
        this.opIndex = requireNonNegativeInt(opIndex, 'opIndex');
        this.receiptPatchSha = optionalSha(receiptPatchSha, 'receiptPatchSha');
        this.receiptLamport = optionalNonNegativeInt(receiptLamport, 'receiptLamport');
        this.receiptOpIndex = optionalNonNegativeInt(receiptOpIndex, 'receiptOpIndex');
        Object.freeze(this);
    }
    /**
     * Serializes this anchor into a deterministic padded string for sorting and hashing.
     *
     * Format: `writerId:lamport(16-padded):patchSha:opIndex(8-padded)`
     */
    toString() {
        return `${this.writerId}:${String(this.lamport).padStart(16, '0')}:${this.patchSha}:${String(this.opIndex).padStart(8, '0')}`;
    }
    /**
     * Compares two ConflictAnchors using their deterministic string representations.
     */
    static compare(a, b) {
        return compareStrings(a.toString(), b.toString());
    }
    /**
     * Creates a ConflictAnchor from an OpRecord, using the record's patch coordinates
     * and mapping receiptPatchSha/receiptLamport from the same patch.
     */
    static fromRecord(record) {
        return new ConflictAnchor({
            patchSha: record.patchSha,
            writerId: record.writerId,
            lamport: record.lamport,
            opIndex: record.opIndex,
            receiptPatchSha: record.patchSha,
            receiptLamport: record.lamport,
            receiptOpIndex: record.receiptOpIndex,
        });
    }
    /**
     * Creates a ConflictAnchor from a PatchFrame for diagnostic/traversal output.
     */
    static fromFrame(frame) {
        return new ConflictAnchor({
            patchSha: frame.sha,
            writerId: frame.patch.writer,
            lamport: frame.patch.lamport,
            opIndex: 0,
        });
    }
}
