/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { TypedEventEmitter } from "@fluid-internal/client-utils";
import type { IDeltaManager } from "@fluidframework/container-definitions/internal";
import type { IEvent, IEventProvider, ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
import type { IClient, IQuorumClients } from "@fluidframework/driver-definitions";
import { type ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
export type ImmutablePrimitives = undefined | null | boolean | string | number | Function;
export type Immutable<T> = T extends ImmutablePrimitives ? T : T extends (infer A)[] ? readonly Immutable<A>[] : T extends Map<infer K, infer V> ? ReadonlyMap<Immutable<K>, Immutable<V>> : T extends Set<infer V> ? ReadonlySet<Immutable<V>> : {
    readonly [K in keyof T]: Immutable<T[K]>;
};
/**
 * Minimum information for a client tracked for election consideration.
 */
export interface ITrackedClient {
    readonly clientId: string;
    readonly sequenceNumber: number;
    readonly client: Immutable<IClient>;
}
/**
 * Common contract for link nodes within an OrderedClientCollection.
 */
export interface ILinkNode {
    readonly sequenceNumber: number;
    youngerClient: ILinkedClient | undefined;
}
/**
 * Placeholder root node within an OrderedClientCollection; does not represent a client.
 */
export interface IRootLinkNode extends ILinkNode {
    readonly sequenceNumber: -1;
    readonly olderClient: undefined;
}
/**
 * Additional information required to keep track of the client within the doubly-linked list.
 */
export interface ILinkedClient extends ILinkNode, ITrackedClient {
    olderClient: LinkNode;
}
/**
 * Any link node within OrderedClientCollection including the placeholder root node.
 */
export type LinkNode = IRootLinkNode | ILinkedClient;
/**
 * Events raised by an OrderedClientCollection.
 */
export interface IOrderedClientCollectionEvents extends IEvent {
    /**
     * Event fires when client is being added.
     */
    (event: "addClient" | "removeClient", listener: (client: ILinkedClient, sequenceNumber: number) => void): any;
}
/**
 * Contract for a sorted collection of all clients in the quorum.
 */
export interface IOrderedClientCollection extends IEventProvider<IOrderedClientCollectionEvents> {
    /**
     * Count of clients in the collection.
     */
    readonly count: number;
    /**
     * Pointer to the oldest client in the collection.
     */
    readonly oldestClient: ILinkedClient | undefined;
    /**
     * Returns a sorted array of all the clients in the collection.
     */
    getAllClients(): ILinkedClient[];
}
/**
 * Tracks clients in the Quorum. It maintains their order using their join op
 * sequence numbers.
 * Internally, the collection of clients is maintained in a doubly-linked list,
 * with pointers to both the first and last nodes.
 * The first (root) node is a placeholder to simplify logic and reduce null checking.
 */
export declare class OrderedClientCollection extends TypedEventEmitter<IOrderedClientCollectionEvents> implements IOrderedClientCollection {
    /**
     * Collection of ALL clients currently in the quorum, with client ids as keys.
     */
    private readonly clientMap;
    /**
     * Placeholder head node of linked list, for simplified null checking.
     */
    private readonly rootNode;
    /**
     * Pointer to end of linked list, for optimized client adds.
     */
    private _youngestClient;
    private readonly logger;
    get count(): number;
    get oldestClient(): ILinkedClient | undefined;
    constructor(logger: ITelemetryBaseLogger, deltaManager: Pick<IDeltaManager<unknown, unknown>, "lastSequenceNumber">, quorum: Pick<IQuorumClients, "getMembers" | "on">);
    private addClient;
    private removeClient;
    /**
     * Returns an array of all clients being tracked in order from oldest to newest.
     */
    getAllClients(): ILinkedClient[];
}
/**
 * Events raised by an OrderedClientElection.
 */
export interface IOrderedClientElectionEvents extends IEvent {
    /**
     * Event fires when the currently elected client changes.
     */
    (event: "election", listener: (
    /**
     * Newly elected client.
     */
    client: ITrackedClient | undefined, 
    /**
     * Sequence number where election took place.
     */
    sequenceNumber: number, 
    /**
     * Previously elected client.
     */
    prevClient: ITrackedClient | undefined) => void): any;
}
/**
 * Serialized state of IOrderedClientElection.
 * @internal
 */
export interface ISerializedElection {
    /**
     * Sequence number at the time of the latest election.
     */
    readonly electionSequenceNumber: number;
    /**
     * Most recently elected client id. This is either:
     *
     * 1. the interactive elected parent client, in which case electedClientId === electedParentId,
     * and the SummaryManager on the elected client will spawn a summarizer client, or
     *
     * 2. the non-interactive summarizer client itself.
     */
    readonly electedClientId: string | undefined;
    /**
     * Most recently elected parent client id. This is always an interactive client.
     */
    readonly electedParentId: string | undefined;
}
/**
 * Contract for maintaining a deterministic client election based on eligibility.
 */
export interface IOrderedClientElection extends IEventProvider<IOrderedClientElectionEvents> {
    /**
     * Count of eligible clients in the collection.
     */
    readonly eligibleCount: number;
    /**
     * Currently elected client. This is either:
     *
     * 1. the interactive elected parent client, in which case electedClientId === electedParentId,
     * and the SummaryManager on the elected client will spawn a summarizer client, or
     *
     * 2. the non-interactive summarizer client itself.
     */
    readonly electedClient: ITrackedClient | undefined;
    /**
     * Currently elected parent client. This is always an interactive client.
     */
    readonly electedParent: ITrackedClient | undefined;
    /**
     * Sequence number of most recent election.
     */
    readonly electionSequenceNumber: number;
    /**
     * Resets the currently elected client back to the oldest eligible client.
     */
    resetElectedClient(sequenceNumber: number): void;
    /**
     * Peeks at what the next elected client would be if incrementElectedClient were called.
     */
    peekNextElectedClient(): ITrackedClient | undefined;
    /**
     * Returns a sorted array of all the eligible clients in the collection.
     */
    getAllEligibleClients(): ITrackedClient[];
    /**
     * Serialize election data
     */
    serialize(): ISerializedElection;
}
/**
 * Adapter for OrderedClientCollection, with the purpose of deterministically maintaining
 * a currently elected client, excluding ineligible clients, in a distributed fashion.
 * This can be true as long as incrementElectedClient and resetElectedClient calls
 * are called under the same conditions for all clients.
 */
export declare class OrderedClientElection extends TypedEventEmitter<IOrderedClientElectionEvents> implements IOrderedClientElection {
    private readonly logger;
    private readonly orderedClientCollection;
    private readonly isEligibleFn;
    private readonly recordPerformanceEvents;
    private _eligibleCount;
    private _electedClient;
    private _electedParent;
    private _electionSequenceNumber;
    get eligibleCount(): number;
    get electionSequenceNumber(): number;
    /**
     * OrderedClientCollection tracks electedClient and electedParent separately. This allows us to handle the case
     * where a new interactive parent client has been elected, but the summarizer is still doing work, so
     * a new summarizer should not yet be spawned. In this case, changing electedParent will cause SummaryManager
     * to stop the current summarizer, but a new summarizer will not be spawned until the old summarizer client has
     * left the quorum.
     *
     * Details:
     *
     * electedParent is the interactive client that has been elected to spawn a summarizer. It is typically the oldest
     * eligible interactive client in the quorum. Only the electedParent is permitted to spawn a summarizer.
     * Once elected, this client will remain the electedParent until it leaves the quorum or the summarizer that
     * it spawned stops producing summaries, at which point a new electedParent will be chosen.
     *
     * electedClient is the non-interactive summarizer client if one exists. If not, then electedClient is equal to
     * electedParent. If electedParent === electedClient, this is the signal for electedParent to spawn a new
     * electedClient. Once a summarizer client becomes electedClient, a new summarizer will not be spawned until
     * electedClient leaves the quorum.
     *
     * A typical sequence looks like this:
     *
     * i. Begin by electing A. electedParent === A, electedClient === A.
     *
     * ii. SummaryManager running on A spawns a summarizer client, A'. electedParent === A, electedClient === A'
     *
     * iii. A' stops producing summaries. A new parent client, B, is elected. electedParent === B, electedClient === A'
     *
     * iv. SummaryManager running on A detects the change to electedParent and tells the summarizer to stop, but A'
     * is in mid-summarization. No new summarizer is spawned, as electedParent !== electedClient.
     *
     * v. A' completes its summary, and the summarizer and backing client are torn down.
     *
     * vi. A' leaves the quorum, and B takes its place as electedClient. electedParent === B, electedClient === B
     *
     * vii. SummaryManager running on B spawns a summarizer client, B'. electedParent === B, electedClient === B'
     */
    get electedClient(): ILinkedClient | undefined;
    get electedParent(): ILinkedClient | undefined;
    constructor(logger: ITelemetryLoggerExt, orderedClientCollection: IOrderedClientCollection, 
    /**
     * Serialized state from summary or current sequence number at time of load if new.
     */
    initialState: ISerializedElection | number, isEligibleFn: (c: ITrackedClient) => boolean, recordPerformanceEvents?: boolean);
    /**
     * Tries changing the elected client, raising an event if it is different.
     * Note that this function does no eligibility or suitability checks. If we get here, then
     * we will set _electedClient, and we will set _electedParent if this is an interactive client.
     */
    private tryElectingClient;
    private tryElectingParent;
    /**
     * Helper function to find the first eligible parent client starting with the passed in client,
     * or undefined if none are eligible.
     * @param client - client to start checking
     * @returns oldest eligible client starting with passed in client or undefined if none.
     */
    private findFirstEligibleParent;
    /**
     * Updates tracking for when a new client is added to the collection.
     * Will automatically elect that new client if none is elected currently.
     * @param client - client added to the collection
     * @param sequenceNumber - sequence number when client was added
     */
    private addClient;
    /**
     * Updates tracking for when an existing client is removed from the collection.
     * Will automatically elect next oldest client if currently elected is removed.
     * @param client - client removed from the collection
     * @param sequenceNumber - sequence number when client was removed
     */
    private removeClient;
    getAllEligibleClients(): ITrackedClient[];
    /**
     * (Re-)start election with the oldest client in the quorum. This is called if we need to summarize
     * and no client has been elected.
     */
    resetElectedClient(sequenceNumber: number): void;
    peekNextElectedClient(): ITrackedClient | undefined;
    serialize(): ISerializedElection;
    private sendPerformanceEvent;
}
//# sourceMappingURL=orderedClientElection.d.ts.map