import { ClockTimer as Clock } from '@colyseus/timer';
import type { Presence } from './presence/Presence.ts';
import type { Serializer } from './serializer/Serializer.ts';
import { type Type, Deferred } from './utils/Utils.ts';
import { type AuthContext, type Client, ClientArray, type ISendOptions, type MessageArgs } from './Transport.ts';
import { type RoomMethodName, type RoomException } from './errors/RoomExceptions.ts';
import { type StandardSchemaV1 } from './utils/StandardSchema.ts';
import { type MessageHandlerWithFormat as SharedMessageHandlerWithFormat, type MessageHandler as SharedMessageHandler, type Messages as SharedMessages } from '@colyseus/shared-types';
export declare const DEFAULT_SEAT_RESERVATION_TIME: number;
export type SimulationCallback = (deltaTime: number) => void;
export interface RoomOptions {
    state?: object;
    metadata?: any;
    client?: Client;
}
export type ExtractRoomState<T> = T extends {
    state?: infer S extends object;
} ? S : any;
export type ExtractRoomMetadata<T> = T extends {
    metadata?: infer M;
} ? M : any;
export type ExtractRoomClient<T> = T extends {
    client?: infer C extends Client;
} ? C : Client;
export interface IBroadcastOptions extends ISendOptions {
    except?: Client | Client[];
}
/**
 * Message handler with automatic type inference from format schema.
 * When a format is provided, the message type is automatically inferred from the schema.
 */
export type MessageHandlerWithFormat<T extends StandardSchemaV1 = any, This = any> = SharedMessageHandlerWithFormat<T, Client, This>;
export type MessageHandler<This = any> = SharedMessageHandler<Client, This>;
/**
 * A map of message types to message handlers.
 */
export type Messages<This extends Room> = SharedMessages<This, Client>;
/**
 * Helper function to create a validated message handler with automatic type inference.
 *
 * @example
 * ```typescript
 * messages = {
 *   move: validate(z.object({ x: z.number(), y: z.number() }), (client, message) => {
 *     // message.x and message.y are automatically typed as numbers
 *     console.log(message.x, message.y);
 *   })
 * }
 * ```
 */
export declare function validate<T extends StandardSchemaV1, This = any>(format: T, handler: (this: This, client: Client, message: StandardSchemaV1.InferOutput<T>) => void): MessageHandlerWithFormat<T, This>;
export declare const RoomInternalState: {
    readonly CREATING: 0;
    readonly CREATED: 1;
    readonly DISPOSING: 2;
};
export type RoomInternalState = (typeof RoomInternalState)[keyof typeof RoomInternalState];
export type OnCreateOptions<T extends Type<Room>> = Parameters<NonNullable<InstanceType<T>['onCreate']>>[0];
/**
 * A Room class is meant to implement a game session, and/or serve as the communication channel
 * between a group of clients.
 *
 * - Rooms are created on demand during matchmaking by default
 * - Room classes must be exposed using `.define()`
 *
 * @example
 * ```typescript
 * class MyRoom extends Room<{
 *   state: MyState,
 *   metadata: { difficulty: string },
 *   client: MyClient
 * }> {
 *   // ...
 * }
 * ```
 */
export declare class Room<T extends RoomOptions = RoomOptions> {
    #private;
    '~client': ExtractRoomClient<T>;
    '~state': ExtractRoomState<T>;
    '~metadata': ExtractRoomMetadata<T>;
    /**
     * This property will change on these situations:
     * - The maximum number of allowed clients has been reached (`maxClients`)
     * - You manually locked, or unlocked the room using lock() or `unlock()`.
     *
     * @readonly
     */
    get locked(): boolean;
    /**
     * Get the room's matchmaking metadata.
     */
    get metadata(): ExtractRoomMetadata<T>;
    /**
     * Set the room's matchmaking metadata.
     *
     * **Note**: This setter does NOT automatically persist. Use `setMatchmaking()` for automatic persistence.
     *
     * @example
     * ```typescript
     * class MyRoom extends Room<{ metadata: { difficulty: string; rating: number } }> {
     *   async onCreate() {
     *     this.metadata = { difficulty: "hard", rating: 1500 };
     *   }
     * }
     * ```
     */
    set metadata(meta: ExtractRoomMetadata<T>);
    /**
     * The room listing cache for matchmaking.
     * @internal
     */
    private _listing;
    /**
     * Timing events tied to the room instance.
     * Intervals and timeouts are cleared when the room is disposed.
     */
    clock: Clock;
    /**
     * Maximum number of clients allowed to connect into the room. When room reaches this limit,
     * it is locked automatically. Unless the room was explicitly locked by you via `lock()` method,
     * the room will be unlocked as soon as a client disconnects from it.
     */
    maxClients: number;
    /**
     * Automatically dispose the room when last client disconnects.
     *
     * @default true
     */
    autoDispose: boolean;
    /**
     * Frequency to send the room state to connected clients, in milliseconds.
     *
     * @default 50ms (20fps)
     */
    patchRate: number | null;
    /**
     * Maximum number of messages a client can send to the server per second.
     * If a client sends more messages than this, it will be disconnected.
     *
     * @default Infinity
     */
    maxMessagesPerSecond: number;
    /**
     * The state instance you provided to `setState()`.
     */
    state: ExtractRoomState<T>;
    /**
     * The presence instance. Check Presence API for more details.
     *
     * @see [Presence API](https://docs.colyseus.io/server/presence)
     */
    presence: Presence;
    /**
     * The array of connected clients.
     *
     * @see [Client instance](https://docs.colyseus.io/room#client)
     */
    clients: ClientArray<ExtractRoomClient<T>>;
    /**
     * Set the number of seconds a room can wait for a client to effectively join the room.
     * You should consider how long your `onAuth()` will have to wait for setting a different seat reservation time.
     * The default value is 15 seconds. You may set the `COLYSEUS_SEAT_RESERVATION_TIME`
     * environment variable if you'd like to change the seat reservation time globally.
     *
     * @default 15 seconds
     */
    seatReservationTimeout: number;
    private _events;
    private _reservedSeats;
    private _reservedSeatTimeouts;
    private _reconnections;
    private _reconnectionAttempts;
    messages?: Messages<any>;
    private onMessageEvents;
    private onMessageValidators;
    private onMessageFallbacks;
    private _serializer;
    private _afterNextPatchQueue;
    private _simulationInterval;
    private _internalState;
    private _lockedExplicitly;
    private _autoDisposeTimeout;
    constructor();
    /**
     * This method is called by the MatchMaker before onCreate()
     * @internal
     */
    private __init;
    /**
     * The name of the room you provided as first argument for `gameServer.define()`.
     *
     * @returns roomName string
     */
    get roomName(): string;
    /**
     * Setting the name of the room. Overwriting this property is restricted.
     *
     * @param roomName
     */
    set roomName(roomName: string);
    /**
     * A unique, auto-generated, 9-character-long id of the room.
     * You may replace `this.roomId` during `onCreate()`.
     *
     * @returns roomId string
     */
    get roomId(): string;
    /**
     * Setting the roomId, is restricted in room lifetime except upon room creation.
     *
     * @param roomId
     * @returns roomId string
     */
    set roomId(roomId: string);
    /**
     * This method is called before the latest version of the room's state is broadcasted to all clients.
     */
    onBeforePatch?(state: ExtractRoomState<T>): void | Promise<any>;
    /**
     * This method is called when the room is created.
     * @param options - The options passed to the room when it is created.
     */
    onCreate?(options: any): void | Promise<any>;
    /**
     * This method is called when a client joins the room.
     * @param client - The client that joined the room.
     * @param options - The options passed to the client when it joined the room.
     * @param auth - The data returned by the `onAuth` method - (Deprecated: use `client.auth` instead)
     */
    onJoin?(client: ExtractRoomClient<T>, options?: any, auth?: any): void | Promise<any>;
    /**
     * This method is called when a client leaves the room without consent.
     * You may allow the client to reconnect by calling `allowReconnection` within this method.
     *
     * @param client - The client that was dropped from the room.
     * @param code - The close code of the leave event.
     */
    onDrop?(client: ExtractRoomClient<T>, code?: number): void | Promise<any>;
    /**
     * This method is called when a client reconnects to the room.
     * @param client - The client that reconnected to the room.
     */
    onReconnect?(client: ExtractRoomClient<T>): void | Promise<any>;
    /**
     * This method is called when a client effectively leaves the room.
     * @param client - The client that left the room.
     * @param code - The close code of the leave event.
     */
    onLeave?(client: ExtractRoomClient<T>, code?: number): void | Promise<any>;
    /**
     * This method is called when the room is disposed.
     */
    onDispose?(): void | Promise<any>;
    /**
     * Define a custom exception handler.
     * If defined, all lifecycle hooks will be wrapped by try/catch, and the exception will be forwarded to this method.
     *
     * These methods will be wrapped by try/catch:
     * - `onMessage`
     * - `onAuth` / `onJoin` / `onLeave` / `onCreate` / `onDispose`
     * - `clock.setTimeout` / `clock.setInterval`
     * - `setSimulationInterval`
     *
     * (Experimental: this feature is subject to change in the future - we're currently getting feedback to improve it)
     */
    onUncaughtException?(error: RoomException, methodName: RoomMethodName): void;
    /**
     * This method is called before onJoin() - this is where you should authenticate the client
     * @param client - The client that is authenticating.
     * @param options - The options passed to the client when it is authenticating.
     * @param context - The authentication context, including the token and the client's IP address.
     * @returns The authentication result.
     *
     * @example
     * ```typescript
     * return {
     *   userId: 123,
     *   username: "John Doe",
     *   email: "john.doe@example.com",
     * };
     * ```
     */
    onAuth(client: Client, options: any, context: AuthContext): any | Promise<any>;
    static onAuth(token: string, options: any, context: AuthContext): Promise<unknown>;
    /**
     * This method is called during graceful shutdown of the server process
     * You may override this method to dispose the room in your own way.
     *
     * Once process reaches room count of 0, the room process will be terminated.
     */
    onBeforeShutdown(): void;
    /**
     * devMode: When `devMode` is enabled, `onCacheRoom` method is called during
     * graceful shutdown.
     *
     * Implement this method to return custom data to be cached. `onRestoreRoom`
     * will be called with the data returned by `onCacheRoom`
     */
    onCacheRoom?(): any;
    /**
     * devMode: When `devMode` is enabled, `onRestoreRoom` method is called during
     * process startup, with the data returned by the `onCacheRoom` method.
     */
    onRestoreRoom?(cached?: any): void;
    /**
     * Returns whether the sum of connected clients and reserved seats exceeds maximum number of clients.
     *
     * @returns boolean
     */
    hasReachedMaxClients(): boolean;
    /**
     * @deprecated Use `seatReservationTimeout=` instead.
     */
    setSeatReservationTime(seconds: number): this;
    hasReservedSeat(sessionId: string, reconnectionToken?: string): boolean;
    checkReconnectionToken(reconnectionToken: string): string;
    /**
     * (Optional) Set a simulation interval that can change the state of the game.
     * The simulation interval is your game loop.
     *
     * @default 16.6ms (60fps)
     *
     * @param onTickCallback - You can implement your physics or world updates here!
     *  This is a good place to update the room state.
     * @param delay - Interval delay on executing `onTickCallback` in milliseconds.
     */
    setSimulationInterval(onTickCallback?: SimulationCallback, delay?: number): void;
    /**
     * @deprecated Use `.patchRate=` instead.
     */
    setPatchRate(milliseconds: number | null): void;
    /**
     * @deprecated Use `.state =` instead.
     */
    setState(newState: ExtractRoomState<T>): void;
    setSerializer(serializer: Serializer<ExtractRoomState<T>>): void;
    setMetadata(meta: Partial<ExtractRoomMetadata<T>>, persist?: boolean): Promise<void>;
    setPrivate(bool?: boolean, persist?: boolean): Promise<void>;
    /**
     * Update multiple matchmaking/listing properties at once with a single persist operation.
     * This is the recommended way to update room listing properties.
     *
     * @param updates - Object containing the properties to update
     *
     * @example
     * ```typescript
     * // Update multiple properties at once
     * await this.setMatchmaking({
     *   metadata: { difficulty: "hard", rating: 1500 },
     *   private: true,
     *   locked: true,
     *   maxClients: 10
     * });
     * ```
     *
     * @example
     * ```typescript
     * // Update only metadata
     * await this.setMatchmaking({
     *   metadata: { status: "in_progress" }
     * });
     * ```
     *
     * @example
     * ```typescript
     * // Partial metadata update (merges with existing)
     * await this.setMatchmaking({
     *   metadata: { ...this.metadata, round: this.metadata.round + 1 }
     * });
     * ```
     */
    setMatchmaking(updates: {
        metadata?: ExtractRoomMetadata<T>;
        private?: boolean;
        locked?: boolean;
        maxClients?: number;
        unlisted?: boolean;
        [key: string]: any;
    }): Promise<void>;
    /**
     * Lock the room. This prevents new clients from joining this room.
     */
    lock(): Promise<void>;
    /**
     * Unlock the room. This allows new clients to join this room, if maxClients is not reached.
     */
    unlock(): Promise<void>;
    /**
     * @deprecated Use `client.send(...)` instead.
     */
    send(client: Client, type: string | number, message: any, options?: ISendOptions): void;
    /**
     * Broadcast a message to all connected clients.
     * @param type - The type of the message.
     * @param message - The message to broadcast.
     * @param options - The options for the broadcast.
     *
     * @example
     * ```typescript
     * this.broadcast('message', { message: 'Hello, world!' });
     * ```
     */
    broadcast<K extends keyof ExtractRoomClient<T>['~messages'] & string | number>(type: K, ...args: MessageArgs<ExtractRoomClient<T>['~messages'][K], IBroadcastOptions>): void;
    /**
     * Broadcast bytes (UInt8Arrays) to a particular room
     */
    broadcastBytes(type: string | number, message: Uint8Array, options: IBroadcastOptions): void;
    /**
     * Checks whether mutations have occurred in the state, and broadcast them to all connected clients.
     */
    broadcastPatch(): boolean;
    /**
     * Register a message handler for a specific message type.
     * This method is used to handle messages sent by clients to the room.
     * @param messageType - The type of the message.
     * @param callback - The callback to call when the message is received.
     * @returns A function to unbind the callback.
     *
     * @example
     * ```typescript
     * this.onMessage('message', (client, message) => {
     *   console.log(message);
     * });
     * ```
     *
     * @example
     * ```typescript
     * const unbind = this.onMessage('message', (client, message) => {
     *   console.log(message);
     * });
     *
     * // Unbind the callback when no longer needed
     * unbind();
     * ```
     */
    onMessage<T = any, C extends Client = ExtractRoomClient<T>>(messageType: '*', callback: (client: C, type: string | number, message: T) => void): any;
    onMessage<T = any, C extends Client = ExtractRoomClient<T>>(messageType: string | number, callback: (client: C, message: T) => void): any;
    onMessage<T = any, C extends Client = ExtractRoomClient<T>>(messageType: string | number, validationSchema: StandardSchemaV1<T>, callback: (client: C, message: T) => void): any;
    onMessageBytes<T = any, C extends Client = ExtractRoomClient<T>>(messageType: string | number, callback: (client: C, message: T) => void): any;
    onMessageBytes<T = any, C extends Client = ExtractRoomClient<T>>(messageType: string | number, validationSchema: StandardSchemaV1<T>, callback: (client: C, message: T) => void): any;
    /**
     * Disconnect all connected clients, and then dispose the room.
     *
     * @param closeCode WebSocket close code (default = 4000, which is a "consented leave")
     * @returns Promise<void>
     */
    disconnect(closeCode?: number): Promise<any>;
    private _rejectPendingReconnections;
    private _onJoin;
    /**
     * Allow the specified client to reconnect into the room. Must be used inside `onLeave()` method.
     * If seconds is provided, the reconnection is going to be cancelled after the provided amount of seconds.
     *
     * @param client - The client that is allowed to reconnect into the room.
     * @param seconds - The time in seconds that the client is allowed to reconnect into the room.
     *
     * @returns Deferred<Client> - The differed is a promise like type.
     *  This type can forcibly reject the promise by calling `.reject()`.
     *
     * @example
     * ```typescript
     * onDrop(client: Client, code: CloseCode) {
     *   // Allow the client to reconnect into the room with a 15 seconds timeout.
     *   this.allowReconnection(client, 15);
     * }
     * ```
     */
    allowReconnection(previousClient: Client, seconds: number | "manual"): Deferred<Client>;
    private resetAutoDisposeTimeout;
    private broadcastMessageType;
    private sendFullState;
    private _dequeueAfterPatchMessages;
    private _reserveSeat;
    private _reserveMultipleSeats;
    private _onMessage;
    private _onLeave;
}
/**
 * (WIP) Alternative, method-based room definition.
 * We should be able to define
 */
type RoomLifecycleMethods = 'messages' | 'onCreate' | 'onJoin' | 'onLeave' | 'onDispose' | 'onCacheRoom' | 'onRestoreRoom' | 'onDrop' | 'onReconnect' | 'onUncaughtException' | 'onAuth' | 'onBeforeShutdown' | 'onBeforePatch';
type DefineRoomOptions<T extends RoomOptions = RoomOptions> = Partial<Pick<Room<T>, RoomLifecycleMethods>> & {
    state?: ExtractRoomState<T> | (() => ExtractRoomState<T>);
} & ThisType<Exclude<Room<T>, RoomLifecycleMethods>> & ThisType<Room<T>>;
export declare function room<T>(options: DefineRoomOptions<T>): typeof Room<T>;
export {};
