"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultResubmitMachine = void 0;
const internal_1 = require("@fluidframework/core-utils/internal");
const index_js_1 = require("../util/index.js");
/**
 * Default implementation of {@link ResubmitMachine}.
 */
class DefaultResubmitMachine {
    constructor(
    /**
     * Change enricher that represent the tip of the top-level local branch (i.e., the branch on which in-flight
     * commits are applied and automatically rebased).
     */
    enricher) {
        this.enricher = enricher;
        /**
         * Maps from revision of submitted commit to the pending change for that commit.
         */
        this.pendingChanges = new Map();
        /**
         * The current enrichment version for in-flight commits.
         * Incremented when a peer commit is sequenced.
         */
        this.currentEnrichment = 0;
    }
    onCommitSubmitted(commit) {
        this.pendingChanges.set(commit.revision, {
            commit,
            lastEnrichment: this.currentEnrichment,
        });
    }
    onCommitRollback(commit) {
        this.pendingChanges.delete(commit.revision);
    }
    updateEnrichments(revision, getLocalCommits) {
        const pendingChange = this.pendingChanges.get(revision);
        if (pendingChange === undefined ||
            pendingChange.lastEnrichment === this.currentEnrichment) {
            // The first commit to resubmit has a valid enrichment, so all pending commits must be valid.
            return;
        }
        const newCommits = getLocalCommits();
        const staleChanges = [];
        for (const newCommit of newCommits) {
            const pending = this.getPendingChange(newCommit.revision);
            (0, internal_1.assert)(pending !== undefined, 0xbda /* there must be an inflight commit for each resubmit commit */);
            const isStale = pending.lastEnrichment < this.currentEnrichment;
            if (isStale) {
                staleChanges.push({ pending, newCommit });
            }
            else {
                // We have reached a commit with a valid enrichment. Later commits must also be valid.
                break;
            }
        }
        (0, internal_1.assert)((0, index_js_1.hasSome)(staleChanges) && staleChanges[0].newCommit.revision === revision, 0xc79 /* Expected local commits to start with specified revision */);
        const startingState = staleChanges[0].newCommit.parent;
        (0, internal_1.assert)(startingState !== undefined, 0xcc3 /* New commits must have a parent. */);
        // Some in-flight commits have stale enrichments, so we recompute them.
        const enriched = this.enricher.enrich(startingState, newCommits.slice(0, staleChanges.length));
        for (const [index, { pending, newCommit }] of staleChanges.entries()) {
            const enrichedChange = enriched[index];
            (0, internal_1.assert)(enrichedChange !== undefined, 0xcc4 /* Missing enriched commit. */);
            const enrichedCommit = { ...newCommit, change: enrichedChange };
            pending.commit = enrichedCommit;
            pending.lastEnrichment = this.currentEnrichment;
        }
    }
    getEnrichedCommit(revision, getLocalCommitsSince) {
        this.updateEnrichments(revision, getLocalCommitsSince);
        const pendingChange = this.pendingChanges.get(revision);
        return pendingChange?.commit;
    }
    getPendingChange(revision) {
        return (this.pendingChanges.get(revision) ??
            (0, internal_1.fail)(0xc7a /* No pending change stored for this revision */));
    }
    onSequencedCommitApplied(revision, isLocal) {
        // We no longer need to track enrichment for the commit with this revision.
        // Note that we may have a commit for this revision even if this is not a local change,
        // as this client and another peer may have merged the same commit from a shared branch.
        this.pendingChanges.delete(revision);
        if (!isLocal) {
            // A peer commit has been sequenced, invalidating the enrichment of our in-flight commits.
            this.currentEnrichment++;
        }
    }
}
exports.DefaultResubmitMachine = DefaultResubmitMachine;
//# sourceMappingURL=defaultResubmitMachine.js.map