import { ProvenanceIndex } from "../provenance/ProvenanceIndex.js";
import { MaterializePatchSummary } from "./MaterializePatchSummary.js";
export default class MaterializeCoordinateStrategy {
    runtime;
    constructor(runtime) {
        this.runtime = runtime;
    }
    async materialize(opts) {
        if (this.canReturnEmpty(opts)) {
            return await this.runtime.emptyResult(opts.ceiling, opts.frontier);
        }
        const coordinate = this.snapshotCoordinate(opts.frontier, opts.ceiling);
        const cacheResolved = await this.tryResolveSnapshotCache({
            coordinate,
            receipts: opts.receipts,
        });
        if (cacheResolved !== null) {
            return cacheResolved;
        }
        const reduction = await this.reduceFrontierPatches(opts);
        if (reduction.summary.patchCount === 0) {
            return await this.runtime.emptyResult(opts.ceiling, opts.frontier);
        }
        return await this.runtime.buildResult({
            reduced: reduction.reduced,
            summary: reduction.summary,
            degraded: false,
            ceiling: opts.ceiling,
            frontier: opts.frontier,
        });
    }
    async reduceFrontierPatches(opts) {
        return await this.runtime.reducePatchStream(this.runtime.deps.patches.streamForFrontier(opts.frontier, opts.ceiling), undefined, {
            receipts: opts.receipts,
            wantDiff: false,
        });
    }
    canReturnEmpty(opts) {
        return opts.frontier.size === 0 || this.ceilingExcludesAll(opts.ceiling);
    }
    ceilingExcludesAll(ceiling) {
        return ceiling !== null && ceiling <= 0;
    }
    snapshotCoordinate(frontier, ceiling) {
        return {
            frontier,
            ceiling,
        };
    }
    async tryResolveSnapshotCache(opts) {
        const stateCache = this.runtime.deps.getStateCache?.() ?? null;
        if (stateCache === null) {
            return null;
        }
        return await this.tryResolveCachedSnapshot(stateCache, opts);
    }
    async tryResolveCachedSnapshot(stateCache, opts) {
        const exactResult = await this.tryResolveExactSnapshot(stateCache, opts);
        if (exactResult !== null) {
            return exactResult;
        }
        return await this.tryResolvePredecessorSnapshot(stateCache, opts);
    }
    async tryResolveExactSnapshot(stateCache, opts) {
        const exact = await stateCache.getExact(opts.coordinate);
        if (this.canUseSnapshot(exact, opts.receipts)) {
            return await this.snapshotToResult(exact);
        }
        return null;
    }
    async tryResolvePredecessorSnapshot(stateCache, opts) {
        const predecessor = await stateCache.getBestCompatiblePredecessor(opts.coordinate);
        if (!this.canUseSnapshot(predecessor, opts.receipts)) {
            return null;
        }
        const reduction = await this.runtime.reducePatchStream(this.runtime.deps.patches.streamForFrontierSinceCoordinate(opts.coordinate.frontier, opts.coordinate.ceiling, predecessor.coordinate), predecessor.state, {
            receipts: false,
            wantDiff: false,
        });
        return await this.runtime.buildResult({
            reduced: reduction.reduced,
            summary: reduction.summary,
            degraded: predecessor.provenancePosture === 'degraded',
            ceiling: opts.coordinate.ceiling,
            frontier: opts.coordinate.frontier,
        });
    }
    canUseSnapshot(snapshot, receipts) {
        if (snapshot === null || snapshot.state === undefined) {
            return false;
        }
        if (receipts && snapshot.provenancePosture === 'degraded') {
            return false;
        }
        return true;
    }
    async snapshotToResult(snapshot) {
        return await this.runtime.buildResult({
            reduced: { state: snapshot.state },
            summary: MaterializePatchSummary.empty(new ProvenanceIndex()),
            degraded: snapshot.provenancePosture === 'degraded',
            ceiling: snapshot.coordinate.ceiling,
            frontier: snapshot.coordinate.frontier,
        });
    }
}
