import { ChainSpecData, SubstrateClient, JsonRpcMessage, JsonRpcRequest, JsonRpcProvider } from '@polkadot-api/substrate-client';
import { HexString, BlockHeader, Enum } from 'polkadot-api';
import { Observable, Subscription } from 'rxjs';
import pino from 'pino';

interface Executor {
    runRuntimeCall(params: RuntimeCallParams): Promise<RuntimeCallResult>;
    getRuntimeVersion(code: Uint8Array): Promise<RuntimeVersion>;
    createProof: (nodes: HexString[], updates: [HexString, HexString | null][]) => Promise<[HexString, HexString[]]>;
    decodeProof: (trieRootHash: HexString, nodes: HexString[]) => Promise<[[HexString, HexString]]>;
}
interface RuntimeCallParams {
    storage: {
        code: Uint8Array;
        getValue(key: HexString): Promise<Uint8Array | null>;
        getDescendantKeys(prefix: HexString): Promise<HexString[]>;
    };
    call: string;
    params: HexString;
    storageOverrides?: Record<HexString, HexString | null>;
    mockSignatureHost?: 0 | 1 | 2;
}
interface RuntimeVersion {
    specName: string;
    implName: string;
    authoringVersion: number;
    specVersion: number;
    implVersion: number;
    apis: Array<[HexString, number]>;
    transactionVersion: number;
    stateVersion: number;
}
interface RuntimeCallResult {
    result: HexString;
    storageDiff: Array<[HexString, HexString | null]>;
    offchainStorageDiff: Array<[HexString, HexString | null]>;
    runtimeLogs: string[];
}

interface Source {
    block: Promise<{
        blockHash: HexString;
        header: BlockHeader;
        body: Uint8Array[];
    }>;
    /** Get a single storage value */
    getStorage(key: HexString): Promise<Uint8Array | null>;
    /** Get multiple storage values */
    getStorageBatch(keys: HexString[]): Promise<(Uint8Array | null)[]>;
    /** Get all storage entries under a prefix */
    getStorageDescendants(prefix: HexString): Promise<Record<HexString, Uint8Array>>;
    getChainSpecData(): Promise<ChainSpecData>;
    /** Disconnect from the source */
    destroy(): void;
    archive?: {
        getBody: (hash: HexString) => Promise<Uint8Array[]>;
        getHeader: (hash: HexString) => Promise<BlockHeader>;
        call: (hash: HexString, fnName: string, parameters: HexString) => Promise<HexString>;
        getHashByHeight: (height: number) => Promise<HexString[]>;
        storageSubscription: SubstrateClient["archive"]["storageSubscription"];
    };
}
declare const wsSource: (url: string | string[], options?: {
    atBlock?: number | string;
}) => Source;

interface StorageNode {
    hash: Uint8Array;
    children: Array<StorageNode>;
    value?: Uint8Array | null;
    exhaustive?: boolean;
}

interface Chain {
    blocks$: Observable<Record<HexString, Block>>;
    newBlocks$: Observable<HexString>;
    best$: Observable<HexString>;
    finalized$: Observable<HexString>;
    executor: Executor;
    getBlock: (hash: HexString) => Block | undefined;
    newBlock: (params: CreateBlockParams) => Promise<Block>;
    changeFinalized: (hash: HexString) => void;
    setStorage: (hash: HexString, changes: Record<HexString, Uint8Array | null>) => void;
    getStorage: (hash: HexString, key: HexString) => Promise<StorageNode>;
    getStorageBatch: (hash: HexString, keys: HexString[]) => Promise<StorageNode[]>;
    getStorageDescendants: (hash: HexString, prefix: HexString) => Promise<Record<HexString, StorageNode>>;
    getStorageDiff: (hash: HexString, baseHash?: HexString) => Promise<Record<string, {
        value: Uint8Array | null;
        prev?: Uint8Array | null;
    }>>;
    hrmpChannels: Set<number>;
    openHrmpChannel: (recipientParaId: number) => void;
}

interface CreateBlockParams {
    parent: HexString;
    unsafeBlockHeight?: number;
    transactions: Uint8Array[];
    xcm: XcmMessages;
    storage: Record<HexString, Uint8Array | null>;
    disableOnIdle: boolean;
    mockSignatureHost?: boolean;
}
interface Block {
    hash: HexString;
    parent: HexString;
    code: Uint8Array;
    storageRoot: StorageNode;
    header: BlockHeader;
    runtime: RuntimeVersion;
    body: Uint8Array[];
    hasNewRuntime?: boolean;
    children: HexString[];
}
interface DmpMessage {
    sent_at: number;
    msg: Uint8Array<ArrayBufferLike>;
}
interface XcmMessages {
    dmp: Array<DmpMessage>;
    hrmp: Record<number, Uint8Array[]>;
}

/**
 * This tx pool is simplified: There's just one set of transactions, that get
 * validated against the best block, but fallbacking to others.
 */
interface TxPool {
    addTx: (tx: Uint8Array) => Promise<void>;
    getTxsForBlock: (block: Block) => Promise<Uint8Array[]>;
    destroy: () => void;
    txAdded$: Observable<void>;
}

interface Connection {
    disconnect$: Observable<void>;
    send: (msg: JsonRpcMessage) => void;
    context: {
        chainHead_v1_subs: Record<string, {
            pinnedBlocks: Set<string>;
            operations: Record<string, Subscription>;
            subscription: Subscription;
        }>;
        archive_v1_storage_subs: Record<string, Subscription>;
    };
}
type ServerContext = {
    source: Source;
    chain: Chain;
    provider: JsonRpcProvider;
    txPool: TxPool;
    newBlock: Forklift["newBlock"];
    getOptions: () => ForkliftOptions;
    changeOptions: (opts: Partial<ForkliftOptions>) => void;
    xcm: {
        pushDmp: (messages: Array<DmpMessage>) => void;
        pushUmp: (paraId: number, messages: Array<Uint8Array>) => void;
        pushHrmp: (paraId: number, messages: Array<Uint8Array>) => void;
    };
};
type RpcMethod<T = any> = (con: Connection, req: JsonRpcRequest<T>, ctx: ServerContext) => void;

interface NewBlockOptions {
    unsafeBlockHeight?: number;
    parent: HexString;
    disableOnIdle: boolean;
    storage: CreateBlockParams["storage"];
    mockSignatureHost?: boolean;
    transactions: Uint8Array[];
    finalize?: boolean;
}
interface Forklift {
    serve: JsonRpcProvider;
    newBlock: (opts?: Partial<NewBlockOptions>) => Promise<HexString>;
    changeFinalized: (hash: HexString) => Promise<void>;
    setStorage: (hash: HexString, changes: Record<string, Uint8Array>) => Promise<void>;
    getStorageDiff: (hash: HexString, baseHash?: HexString) => Promise<Record<string, {
        value: Uint8Array | null;
        prev?: Uint8Array | null;
    }>>;
    changeOptions: (options: Partial<Omit<ForkliftOptions, "executor">>) => void;
    destroy: () => void;
}
type DelayMode = Enum<{
    manual: undefined;
    timer: number;
}>;
interface ForkliftOptions {
    buildBlockMode: DelayMode;
    finalizeMode: DelayMode;
    disableOnIdle?: boolean;
    mockSignatureHost?: boolean;
    rpcOverrides: Record<string, RpcMethod | null>;
    executor: Executor;
}
declare function forklift(source: Source, opts?: Partial<ForkliftOptions>): Forklift;

declare const logger: pino.Logger<never, boolean>;

declare const methods: Record<string, RpcMethod>;

declare const fromWorker: (worker: Worker) => Executor;

export { forklift, fromWorker, logger, methods, wsSource };
export type { Forklift, ForkliftOptions, Source };
