/**
 * RuntimeHost - Internal host for WARP multi-writer graph database.
 *
 * Provides a factory for opening multi-writer graphs and methods for
 * creating patches, materializing state, and managing checkpoints.
 *
 * @module domain/RuntimeHost
 * @see WARP Spec Section 11
 */
import VersionVector from "./crdt/VersionVector.js";
import GCPolicy from "./services/GCPolicy.js";
import { TemporalQuery } from "./services/TemporalQuery.js";
import { createImmutableTickReceiptArraySnapshot, createSnapshotWarpState, } from "./services/ImmutableSnapshot.js";
import defaultCodec from "./utils/defaultCodec.js";
import defaultCrypto from "./utils/defaultCrypto.js";
import nullLogger from "./utils/nullLogger.js";
import LogicalTraversal from "./services/query/LogicalTraversal.js";
import LiveQueryReadModelProvider from "./services/query/LiveQueryReadModelProvider.js";
import LRUCache from "./utils/LRUCache.js";
import SyncController from "./services/controllers/SyncController.js";
import StrandController from "./services/controllers/StrandController.js";
import ComparisonController from "./services/controllers/ComparisonController.js";
import HostBackedComparisonCoordinateSideReader from "./services/controllers/HostBackedComparisonCoordinateSideReader.js";
import HostBackedComparisonSideFinalizer from "./services/controllers/HostBackedComparisonSideFinalizer.js";
import SubscriptionController from "./services/controllers/SubscriptionController.js";
import ProvenanceController from "./services/controllers/ProvenanceController.js";
import ForkController from "./services/controllers/ForkController.js";
import QueryController from "./services/controllers/QueryController.js";
import PatchController from "./services/controllers/PatchController.js";
import CheckpointController from "./services/controllers/CheckpointController.js";
import SyncTrustGate from "./services/sync/SyncTrustGate.js";
import AuditVerifierService from "./services/audit/AuditVerifierService.js";
import { E_NO_STATE_MSG } from "./services/controllers/QueryStateMessages.js";
import StateHashService from "./services/state/StateHashService.js";
import { computeStateHash } from "./services/state/StateSerializer.js";
import MaterializeController from "./services/controllers/MaterializeController.js";
import RuntimePatchCollector from "./warp/RuntimePatchCollector.js";
import RuntimeDetachedFactory from "./warp/RuntimeDetachedFactory.js";
import BitmapNeighborProvider from "./services/index/BitmapNeighborProvider.js";
import { cloneState } from "./services/JoinReducer.js";
import { diffStates, isEmptyDiff } from "./services/state/StateDiff.js";
import { buildAdjacency } from "./services/controllers/MaterializeHelpers.js";
import WarpError from "./errors/WarpError.js";
import QueryError from "./errors/QueryError.js";
import { DEFAULT_COMMIT_MESSAGE_CODEC } from "./services/codec/WarpMessageCodec.js";
import { DEFAULT_ADJACENCY_CACHE_SIZE, normalizeTrustConfig, } from "./runtimeHelpers.js";
import { resolveRuntimeHostConstructionOptions, } from "./warp/RuntimeHostBoot.js";
function wantsReceipts(options) {
    return options?.receipts === true;
}
function materializeControllerOptions(options, wantDiff) {
    const controllerOptions = { wantDiff };
    if (options?.receipts !== undefined) {
        controllerOptions.receipts = options.receipts;
    }
    if (options?.ceiling !== undefined) {
        controllerOptions.ceiling = options.ceiling;
    }
    return controllerOptions;
}
function materializeReceiptsResult(result) {
    return Object.freeze({
        state: createSnapshotWarpState(result.state),
        receipts: createImmutableTickReceiptArraySnapshot(result.receipts ?? []),
    });
}
function materializeSnapshotResult(result, includeReceipts) {
    return includeReceipts
        ? materializeReceiptsResult(result)
        : createSnapshotWarpState(result.state);
}
function canUseCachedMaterializedGraph(options, stateDirty, materializedGraph) {
    return options.ceiling === undefined && !stateDirty && materializedGraph !== null;
}
function resolveMaterializedStateDiff(optionsOrDiff) {
    if (optionsOrDiff === null || optionsOrDiff === undefined) {
        return undefined;
    }
    if ('edgesAdded' in optionsOrDiff) {
        return optionsOrDiff;
    }
    return optionsOrDiff.diff ?? undefined;
}
// ── Constructor options ──────────────────────────────────────────────
/**
 * RuntimeHost class for interacting with a WARP multi-writer graph.
 */
export default class RuntimeHost {
    _persistence;
    _graphName;
    _writerId;
    _versionVector;
    _cachedState;
    _stateDirty;
    _gcPolicy;
    _lastGCLamport;
    _patchesSinceGC;
    _patchesSinceCheckpoint;
    _maxObservedLamport;
    _checkpointPolicy;
    _checkpointing;
    _autoMaterialize;
    traverse;
    _materializedGraph;
    _adjacencyCache;
    _lastFrontier;
    _logger;
    _crypto;
    _codec;
    _onDeleteWithData;
    _subscribers;
    _lastNotifiedState;
    _provenanceIndex;
    _temporalQuery;
    _seekCeiling;
    _cachedCeiling;
    _cachedFrontier;
    _seekCache;
    _stateCache;
    _blobStorage;
    _patchBlobStorage;
    _commitMessageCodec;
    _patchInProgress;
    _provenanceDegraded;
    _audit;
    _auditSkipCount;
    _trustConfig;
    _createSyncTrustGate;
    _syncController;
    _strandController;
    _comparisonController;
    _subscriptionController;
    _provenanceController;
    _forkController;
    _queryController;
    _patchController;
    _checkpointController;
    _materializeController;
    _viewService;
    _logicalIndex;
    _propertyReader;
    _cachedViewHash;
    _cachedIndexTree;
    _indexDegraded;
    _effectPipeline;
    _patchJournal;
    _checkpointStore;
    _indexStore;
    _stateHashService;
    _auditService;
    /**
     * Constructs a RuntimeHost instance with injected dependencies and configuration.
     * @private
     */
    // TODO(OG): split constructor responsibilities; legacy hotspot kept explicit until the API redesign cycle.
    // eslint-disable-next-line max-lines-per-function, complexity
    constructor(options) {
        const { persistence, graphName, writerId, gcPolicy = {}, adjacencyCacheSize = DEFAULT_ADJACENCY_CACHE_SIZE, checkpointPolicy, autoMaterialize = true, onDeleteWithData = 'warn', logger, crypto, codec, seekCache, stateCache, audit = false, blobStorage, patchBlobStorage, commitMessageCodec, trust, patchJournal, checkpointStore, indexStore, viewService, stateHashService, auditService, effectPipeline, openStateSession, } = options;
        this._persistence = persistence;
        this._graphName = graphName;
        this._writerId = writerId;
        this._versionVector = VersionVector.empty();
        this._cachedState = null;
        this._stateDirty = false;
        this._gcPolicy = new GCPolicy({ ...GCPolicy.DEFAULT, ...gcPolicy });
        this._lastGCLamport = 0;
        this._patchesSinceGC = 0;
        this._patchesSinceCheckpoint = 0;
        this._maxObservedLamport = 0;
        this._checkpointPolicy = checkpointPolicy || null;
        this._checkpointing = false;
        this._autoMaterialize = autoMaterialize;
        this._materializedGraph = null;
        this._adjacencyCache = adjacencyCacheSize > 0 ? new LRUCache(adjacencyCacheSize) : null;
        this._lastFrontier = null;
        this._logger = logger || null;
        this._crypto = crypto || defaultCrypto;
        this._codec = codec || defaultCodec;
        this._onDeleteWithData = onDeleteWithData;
        this._subscribers = [];
        this._lastNotifiedState = null;
        this._provenanceIndex = null;
        this._temporalQuery = null;
        this._seekCeiling = null;
        this._cachedCeiling = null;
        this._cachedFrontier = null;
        this._seekCache = seekCache || null;
        this._stateCache = stateCache || null;
        this._blobStorage = blobStorage || null;
        this._patchBlobStorage = patchBlobStorage || null;
        this._commitMessageCodec = commitMessageCodec ?? DEFAULT_COMMIT_MESSAGE_CODEC;
        this._patchInProgress = false;
        this._provenanceDegraded = false;
        this._audit = !!audit;
        this._auditSkipCount = 0;
        this._trustConfig = normalizeTrustConfig(trust);
        this._stateHashService = stateHashService || null;
        this._createSyncTrustGate = (override) => {
            const config = normalizeTrustConfig(override ?? this._trustConfig);
            if (config.mode === 'off') {
                return null;
            }
            return this._buildTrustGate(config);
        };
        const trustGate = this._createSyncTrustGate() || undefined;
        this._syncController = new SyncController(this, {
            ...(trustGate !== undefined ? { trustGate } : {}),
        });
        this._strandController = new StrandController(this);
        this._comparisonController = new ComparisonController({
            host: this,
            selectorContext: {
                coordinateReader: new HostBackedComparisonCoordinateSideReader(this),
                sideFinalizer: new HostBackedComparisonSideFinalizer(this),
                strandGraph: this,
            },
        });
        this._subscriptionController = new SubscriptionController(this);
        this._provenanceController = new ProvenanceController(this);
        this._forkController = new ForkController(this);
        this._queryController = new QueryController({
            hostGraph: this,
            graphCloner: new RuntimeDetachedFactory(this, async (detachedOptions) => await RuntimeHost.open(detachedOptions)),
            hashState: async (state) => {
                if (this._stateHashService !== null) {
                    return await this._stateHashService.compute(state);
                }
                return await new StateHashService({
                    crypto: this._crypto,
                    codec: this._codec,
                }).compute(state);
            },
        });
        this.traverse = new LogicalTraversal(new LiveQueryReadModelProvider({
            ensureFreshState: async () => { await this._ensureFreshState(); },
            currentState: () => this._cachedState,
            stateHash: async (state) => {
                if (this._stateHashService !== null) {
                    return await this._stateHashService.compute(state);
                }
                return await new StateHashService({
                    crypto: this._crypto,
                    codec: this._codec,
                }).compute(state);
            },
            neighborProvider: () => this._materializedGraph?.provider ?? null,
        }));
        this._patchController = new PatchController(this);
        this._checkpointController = new CheckpointController(this);
        this._materializeController = new MaterializeController({
            logger: this._logger ?? nullLogger,
            codec: this._codec,
            crypto: this._crypto,
            persistence: this._persistence,
            getStateCache: () => this._stateCache ?? null,
            ...(openStateSession === undefined ? {} : { openStateSession }),
            patches: new RuntimePatchCollector(this),
            graphCloner: new RuntimeDetachedFactory(this, async (detachedOptions) => await RuntimeHost.open(detachedOptions)),
            graphName: this._graphName,
        });
        this._viewService = viewService;
        this._logicalIndex = null;
        this._propertyReader = null;
        this._cachedViewHash = null;
        this._cachedIndexTree = null;
        this._indexDegraded = false;
        this._effectPipeline = effectPipeline || null;
        this._patchJournal = patchJournal;
        this._checkpointStore = checkpointStore;
        this._indexStore = indexStore;
        this._auditService = auditService || null;
    }
    async materialize(options) {
        const wantDiff = options?.receipts !== true && this._cachedIndexTree !== null;
        const result = await this._materializeController.materialize(materializeControllerOptions(options, wantDiff));
        await this._onMaterialized(result);
        return materializeSnapshotResult(result, wantsReceipts(options));
    }
    async materializeCoordinate(options) {
        const result = await this._materializeController.materializeCoordinate(options);
        return materializeSnapshotResult(result, wantsReceipts(options));
    }
    async materializeAt(checkpointSha) {
        const result = await this._materializeController.materializeAt(checkpointSha);
        await this._onMaterialized(result);
        return createSnapshotWarpState(result.state);
    }
    async _materializeGraph(options = {}) {
        if (canUseCachedMaterializedGraph(options, this._stateDirty, this._materializedGraph)) {
            return this._materializedGraph;
        }
        const result = await this._materializeController.materialize({
            ...(options.ceiling !== undefined ? { ceiling: options.ceiling } : {}),
            wantDiff: this._cachedIndexTree !== null,
        });
        await this._onMaterialized(result);
        if (this._materializedGraph !== null) {
            return this._materializedGraph;
        }
        return await this._materializedGraphFromCachedState();
    }
    async _materializeCoordinateGraph(options) {
        const result = await this._materializeController.materializeCoordinate(options);
        return await this._materializedGraphFromState(result.state);
    }
    async _materializeStrandGraph(strandId, options = {}) {
        const result = await this._strandController._materializeStrandRead(strandId, options);
        return await this._materializedGraphFromState(result.state);
    }
    async _materializedGraphFromState(state) {
        const adjacency = this._buildAdjacency(state);
        const stateHash = await computeStateHash(state, { crypto: this._crypto, codec: this._codec });
        return { state, stateHash, adjacency };
    }
    async _materializedGraphFromCachedState() {
        const state = this._cachedState;
        if (state === null) {
            throw new QueryError(E_NO_STATE_MSG, {
                code: 'E_NO_STATE',
            });
        }
        this._cachedState = state;
        this._stateDirty = false;
        this._versionVector = state.observedFrontier.clone();
        this._materializedGraph = await this._materializedGraphFromState(state);
        return this._materializedGraph;
    }
    _buildAdjacency(state) {
        return buildAdjacency(state);
    }
    async _setMaterializedState(state, optionsOrDiff) {
        const stateHash = await computeStateHash(state, { crypto: this._crypto, codec: this._codec });
        const adjacency = this._buildAdjacency(state);
        const diff = resolveMaterializedStateDiff(optionsOrDiff);
        this._cachedState = state;
        this._stateDirty = false;
        this._versionVector = state.observedFrontier.clone();
        this._materializedGraph = { state, stateHash, adjacency };
        this._buildViewFromResult({ state, stateHash, diff });
        this._notifyAfterMaterialize(state);
        return this._materializedGraph;
    }
    _buildView() {
        // handled by _onMaterialized()
    }
    _resolveCeiling() {
        return null;
    }
    async _persistSeekCacheEntry() {
        // handled by MaterializeController
    }
    async _restoreIndexFromCache() {
        // handled by MaterializeController
    }
    verifyIndex(options) {
        if (this._logicalIndex === null || this._cachedState === null || this._viewService === null) {
            throw new QueryError('Cannot verify index: graph not materialized or index not built', { code: 'E_QUERY_NO_STATE' });
        }
        return this._viewService.verifyIndex({
            state: this._cachedState,
            logicalIndex: this._logicalIndex,
            ...(options !== undefined ? { options } : {}),
        });
    }
    invalidateIndex() {
        this._cachedIndexTree = null;
        this._cachedViewHash = null;
    }
    /**
     * Inspection API: reads one node from the current materialized state.
     *
     * Prefer `worldline().query()` for stable product reads, or
     * `worldline().observer(...).query()` when you need a filtered aperture.
     */
    getNodeProps = (...args) => this._queryController.getNodeProps(...args);
    /**
     * Inspection API: walks visible neighbors from the current materialized state.
     *
     * For application-facing reads, prefer `Observer` query/traverse helpers over direct materialization.
     */
    neighbors = (...args) => this._queryController.neighbors(...args);
    /**
     * Inspection API: enumerates all visible nodes in the current materialized state.
     *
     * Prefer `worldline().query()` for stable product reads, or
     * `worldline().observer(...).query()` when you need a filtered aperture.
     */
    getNodes = (...args) => this._queryController.getNodes(...args);
    /**
     * Inspection API: enumerates all visible edges in the current materialized state.
     */
    getEdges = (...args) => this._queryController.getEdges(...args);
    /**
     * Advanced substrate replay primitive for this pinned source.
     */
    materializeSlice = (...args) => this._provenanceController.materializeSlice(...args);
    async materializeStrand(strandId, options) {
        const result = await this._strandController.materializeStrand(strandId, options);
        if (options?.receipts === true) {
            if ('state' in result) {
                return result;
            }
            throw new WarpError('strand materialization requested receipts but returned plain state', 'E_STRAND_RECEIPTS_MISSING');
        }
        if ('state' in result) {
            throw new WarpError('strand materialization returned receipts without requesting them', 'E_STRAND_RECEIPTS_UNEXPECTED');
        }
        return result;
    }
    createCheckpoint = (...args) => this._checkpointController.createCheckpoint(...args);
    _readCheckpointSha = (...args) => this._checkpointController._readCheckpointSha(...args);
    syncCoverage = (...args) => this._checkpointController.syncCoverage(...args);
    _loadLatestCheckpoint = (...args) => this._checkpointController._loadLatestCheckpoint(...args);
    _loadPatchesSince = (...args) => this._checkpointController._loadPatchesSince(...args);
    _validateMigrationBoundary = (...args) => this._checkpointController._validateMigrationBoundary(...args);
    _hasSchema1Patches = (...args) => this._checkpointController._hasSchema1Patches(...args);
    _maybeRunGC = (...args) => this._checkpointController._maybeRunGC(...args);
    maybeRunGC = (...args) => this._checkpointController.maybeRunGC(...args);
    runGC = (...args) => this._checkpointController.runGC(...args);
    getGCMetrics = (...args) => this._checkpointController.getGCMetrics(...args);
    createPatch = (...args) => this._patchController.createPatch(...args);
    patch = (...args) => this._patchController.patch(...args);
    patchMany = (...args) => this._patchController.patchMany(...args);
    _nextLamport = (...args) => this._patchController._nextLamport(...args);
    _loadPatchChainFromSha = (...args) => this._patchController._loadPatchChainFromSha(...args);
    _loadWriterPatches = (...args) => this._patchController._loadWriterPatches(...args);
    getWriterPatches = (...args) => this._patchController.getWriterPatches(...args);
    _onPatchCommitted = (...args) => this._patchController._onPatchCommitted(...args);
    writer = (...args) => this._patchController.writer(...args);
    _ensureFreshState = (...args) => this._patchController._ensureFreshState(...args);
    _readPatchBlob = (...args) => this._patchController._readPatchBlob(...args);
    discoverWriters = (...args) => this._patchController.discoverWriters(...args);
    discoverTicks = (...args) => this._patchController.discoverTicks(...args);
    join = (...args) => this._patchController.join(...args);
    _frontierEquals = (...args) => this._patchController._frontierEquals(...args);
    createStrand = (...args) => this._strandController.createStrand(...args);
    braidStrand = (...args) => this._strandController.braidStrand(...args);
    getStrand = (...args) => this._strandController.getStrand(...args);
    listStrands = (...args) => this._strandController.listStrands(...args);
    dropStrand = (...args) => this._strandController.dropStrand(...args);
    getStrandPatches = (...args) => this._strandController.getStrandPatches(...args);
    patchesForStrand = (...args) => this._strandController.patchesForStrand(...args);
    createStrandPatch = (...args) => this._strandController.createStrandPatch(...args);
    patchStrand = (...args) => this._strandController.patchStrand(...args);
    queueStrandIntent = (...args) => this._strandController.queueStrandIntent(...args);
    async listStrandIntents(strandId) {
        return [...await this._strandController.listStrandIntents(strandId)];
    }
    tickStrand = (...args) => this._strandController.tickStrand(...args);
    analyzeConflicts = (...args) => this._strandController.analyzeConflicts(...args);
    hasNode = (...args) => this._queryController.hasNode(...args);
    getEdgeProps = (...args) => this._queryController.getEdgeProps(...args);
    getStateSnapshot = (...args) => this._queryController.getStateSnapshot(...args);
    getPropertyCount = (...args) => this._queryController.getPropertyCount(...args);
    query = (...args) => this._queryController.query(...args);
    worldline = (...args) => this._queryController.worldline(...args);
    observer = (...args) => this._queryController.observer(...args);
    translationCost = (...args) => this._queryController.translationCost(...args);
    getContentOid = (...args) => this._queryController.getContentOid(...args);
    getContentMeta = (...args) => this._queryController.getContentMeta(...args);
    getContent = (...args) => this._queryController.getContent(...args);
    getEdgeContentOid = (...args) => this._queryController.getEdgeContentOid(...args);
    getEdgeContentMeta = (...args) => this._queryController.getEdgeContentMeta(...args);
    getEdgeContent = (...args) => this._queryController.getEdgeContent(...args);
    getContentStream = (...args) => this._queryController.getContentStream(...args);
    getEdgeContentStream = (...args) => this._queryController.getEdgeContentStream(...args);
    fork = (...args) => this._forkController.fork(...args);
    createWormhole = (...args) => this._forkController.createWormhole(...args);
    _isAncestor = (...args) => this._forkController._isAncestor(...args);
    _relationToCheckpointHead = (...args) => this._forkController._relationToCheckpointHead(...args);
    _validatePatchAgainstCheckpoint = (...args) => this._forkController._validatePatchAgainstCheckpoint(...args);
    patchesFor = (...args) => this._provenanceController.patchesFor(...args);
    _computeBackwardCone = (...args) => this._provenanceController._computeBackwardCone(...args);
    loadPatchBySha = (...args) => this._provenanceController.loadPatchBySha(...args);
    _loadPatchBySha = (...args) => this._provenanceController._loadPatchBySha(...args);
    _loadPatchesBySha = (...args) => this._provenanceController._loadPatchesBySha(...args);
    _sortPatchesCausally = (...args) => this._provenanceController._sortPatchesCausally(...args);
    subscribe = (...args) => this._subscriptionController.subscribe(...args);
    watch = (...args) => this._subscriptionController.watch(...args);
    _notifySubscribers = (...args) => this._subscriptionController._notifySubscribers(...args);
    buildPatchDivergence = (...args) => this._comparisonController.buildPatchDivergence(...args);
    compareStrand = (...args) => this._comparisonController.compareStrand(...args);
    planStrandTransfer = (...args) => this._comparisonController.planStrandTransfer(...args);
    planCoordinateTransfer = (...args) => this._comparisonController.planCoordinateTransfer(...args);
    compareCoordinates = (...args) => this._comparisonController.compareCoordinates(...args);
    diff = (...args) => this._comparisonController.diff(...args);
    getFrontier = (...args) => this._syncController.getFrontier(...args);
    hasFrontierChanged = (...args) => this._syncController.hasFrontierChanged(...args);
    status = (...args) => this._syncController.status(...args);
    createSyncRequest = (...args) => this._syncController.createSyncRequest(...args);
    processSyncRequest = (...args) => this._syncController.processSyncRequest(...args);
    applySyncResponse = (...args) => this._syncController.applySyncResponse(...args);
    syncNeeded = (...args) => this._syncController.syncNeeded(...args);
    syncWith = (...args) => this._syncController.syncWith(...args);
    serve = (...args) => this._syncController.serve(...args);
    /**
     * Returns the attached seek cache, or null if none is set.
     */
    get seekCache() {
        return this._seekCache;
    }
    /**
     * Attaches a persistent seek cache after construction.
     */
    setSeekCache(cache) {
        this._seekCache = cache;
    }
    /**
     * Builds a SyncTrustGate from a resolved trust configuration.
     */
    _buildTrustGate(config) {
        const verifier = new AuditVerifierService({
            persistence: this._persistence,
            codec: this._codec,
            ...(this._logger ? { logger: this._logger } : {}),
        });
        return new SyncTrustGate({
            trustMode: config.mode,
            ...(this._logger ? { logger: this._logger } : {}),
            trustEvaluator: {
                evaluateWriters: async (writerIds) => {
                    const pin = (typeof config.pin === 'string' && config.pin.length > 0) ? config.pin : undefined;
                    const assessment = await verifier.evaluateTrust(this._graphName, {
                        ...(pin !== undefined ? { pin } : {}),
                        mode: config.mode === 'enforce' ? 'enforce' : 'warn',
                        writerIds,
                    });
                    return this._extractTrustedWriters(assessment);
                },
            },
        });
    }
    /**
     * Extracts trusted writer IDs from a trust assessment result.
     */
    _extractTrustedWriters(assessment) {
        return {
            trusted: new Set(assessment.trust.explanations
                .filter((explanation) => explanation.trusted)
                .map((explanation) => explanation.writerId)),
        };
    }
    /**
     * Extracts the maximum Lamport timestamp from a WarpState.
     */
    _maxLamportFromState(state) {
        let max = 0;
        for (const v of state.observedFrontier.values()) {
            if (v > max) {
                max = v;
            }
        }
        return max;
    }
    /**
     * Opens a multi-writer graph.
     *
     * @example
     * const graph = await RuntimeHost.open({
     *   persistence: gitAdapter,
     *   graphName: 'events',
     *   writerId: 'node-1'
     * });
     */
    static async open(options) {
        return await openRuntimeHost(options);
    }
    /** Gets the graph name. */
    get graphName() {
        return this._graphName;
    }
    /** Gets the writer ID. */
    get writerId() {
        return this._writerId;
    }
    /** Gets the persistence adapter. */
    get persistence() {
        return this._persistence;
    }
    /** Gets the onDeleteWithData policy. */
    get onDeleteWithData() {
        return this._onDeleteWithData;
    }
    /** Gets the current GC policy. */
    get gcPolicy() {
        return this._gcPolicy;
    }
    /**
     * Gets the temporal query interface for CTL*-style temporal operators.
     *
     * Returns a TemporalQuery instance that provides `always` and `eventually`
     * operators for evaluating predicates across the graph's history.
     */
    get temporal() {
        if (!this._temporalQuery) {
            this._temporalQuery = new TemporalQuery({
                loadAllPatches: async () => {
                    const writerIds = await this.discoverWriters();
                    const allPatches = [];
                    for (const wid of writerIds) {
                        const writerPatches = await this._loadWriterPatches(wid);
                        allPatches.push(...writerPatches);
                    }
                    return this._sortPatchesCausally(allPatches);
                },
                loadCheckpoint: async () => {
                    const ck = await this._loadLatestCheckpoint();
                    if (!ck) {
                        return null;
                    }
                    return { state: ck.state, maxLamport: this._maxLamportFromState(ck.state) };
                },
            });
        }
        return this._temporalQuery;
    }
    /**
     * Gets the current provenance index for this graph.
     */
    get provenanceIndex() {
        return this._provenanceIndex;
    }
    /**
     * Callback invoked by MaterializeController after every materialization.
     * Applies all host-level side effects: state caching, index build,
     * subscriber notification, GC, auto-checkpoint, timing.
     */
    // eslint-disable-next-line max-lines-per-function -- side-effect callback must apply all post-materialize state in one method
    async _onMaterialized(result) {
        // 1. Cache state + lamport
        this._cachedState = result.state;
        this._stateDirty = false;
        this._versionVector = result.state.observedFrontier.clone();
        if (result.maxObservedLamport > this._maxObservedLamport) {
            this._maxObservedLamport = result.maxObservedLamport;
        }
        this._materializedGraph = {
            state: result.state,
            stateHash: result.stateHash,
            adjacency: {
                outgoing: new Map(result.adjacency.outgoing),
                incoming: new Map(result.adjacency.incoming),
            },
        };
        this._provenanceIndex = result.provenanceIndex;
        this._provenanceDegraded = result.provenanceDegraded;
        this._cachedCeiling = result.ceiling;
        this._cachedFrontier = result.frontier ? new Map(result.frontier) : null;
        // 2. Build view (index)
        this._buildViewFromResult(result);
        // 3. Side effects (live frontier only)
        if (result.ceiling === null) {
            this._lastFrontier = await this.getFrontier();
            this._patchesSinceCheckpoint = result.patchCount;
            await this._tryAutoCheckpoint(result.patchCount);
            this._maybeRunGC(result.state);
        }
        // 4. Notify subscribers
        this._notifyAfterMaterialize(result.state);
    }
    /**
     * Builds bitmap index from materialized state.
     */
    // eslint-disable-next-line complexity -- index build + fallback + provider attach
    _buildViewFromResult(result) {
        if (this._cachedViewHash === result.stateHash) {
            return;
        }
        try {
            const viewResult = result.diff && this._cachedIndexTree
                ? this._viewService.applyDiff({ existingTree: this._cachedIndexTree, diff: result.diff, state: result.state })
                : this._viewService.build(result.state);
            this._logicalIndex = viewResult.logicalIndex;
            this._propertyReader = viewResult.propertyReader;
            this._cachedViewHash = result.stateHash;
            this._cachedIndexTree = viewResult.tree;
            this._indexDegraded = false;
            if (this._materializedGraph) {
                this._materializedGraph.provider = new BitmapNeighborProvider({ logicalIndex: viewResult.logicalIndex });
            }
        }
        catch (err) {
            this._indexDegraded = true;
            this._logicalIndex = null;
            this._propertyReader = null;
            this._cachedIndexTree = null;
            this._logger?.warn('index build failed', { error: err instanceof Error ? err.message : String(err) });
        }
    }
    async _tryAutoCheckpoint(patchCount) {
        if (!this._checkpointPolicy || this._checkpointing) {
            return;
        }
        if (patchCount < this._checkpointPolicy.every) {
            return;
        }
        try {
            await this.createCheckpoint();
            this._patchesSinceCheckpoint = 0;
        }
        catch { /* non-fatal */ }
    }
    _notifyAfterMaterialize(state) {
        if (this._subscribers.length > 0) {
            const hasPendingReplay = this._subscribers.some((s) => s.pendingReplay === true);
            const delta = diffStates(this._lastNotifiedState, state);
            if (!isEmptyDiff(delta) || hasPendingReplay) {
                this._notifySubscribers(delta, state);
            }
        }
        this._lastNotifiedState = cloneState(state);
    }
}
export async function openRuntimeHost(options) {
    const { options: resolvedOptions } = await resolveRuntimeHostConstructionOptions(options);
    const graph = new RuntimeHost(resolvedOptions);
    await graph._validateMigrationBoundary();
    return graph;
}
