import type { ReadResponse, UserResponse } from '../types';
type MessageId = string;
export type MsgRef = {
    timestampMs: number;
    msgId: MessageId;
};
export type OwnMessageReceiptsTrackerMessageLocator = (timestampMs: number) => MsgRef | null;
export type UserProgress = {
    user: UserResponse;
    lastReadRef: MsgRef;
    lastDeliveredRef: MsgRef;
};
export type OwnMessageReceiptsTrackerOptions = {
    locateMessage: OwnMessageReceiptsTrackerMessageLocator;
};
/**
 * MessageReceiptsTracker
 * --------------------------------
 * Tracks **other participants’** delivery/read progress toward **own (outgoing) messages**
 * within a **single timeline** (one channel/thread).
 *
 * How it works
 * ------------
 * - Each user has a compact progress record:
 *     - `lastReadRef`: latest message they have **read**
 *     - `lastDeliveredRef`: latest message they have **received** (always `>= lastReadRef`)
 * - Internally keeps two arrays sorted **ascending by timestamp**:
 *     - `readSorted` (by `lastReadRef`)
 *     - `deliveredSorted` (by `lastDeliveredRef`)
 * - Queries like “who read message M?” become a **binary search + suffix slice**.
 *
 * Construction
 * ------------
 * `new MessageReceiptsTracker({locateMessage})`
 * - `locateMessage(timestamp) => MsgRef | null` must resolve a message ref representation - `{ timestamp, msgId }`.
 *   - If `locateMessage` returns `null`, the event is ignored (message unknown locally).
 *
 * Event ingestion
 * ---------------
 * - `ingestInitial(rows: ReadResponse[])`: Builds initial state from server snapshot.
 *   If a user’s `last_read` is ahead of `last_delivered_at`, the tracker enforces
 *   the invariant `lastDeliveredRef >= lastReadRef`.
 * - `onMessageRead(user, readAtISO)`:
 *   Advances the user’s read; also bumps delivered to match if needed.
 * - `onMessageDelivered(user, deliveredAtISO)`:
 *   Advances the user’s delivered to `max(currentRead, deliveredAt)`.
 *
 * Queries
 * -------
 * - `readersForMessage(msgRef)         : UserResponse[]` → users with `lastReadRef >= msgRef`
 * - `deliveredForMessage(msgRef)       : UserResponse[]` → users with `lastDeliveredRef >= msgRef`
 * - `deliveredNotReadForMessage(msgRef): UserResponse[]` → delivered but `lastReadRef < msgRef`
 * - `usersWhoseLastReadIs              : UserResponse[]` →  users for whom `msgRef` is their *last read* (exact match)
 * - `usersWhoseLastDeliveredIs         : UserResponse[]` →  users for whom `msgRef` is their *last delivered* (exact match)
 * - `groupUsersByLastReadMessage       : Record<MsgId, UserResponse[]> → mapping of messages to their readers
 * - `groupUsersByLastDeliveredMessage  : Record<MsgId, UserResponse[]> → mapping of messages to their receivers
 * - `hasUserRead(msgRef, userId)       : boolean`
 * - `hasUserDelivered(msgRef, userId)  : boolean`
 *
 * Complexity
 * ----------
 * - Update on read/delivered: **O(log U)** (binary search + one splice) per event, where U is count of users stored by tracker.
 * - Query lists: **O(log U + K)** where `K` is the number of returned users (suffix length).
 * - Memory: **O(U)** - tracker’s memory grows linearly with the number of users in the channel/thread and does not depend on the number of messages.
 *
 * Scope & notes
 * -------------
 * - One tracker instance is **scoped to a single timeline**. Instantiate per channel/thread.
 * - Ordering is by **ascending timestamp**; ties are kept stable by inserting at the end of the
 *   equal-timestamp plateau (upper-bound insertion), preserving intuitive arrival order.
 * - This tracker models **others’ progress toward own messages**;
 */
export declare class MessageReceiptsTracker {
    private byUser;
    private readSorted;
    private deliveredSorted;
    private locateMessage;
    constructor({ locateMessage }: OwnMessageReceiptsTrackerOptions);
    /** Build initial state from server snapshots (single pass + sort). */
    ingestInitial(responses: ReadResponse[]): void;
    /** message.delivered — user device confirmed delivery up to and including messageId. */
    onMessageDelivered({ user, deliveredAt, lastDeliveredMessageId, }: {
        user: UserResponse;
        deliveredAt: string;
        lastDeliveredMessageId?: string;
    }): void;
    /** message.read — user read up to and including messageId. */
    onMessageRead({ user, readAt, lastReadMessageId, }: {
        user: UserResponse;
        readAt: string;
        lastReadMessageId?: string;
    }): void;
    /** notification.mark_unread — user marked messages unread starting at `first_unread_message_id`.
     * Sets lastReadRef to the event’s last_read_* values. Delivery never moves backward.
     * The event is sent only to the user that triggered the action (own user), so we will never adjust read ref
     * for other users - we will not see changes in the UI for other users. However, this implementation does not
     * take into consideration this fact and is ready to handle the mark-unread event for any user.
     */
    onNotificationMarkUnread({ user, lastReadAt, lastReadMessageId, }: {
        user: UserResponse;
        lastReadAt?: string;
        lastReadMessageId?: string;
    }): void;
    /** All users who READ this message. */
    readersForMessage(msgRef: MsgRef): UserResponse[];
    /** All users who have it DELIVERED (includes readers). */
    deliveredForMessage(msgRef: MsgRef): UserResponse[];
    /** Users who delivered but have NOT read. */
    deliveredNotReadForMessage(msgRef: MsgRef): UserResponse[];
    /** Users for whom `msgRef` is their *last read* (exact match). */
    usersWhoseLastReadIs(msgRef: MsgRef): UserResponse[];
    /** Users for whom `msgRef` is their *last delivered* (exact match). */
    usersWhoseLastDeliveredIs(msgRef: MsgRef): UserResponse[];
    hasUserRead(msgRef: MsgRef, userId: string): boolean;
    hasUserDelivered(msgRef: MsgRef, userId: string): boolean;
    getUserProgress(userId: string): UserProgress | null;
    groupUsersByLastReadMessage(): Record<MessageId, UserResponse[]>;
    groupUsersByLastDeliveredMessage(): Record<MessageId, UserResponse[]>;
    private ensureUser;
}
export {};
