/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { TypedEventEmitter } from "@fluid-internal/client-utils";
import { summarizerClientType } from "./summary/index.js";
/**
 * Base class for DeltaManager proxy that proxy's access to the real DeltaManager.
 *
 * This class allows us to build proxy functionality without actually having to implement all the methods
 * of the DeltaManager.
 */
export class BaseDeltaManagerProxy extends TypedEventEmitter {
    get IDeltaSender() {
        return this;
    }
    get inbound() {
        return this.deltaManager.inbound;
    }
    get outbound() {
        return this.deltaManager.outbound;
    }
    get inboundSignal() {
        return this.deltaManager.inboundSignal;
    }
    get minimumSequenceNumber() {
        return this.deltaManager.minimumSequenceNumber;
    }
    get lastSequenceNumber() {
        return this.deltaManager.lastSequenceNumber;
    }
    get lastMessage() {
        return this.deltaManager.lastMessage;
    }
    get lastKnownSeqNumber() {
        return this.deltaManager.lastKnownSeqNumber;
    }
    get initialSequenceNumber() {
        return this.deltaManager.initialSequenceNumber;
    }
    get hasCheckpointSequenceNumber() {
        return this.deltaManager.hasCheckpointSequenceNumber;
    }
    get clientDetails() {
        return this.deltaManager.clientDetails;
    }
    get version() {
        return this.deltaManager.version;
    }
    get maxMessageSize() {
        return this.deltaManager.maxMessageSize;
    }
    get serviceConfiguration() {
        return this.deltaManager.serviceConfiguration;
    }
    get active() {
        return this.deltaManager.active;
    }
    get readOnlyInfo() {
        return this.deltaManager.readOnlyInfo;
    }
    constructor(deltaManager, overrides) {
        super();
        this.deltaManager = deltaManager;
        this.eventHandlers = {
            onPrepareSend: (messageBuffer) => {
                this.emit("prepareSend", messageBuffer);
            },
            onSubmitOp: (message) => {
                this.emit("submitOp", message);
            },
            onOp: (message, processingTime) => {
                this.emit("op", message, processingTime);
            },
            onPong: (latency) => {
                this.emit("pong", latency);
            },
            onConnect: (details, opsBehind) => {
                this.emit("connect", details, opsBehind);
            },
            onDisconnect: (reason, error) => {
                this.emit("disconnect", reason, error);
            },
            onReadonly: (readonly, readonlyConnectionReason) => {
                this.emit("readonly", readonly, readonlyConnectionReason);
            },
            ...overrides,
        };
        // We are expecting this class to have many listeners, so we suppress noisy "MaxListenersExceededWarning" logging.
        super.setMaxListeners(0);
        this.deltaManager.on("prepareSend", this.eventHandlers.onPrepareSend);
        this.deltaManager.on("submitOp", this.eventHandlers.onSubmitOp);
        this.deltaManager.on("op", this.eventHandlers.onOp);
        this.deltaManager.on("pong", this.eventHandlers.onPong);
        this.deltaManager.on("connect", this.eventHandlers.onConnect);
        this.deltaManager.on("disconnect", this.eventHandlers.onDisconnect);
        this.deltaManager.on("readonly", this.eventHandlers.onReadonly);
    }
    dispose() {
        this.deltaManager.off("prepareSend", this.eventHandlers.onPrepareSend);
        this.deltaManager.off("submitOp", this.eventHandlers.onSubmitOp);
        this.deltaManager.off("op", this.eventHandlers.onOp);
        this.deltaManager.off("pong", this.eventHandlers.onPong);
        this.deltaManager.off("connect", this.eventHandlers.onConnect);
        this.deltaManager.off("disconnect", this.eventHandlers.onDisconnect);
        this.deltaManager.off("readonly", this.eventHandlers.onReadonly);
        this.removeAllListeners();
    }
    submitSignal(content, targetClientId) {
        return this.deltaManager.submitSignal(content, targetClientId);
    }
    flush() {
        return this.deltaManager.flush();
    }
}
/**
 * Proxy to the real IDeltaManager for restricting certain access to layers below container runtime in summarizer clients:
 * - Summarizer client should be read-only to layers below the container runtime to restrict local changes.
 * - Summarizer client should not be active to layers below the container runtime to restrict local changes.
 */
export class DeltaManagerSummarizerProxy extends BaseDeltaManagerProxy {
    static wrapIfSummarizer(deltaManager) {
        if (deltaManager.clientDetails.type === summarizerClientType) {
            return new DeltaManagerSummarizerProxy(deltaManager);
        }
        return deltaManager;
    }
    // eslint-disable-next-line @typescript-eslint/class-literal-property-style
    get active() {
        // Summarize clients should not be active. There shouldn't be any local changes (writes) in the summarizer
        // except for the SummarizeOp which is generated by the runtime.
        return false;
    }
    get readOnlyInfo() {
        // Summarizer clients should be read-only as far as the runtime and layers below are concerned. There shouldn't
        // be any local changes (writes) in the summarizer except for the summarize op which is generated by the runtime.
        return {
            readonly: true,
            forced: false,
            permissions: undefined,
            storageOnly: false,
        };
    }
    constructor(deltaManager) {
        super(deltaManager, { onReadonly: () => { } });
        this.deltaManager = deltaManager;
    }
}
export class DeltaManagerPendingOpsProxy extends BaseDeltaManagerProxy {
    get minimumSequenceNumber() {
        const minPendingSeqNum = this.pendingStateManager.minimumPendingMessageSequenceNumber;
        /**
         * The reason why the minimum pending sequence number can be less than the delta manager's minimum sequence
         * number (DM's msn) is that when we are processing messages in the container runtime/delta manager, the delta
         * manager's msn can be updated to continually increase. In the meantime, the pending state manager's op which
         * hasn't been sent can still have a lower sequence number than the DM's msn (think about a disconnected
         * scenario). To successfully resubmit that pending op it has to be rebased first by the DDS. The DDS still
         * needs to keep the local data for that op that has a reference sequence number lower than the DM's msn. To
         * achieve this, the msn passed to the DDS needs to be the minimum of the DM's msn and the minimum pending
         * sequence number, so that it can keep the relevant local data to generate the right data for the new op
         * during resubmission.
         */
        if (minPendingSeqNum !== undefined &&
            minPendingSeqNum < this.deltaManager.minimumSequenceNumber) {
            return minPendingSeqNum;
        }
        return this.deltaManager.minimumSequenceNumber;
    }
    get lastMessage() {
        if (this.deltaManager.lastMessage === undefined) {
            return this.deltaManager.lastMessage;
        }
        return {
            ...this.deltaManager.lastMessage,
            minimumSequenceNumber: this.minimumSequenceNumber,
        };
    }
    constructor(deltaManager, pendingStateManager) {
        super(deltaManager, {
            onOp: (message, processingTime) => {
                const messageIntercept = {
                    ...message,
                    minimumSequenceNumber: this.minimumSequenceNumber,
                };
                this.emit("op", messageIntercept, processingTime);
            },
        });
        this.deltaManager = deltaManager;
        this.pendingStateManager = pendingStateManager;
    }
}
//# sourceMappingURL=deltaManagerProxies.js.map