import { BehaviorSubject, type Observable } from 'rxjs';
import { Model, Patch } from 'json-joy/lib/json-crdt';
import type { ServerBatch, ServerHistory } from '../../remote/types';
import type { BlockId, LocalRepo, LocalRepoEvent, LocalRepoSyncRequest, LocalRepoSyncResponse, LocalRepoGetResponse, LocalRepoGetRequest, LocalRepoCreateResponse, LocalRepoCreateRequest, LocalRepoGetIfResponse, LocalRepoGetIfRequest, LocalRepoPullResponse } from '../types';
import type { BinStrLevel, BinStrLevelOperation, BlockMeta, SyncResult, LevelLocalRepoPubSub, LevelLocalRepoCursor, BlockSyncRecord } from './types';
import type { CrudLocalRepoCipher } from './types';
import type { Locks } from 'thingies/lib/Locks';
import type { JsonValueCodec } from '@jsonjoy.com/json-pack/lib/codecs/types';
export interface LevelLocalRepoOpts {
    /**
     * Session ID of the user on this device. The same session ID is reused across
     * all tabs.
     */
    readonly sid: number;
    /**
     * Local persistance LevelDB API.
     */
    readonly kv: BinStrLevel;
    /**
     * Optional content encryption/decryption API.
     */
    readonly cipher?: CrudLocalRepoCipher;
    /**
     * Cross-tab locking API.
     */
    readonly locks: Locks;
    /**
     * Optional observable that emits `true` when the device is connected to the
     * server and `false` when it's not.
     */
    readonly connected$?: BehaviorSubject<boolean>;
    /**
     * RPC API for communication with the server.
     */
    readonly rpc: ServerHistory;
    /**
     * Event bus.
     */
    readonly pubsub?: LevelLocalRepoPubSub;
    /**
     * Number of milliseconds after which remote calls are considered timed out.
     */
    readonly remoteTimeout?: number;
    /**
     * Minimum backoff time in milliseconds for the sync loop.
     */
    readonly syncLoopMinBackoff?: number;
    /**
     * Maximum backoff time in milliseconds for the sync loop.
     */
    readonly syncLoopMaxBackoff?: number;
    /**
     * If specified, background sync errors will be emitted here.
     */
    readonly onSyncError?: (error: Error | unknown) => void;
}
export declare class LevelLocalRepo implements LocalRepo {
    protected readonly opts: LevelLocalRepoOpts;
    readonly kv: BinStrLevel;
    readonly locks: Locks;
    readonly sid: number;
    readonly connected$: BehaviorSubject<boolean>;
    protected readonly pubsub: LevelLocalRepoPubSub;
    protected readonly cipher?: CrudLocalRepoCipher;
    protected readonly codec: JsonValueCodec;
    constructor(opts: LevelLocalRepoOpts);
    private _conSub;
    private _stopped;
    stop(): void;
    protected encrypt(blob: Uint8Array, zip: boolean): Promise<Uint8Array>;
    protected decrypt(blob: Uint8Array, zip: boolean): Promise<Uint8Array>;
    protected encode(value: unknown, zip: boolean): Promise<Uint8Array>;
    protected decode(blob: Uint8Array, zip: boolean): Promise<unknown>;
    /** @todo Encrypt collection and key. */
    blockKeyBase(id: BlockId): Promise<string>;
    frontierKeyBase(blockKeyBase: string): string;
    frontierKey(blockKeyBase: string, time: number): string;
    batchKeyBase(blockKeyBase: string): string;
    batchKey(blockKeyBase: string, seq: number): string;
    snapshotKeyBase(blockKeyBase: string): string;
    snapshotKey(blockKeyBase: string, seq: number): string;
    protected syncKey(keyBase: string): string;
    protected _exists(keyBase: string): Promise<boolean>;
    protected _modelWrOp(keyBase: string, model: Uint8Array): Promise<BinStrLevelOperation>;
    protected _metaWrOp(keyBase: string, meta?: BlockMeta): Promise<BinStrLevelOperation>;
    protected _modelWrOps(keyBase: string, model: Uint8Array, meta?: BlockMeta): Promise<BinStrLevelOperation[]>;
    protected _wrModel(keyBase: string, model: Uint8Array, meta?: BlockMeta): Promise<void>;
    protected load(id: BlockId): Promise<{
        model: Model;
        cursor: number;
    }>;
    protected readMeta(keyBase: string): Promise<BlockMeta>;
    readModel0(keyBase: string): Promise<Uint8Array>;
    readModel(keyBase: string): Promise<Model>;
    readFrontierBlobs0(keyBase: string): AsyncGenerator<readonly [string, Uint8Array<ArrayBufferLike>], void, unknown>;
    readFrontier0(keyBase: string): Promise<Patch[]>;
    readFrontierTip(keyBase: string): Promise<Patch | undefined>;
    protected lockBlock<T>(keyBase: string, fn: () => Promise<T>): Promise<T>;
    protected lockBlockForSync<T>(keyBase: string, fn: () => Promise<T>): Promise<T>;
    protected isBlockLockedForSync(keyBase: string): boolean;
    protected markDirty(keyBase: string, id: BlockId): Promise<void>;
    protected markDirtyAndSync(keyBase: string, id: BlockId): Promise<boolean>;
    protected remoteTimeout(): number;
    /**
     * Pushes to remote.
     * @param id Block ID.
     * @param pull Whether to pull if there are no patches to push.
     */
    protected push(keyBase: string, id: BlockId, doPull?: boolean): Promise<boolean>;
    /**
     * Iterates over all blocks marked as dirty.
     */
    protected listDirty(): AsyncIterableIterator<BlockSyncRecord>;
    protected listDirtyBatch(batchSize?: number): AsyncIterableIterator<BlockSyncRecord[]>;
    syncItem(keyBase: string, id: BlockId): Promise<SyncResult>;
    remoteSyncAll(): Promise<SyncResult[]>;
    protected _remoteSyncLoopActive: boolean;
    protected _remoteSyncDelayTimer: unknown;
    protected runRemoteSyncLoop(): void;
    /** ----------------------------------------------------- {@link LocalRepo} */
    create({ id, patches }: LocalRepoCreateRequest): Promise<LocalRepoCreateResponse>;
    get({ id, remote }: LocalRepoGetRequest): Promise<LocalRepoGetResponse>;
    getIf(request: LocalRepoGetIfRequest): Promise<null | LocalRepoGetIfResponse>;
    sync(req: LocalRepoSyncRequest): Promise<LocalRepoSyncResponse>;
    private _syncCreate;
    private _syncMerge;
    protected readLocal0(keyBase: string): Promise<[model: Model, cursor: LevelLocalRepoCursor]>;
    private _syncRead0;
    private _syncRead;
    del(id: BlockId): Promise<void>;
    /**
     * Pull from remote.
     */
    pull(id: BlockId): Promise<LocalRepoPullResponse>;
    protected pullNew(id: BlockId, keyBase: string): Promise<{
        model: Model;
        meta: BlockMeta;
    }>;
    protected pullExisting(id: BlockId, keyBase: string): Promise<{
        model: Model;
        meta: BlockMeta;
    }>;
    change$(id: BlockId): Observable<LocalRepoEvent>;
    private _subs;
    protected _subRemote(id: BlockId): Observable<void>;
    protected _onUpd(id: BlockId, batch: ServerBatch): Promise<void>;
}
