/**
 * WorldlineSelector — abstract base for worldline selector descriptors.
 *
 * A worldline selector specifies which worldline an observer projects.
 * Three variants: LiveSelector (canonical worldline),
 * CoordinateSelector (hypothetical worldline at specific writer tips),
 * StrandSelector (single writer's isolated worldline).
 *
 * @module domain/types/WorldlineSelector
 */
import QueryError from "../errors/QueryError.js";
/**
 * Validates a ceiling value. Must be null, undefined, or a non-negative integer.
 */
function validateCeiling(ceiling) {
    if (ceiling === undefined || ceiling === null) {
        return null;
    }
    if (typeof ceiling !== 'number' || !Number.isInteger(ceiling) || ceiling < 0) {
        throw new QueryError(`ceiling must be null or a non-negative integer, got ${typeof ceiling === 'number' ? ceiling : typeof ceiling}`, { code: 'E_SELECTOR_INVALID' });
    }
    return ceiling;
}
/**
 * Subclass registry — populated by each subclass module on import.
 */
const registry = {};
/**
 * Abstract base for worldline selectors.
 *
 * Subclasses: LiveSelector, CoordinateSelector, StrandSelector.
 * Use WorldlineSelector.from() to convert plain { kind } objects
 * at API boundaries.
 */
class WorldlineSelector {
    /**
     * Deep-clone this selector.
     */
    clone() {
        throw new QueryError('WorldlineSelector.clone() is abstract', { code: 'E_SELECTOR_ABSTRACT' });
    }
    /**
     * Convert this selector to a plain DTO matching the WorldlineSource shape.
     */
    toDTO() {
        throw new QueryError('WorldlineSelector.toDTO() is abstract', { code: 'E_SELECTOR_ABSTRACT' });
    }
    /**
     * Register a subclass for use in from().
     */
    static _register(kind, ctor) {
        if (Object.isFrozen(registry)) {
            throw new QueryError('WorldlineSelector registry is frozen — cannot register after first use', { code: 'E_SELECTOR_REGISTRY_FROZEN' });
        }
        registry[kind] = ctor;
    }
    /**
     * Normalize a raw source descriptor into a WorldlineSelector instance.
     *
     * Accepts class instances (returned as-is), plain { kind } objects
     * (converted to the appropriate subclass), and null/undefined
     * (defaults to LiveSelector).
     */
    static from(raw) {
        if (raw instanceof WorldlineSelector) {
            return raw;
        }
        // Freeze registry on first use — prevents post-init hijacking
        if (!Object.isFrozen(registry)) {
            Object.freeze(registry);
        }
        return fromPlainObject(raw);
    }
}
/**
 * Builds a WorldlineSelector from a plain object or null/undefined.
 *
 * Note: the kind->constructor-args mapping is hardcoded for the three
 * known selector kinds. Adding a new kind requires editing this function.
 *
 * Kept separate from the class to reduce static from() complexity.
 */
function fromPlainObject(raw) {
    const value = raw ?? { kind: 'live' };
    const { kind } = value;
    if (!(kind in registry)) {
        throw new QueryError(`unknown worldline selector kind: ${String(kind)}`, { code: 'E_SELECTOR_INVALID' }); // nosemgrep: ts-no-unknown-outside-adapters -- 0025B
    }
    const Ctor = registry[kind];
    if (kind === 'live') {
        return new Ctor(value['ceiling']); // nosemgrep: ts-no-unknown-outside-adapters -- 0025B
    }
    if (kind === 'coordinate') {
        return new Ctor(value['frontier'], value['ceiling'], value['checkpointSha']); // nosemgrep: ts-no-unknown-outside-adapters -- 0025B
    }
    return new Ctor(value['strandId'], value['ceiling']); // nosemgrep: ts-no-unknown-outside-adapters -- 0025B
}
export { validateCeiling };
export default WorldlineSelector;
