import { WebrtcProvider } from 'y-webrtc';
import { WebsocketProvider } from 'y-websocket';

/**
 * The provider awareness property
 */
declare type Awareness = WebsocketProvider['awareness'] | WebrtcProvider['awareness'];
/**
 * Represents presence object that can be associated to a user
 */
interface Presence {
    [key: string]: any;
}
/**
 * Represents a user connected in the room
 */
interface User<T extends Presence = Presence> {
    /**
     * The client id associated to the user
     */
    readonly id: number;
    /**
     * The user presence
     */
    readonly presence: T;
}

/**
 * Wraps the provider's awareness to provide helper methods to listen to changes in self
 * presence, other users' presence and all users' presence.
 *
 * @example
 *
 * interface AppPresence {
 *     name: string;
 * }
 *
 * const initialPresence: AppPresence = { name: "John Doe" }
 *
 * const room = new Room<AppPresence>(provider.awareness, initialPresence)
 *
 * // listen to changes in all users' presence
 * room.subscribe('users', (users) => {
 *     // do something
 * })
 */
declare class Room<T extends Presence = Presence> {
    readonly awareness: Awareness;
    private listeners;
    /**
     * Creates a new instance of a room to manipulate the current user's presence
     * @param awareness the awareness object associated to WebSocketProvider or WebrtcProvider
     * @param initialPresence the initial presence object to assign to the current user
     */
    constructor(awareness: Awareness, initialPresence: T);
    /**
     * Updates only a subset of the current user's presence object
     * @param partial a subset of the current user's presence object to update
     */
    updatePresence(partial: Partial<T>): void;
    /**
     * Overrides the presence object of the current user (or self) in a single transaction
     * @param presence the new presence object to set
     */
    setPresence(presence: T): void;
    /**
     * Returns the current user's id and their presence object
     * @returns the User object associated to the current user (or self)
     */
    getSelf(): User<T>;
    /**
     * Method to retrieve all connected users (excluding self) in the room
     * @returns an array of User object, where each user object is a user connected in the room (excluding self)
     */
    getOthers(): User<T>[];
    /**
     * Method to retrieve all connected users (including self) in the room
     * @returns an array of User object, where each user object is a user connected in the room (including self)
     */
    getUsers(): User<T>[];
    /**
     * Listen to changes in all or other users' presence
     * @param event the event name: "users" or "others"
     * @param callback the function to run whenever presence associated to the provided event changes
     */
    subscribe(event: 'users' | 'others', callback: UsersEventCallback<T>): () => void;
    /**
     * Listen to changes in self presence
     * @param event the event name: "self"
     * @param callback the function to run whenever self presence changes
     */
    subscribe(event: 'self', callback: UserEventCallback<T>): () => void;
    /**
     * Unsubscribe to changes in all or other users' presence
     * @param event the event name: "users" or "others"
     * @param callback the function to unsubscribe
     */
    unsubscribe(event: 'users' | 'others', callback: UsersEventCallback<T>): void;
    /**
     * Unsubscribe to changes in self presence
     * @param event the event name: "self"
     * @param callback the function to unsubscribe
     */
    unsubscribe(event: 'self', callback: UserEventCallback<T>): void;
    /**
     * Sets the current user's presence object to null and destroys the awareness object
     */
    destroy(): void;
}
declare type UsersEventCallback<T extends Presence> = (users: User<T>[]) => void;
declare type UserEventCallback<T extends Presence> = (user: User<T>) => void;

export { Awareness, Presence, Room, User };
