/**
 * StrandCoordinateMetadata — runtime-backed strand-specific metadata
 * attached to a ConflictResolvedCoordinate.
 *
 * Carries the strand identity, its base observation Lamport ceiling,
 * overlay coordinates, and braid summary for analysis results that
 * resolved through a strand coordinate rather than the frontier.
 *
 * Instances are frozen on construction. All invariants are validated
 * eagerly. The nested `braid` block is deep-frozen.
 *
 * @module domain/types/conflict/StrandCoordinateMetadata
 */
import WarpError from "../../errors/WarpError.js";
import { compareStrings, requireBoolean, requireNonEmptyString, requireNonNegativeInt } from "./validation.js";
const CTX = 'StrandCoordinateMetadata';
/**
 * Validates that a value is either a non-negative integer or null.
 */
function requireOptionalNonNegativeInt(value, name) {
    if (value === null) {
        return null;
    }
    return requireNonNegativeInt(value, name, CTX);
}
/**
 * Validates that a value is either a non-empty string or null.
 */
function requireNullableString(value, name) {
    if (value === null) {
        return null;
    }
    return requireNonEmptyString(value, name, CTX);
}
/**
 * Validates the optional braid sub-object and deep-freezes it when
 * provided. Returns undefined when absent.
 */
function freezeBraid(braid) {
    if (braid === undefined) {
        return undefined;
    }
    if (braid === null || typeof braid !== 'object') {
        throw new WarpError(`${CTX}: braid must be an object when provided`, 'E_VALIDATION');
    }
    requireNonNegativeInt(braid.readOverlayCount, 'braid.readOverlayCount', CTX);
    if (!Array.isArray(braid.braidedStrandIds)) {
        throw new WarpError(`${CTX}: braid.braidedStrandIds must be an array`, 'E_VALIDATION');
    }
    const ids = braid.braidedStrandIds;
    return Object.freeze({
        readOverlayCount: braid.readOverlayCount,
        braidedStrandIds: Object.freeze(ids.slice().sort(compareStrings)),
    });
}
/**
 * A runtime-backed summary of a strand coordinate, attached to a
 * ConflictResolvedCoordinate when the analysis ran against a strand
 * instead of the frontier.
 */
export default class StrandCoordinateMetadata {
    strandId;
    baseLamportCeiling;
    overlayHeadPatchSha;
    overlayPatchCount;
    overlayWritable;
    braid;
    /**
     * Creates a frozen StrandCoordinateMetadata with validated fields.
     */
    constructor({ strandId, baseLamportCeiling, overlayHeadPatchSha, overlayPatchCount, overlayWritable, braid }) {
        this.strandId = requireNonEmptyString(strandId, 'strandId', CTX);
        this.baseLamportCeiling = requireOptionalNonNegativeInt(baseLamportCeiling, 'baseLamportCeiling');
        this.overlayHeadPatchSha = requireNullableString(overlayHeadPatchSha, 'overlayHeadPatchSha');
        this.overlayPatchCount = requireNonNegativeInt(overlayPatchCount, 'overlayPatchCount', CTX);
        this.overlayWritable = requireBoolean(overlayWritable, 'overlayWritable', CTX);
        this.braid = freezeBraid(braid);
        Object.freeze(this);
    }
}
