import { ReconnectionStrategy } from '@fuman/net';
import { Middleware, Deferred } from '@fuman/utils';
import { TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime';
import { default as Long } from 'long';
import { StorageManager } from '../storage/storage.js';
import { mtp, tl } from '../tl/index.js';
import { ICorePlatform } from '../types/platform.js';
import { DcOptions, ICryptoProvider, Logger } from '../utils/index.js';
import { ConfigManager } from './config-manager.js';
import { TelegramTransport } from './transports/abstract.js';
import { MultiSessionConnection } from './multi-session-connection.js';
export type ConnectionKind = 'main' | 'upload' | 'download' | 'downloadSmall';
/**
 * Params passed into {@link NetworkManager} by {@link TelegramClient}.
 * This type is intended for internal usage only.
 */
export interface NetworkManagerParams {
    storage: StorageManager;
    crypto: ICryptoProvider;
    log: Logger;
    platform: ICorePlatform;
    apiId: number;
    initConnectionOptions?: Partial<Omit<tl.RawInitConnectionRequest, 'apiId' | 'query'>>;
    transport: TelegramTransport;
    reconnectionStrategy?: ReconnectionStrategy;
    disableUpdates?: boolean;
    testMode: boolean;
    layer: number;
    useIpv6: boolean;
    readerMap: TlReaderMap;
    writerMap: TlWriterMap;
    isPremium: boolean;
    emitError: (err: Error) => void;
    onUpdate: (upd: tl.TypeUpdates, fromClient: boolean) => void;
    onUsable: () => void;
    onConnecting: () => void;
    onNetworkChanged: (connected: boolean) => void;
    stopSignal: AbortSignal;
}
export type ConnectionCountDelegate = (kind: ConnectionKind, dcId: number, isPremium: boolean) => number;
/**
 * Additional params passed into {@link NetworkManager} by the user
 * that customize the behavior of the manager
 */
export interface NetworkManagerExtraParams {
    /**
     * Whether to use PFS (Perfect Forward Secrecy) for all connections.
     * This is disabled by default
     */
    usePfs?: boolean;
    /**
     * Connection count for each connection kind.
     * The function should be pure to avoid unexpected behavior.
     *
     * Defaults to TDLib logic:
     *   - main: 0 (which stands for "handle internally, based on tmp_sessions value")
     *   - upload: if premium or dc id is other than 2 or 4, then 8, otherwise 4
     *   - download: if premium then 8, otherwise 2
     *   - downloadSmall: 2
     *
     * Non-zero value for `main` is **for advanced users only**
     * as it may lead to unexpected behavior, and is generally not recommended
     * because of unnecessary extra load on both the server and the client as well as
     * increased possibility of encountering AUTH_KEY_DUPLICATED errors.
     */
    connectionCount?: ConnectionCountDelegate;
    /**
     * Idle timeout for non-main connections, in ms
     *
     * @default  60000 (60 seconds).
     */
    inactivityTimeout?: number;
    /**
     * List of middlewares to use for the network manager
     *
     * > **Note**: these middlewares apply to **outgoing requests only**.
     * > If you need to handle incoming updates, use a {@link Dispatcher} instead.
     */
    middlewares?: Middleware<RpcCallMiddlewareContext, unknown>[];
    /**
     * Ping interval in milliseconds.
     *
     * @default  60000 (1 minute)
     */
    pingInterval?: number;
}
/** Options that can be customized when making an RPC call */
export interface RpcCallOptions {
    /**
     * If the call results in a `FLOOD_WAIT_X` error,
     * the maximum amount of time to wait before retrying.
     *
     * If set to `0`, the call will not be retried.
     *
     * Only applies when the flood waiter middleware is enabled.
     */
    floodSleepThreshold?: number;
    /**
     * If the call results in an internal server error or a flood wait,
     * the maximum amount of times to retry the call.
     *
     * Only applies when the flood waiter middleware and/or
     * internal errors handler middleware is enabled.
     */
    maxRetryCount?: number;
    /**
     * Timeout for the call, in milliseconds.
     *
     * @default Infinity
     */
    timeout?: number;
    /**
     * **ADVANCED**
     *
     * Kind of connection to use for this call.
     *
     * @default 'main'
     */
    kind?: ConnectionKind;
    /**
     * **ADVANCED**
     *
     * ID of the DC to use for this call
     */
    dcId?: number;
    /**
     * **ADVANCED**
     *
     * DC connection manager to use for this call.
     * Overrides `dcId` if set, unless `businessConnectionId` is passed.
     */
    manager?: DcConnectionManager;
    /**
     * Abort signal for the call.
     */
    abortSignal?: AbortSignal;
    /**
     * Whether we should not retry on -503 errors and throw {@link MtTimeoutError} immediately instead.
     *
     * Useful for methods like `messages.getBotCallbackAnswer` that reliably return
     * -503 in case the upstream bot failed to respond.
     *
     * Only applies if the internal error handler middleware is enabled,
     * otherwise -503 is always thrown.
     */
    throw503?: boolean;
    /**
     * Whether the `X_MIGRATE_%d` errors should be handled locally on request level
     * instead of changing the default datacenter for the entire client.
     *
     * Useful for `invokeWithBusinessConnection`, as it returns a `USER_MIGRATE_%d` error
     * that is in fact not related to the user, but to the specific request.
     */
    localMigrate?: boolean;
    /**
     * Business connection on behalf of which this call should be made.
     *
     * This wraps the request with `invokeWithBusinessConnection`,
     * resolves the correct DC automatically, and overrides `dcId`,
     * `manager` and `localMigrate`.
     */
    businessConnectionId?: string;
    /**
     * Some requests should be processed consecutively, and not in parallel.
     * Using the same `chainId` for multiple requests will ensure that they are processed in the order
     * of calling `.call()`.
     *
     * Particularly useful for `messages.sendMessage` and alike.
     */
    chainId?: string | number;
}
export interface RpcCallMiddlewareContext {
    request: tl.RpcMethod;
    manager: NetworkManager;
    params?: RpcCallOptions;
}
export type RpcCallMiddleware<Result = unknown> = Middleware<RpcCallMiddlewareContext, Result>;
/**
 * Wrapper over all connection pools for a single DC.
 */
export declare class DcConnectionManager {
    /** Network manager instance */
    readonly manager: NetworkManager;
    /** DC ID */
    readonly dcId: number;
    /** DC options to use */
    readonly _dcs: DcOptions;
    /** Whether this DC is the primary one */
    isPrimary: boolean;
    private _salts;
    private _log;
    /** Main connection pool */
    main: MultiSessionConnection;
    /** Upload connection pool */
    upload: MultiSessionConnection;
    /** Download connection pool */
    download: MultiSessionConnection;
    /** Download connection pool (for small files) */
    downloadSmall: MultiSessionConnection;
    private get _mainCountOverride();
    constructor(
    /** Network manager instance */
    manager: NetworkManager, 
    /** DC ID */
    dcId: number, 
    /** DC options to use */
    _dcs: DcOptions, 
    /** Whether this DC is the primary one */
    isPrimary?: boolean);
    private _setupMulti;
    setIsPrimary(isPrimary: boolean): void;
    setIsPremium(isPremium: boolean): void;
    loadKeys(forcePfs?: boolean): Promise<boolean>;
    setMainConnectionCount(count: number): void;
    destroy(): Promise<void>;
}
/**
 * Class that manages all connections to Telegram servers.
 */
export declare class NetworkManager {
    readonly params: NetworkManagerParams & NetworkManagerExtraParams;
    readonly config: ConfigManager;
    readonly _log: Logger;
    readonly _storage: StorageManager;
    readonly _initConnectionParams: tl.RawInitConnectionRequest;
    readonly _transport: TelegramTransport;
    readonly _reconnectionStrategy: ReconnectionStrategy;
    readonly _connectionCount: ConnectionCountDelegate;
    protected readonly _dcConnections: Map<number, DcConnectionManager>;
    protected _primaryDc?: DcConnectionManager;
    protected _primaryDcRecreationPromise?: Deferred<void>;
    private _updateHandler;
    constructor(params: NetworkManagerParams & NetworkManagerExtraParams, config: ConfigManager);
    private _findDcOptions;
    private _resetOnNetworkChange?;
    private _switchPrimaryDc;
    private _dcCreationPromise;
    _getOtherDc(dcId: number): Promise<DcConnectionManager>;
    /**
     * Perform initial connection to the default DC
     *
     * @param defaultDcs  Default DCs to connect to
     */
    connect(defaultDcs: DcOptions): Promise<void>;
    private _pendingExports;
    private _exportAuthTo;
    setIsPremium(isPremium: boolean): void;
    notifyLoggedIn(auth: tl.auth.TypeAuthorization | tl.RawUser): tl.RawUser;
    notifyLoggedOut(): void;
    notifyNetworkChanged(connected: boolean): void;
    resetSessions(): void;
    private _onConfigChanged;
    changePrimaryDc(newDc: number): Promise<void>;
    readonly call: <T extends tl.RpcMethod>(message: T, params?: RpcCallOptions) => Promise<tl.RpcCallReturn[T['_']] | mtp.RawMt_rpc_error>;
    private _businessConnectionDcs;
    private _getBusinessConnectionDcId;
    private _composeCall;
    private _call;
    changeTransport(transport: TelegramTransport): Promise<void>;
    getPoolSize(kind: ConnectionKind, dcId?: number): number;
    getPrimaryDcId(): number;
    destroy(): Promise<void>;
    getMtprotoMessageId(): Long;
    recreateDc(dcId: number): Promise<void>;
}
