import type { Server as HTTPServer } from "http";
import type { UserInfo, SocketWithAuth } from '../types/index.js';
/**
 * True if a read permission filter imposes any row restriction (flat `conditions`
 * or nested `relConditions`). When false, the role can read the whole collection,
 * so a broadcast can be delivered without a per-record DB check (fast path).
 */
export declare function hasReadRestrictions(filter: {
    conditions?: any;
    relConditions?: any;
} | null | undefined): boolean;
/**
 * True only if EVERY changed id is present in the set of ids the subscriber may
 * actually read (the result of a permission-scoped existence query). Fails closed:
 * an empty visible set with non-empty ids → false.
 */
export declare function allIdsVisible(changedIds: any[], visibleIds: any[]): boolean;
export type CustomMessageHandler = (socket: SocketWithAuth, data: any, callback?: (response: any) => void) => void | Promise<void>;
export type RoomValidator = (socket: SocketWithAuth, roomName: string) => boolean | Promise<boolean>;
declare class SocketService {
    private io;
    private userSockets;
    private initialized;
    private redisPublisher;
    private redisSubscriber;
    private customRooms;
    private roomCreators;
    private roomOriginalCreators;
    private socketIdToUserId;
    private customMessageHandlers;
    private roomValidators;
    private static readonly ROOM_HISTORY_LIMIT;
    private roomMessageHistory;
    private get useRedis();
    /** Add / update a member in the room's member store */
    private rRoomMemberSet;
    /** Remove a member from the room's member store */
    private rRoomMemberDel;
    /** Check if a socket is in a room */
    private rRoomMemberHas;
    /** Get all members of a room as Map<socketId, meta> */
    private rRoomMembersAll;
    /** Number of members in a room */
    private rRoomMemberCount;
    /** Delete the entire room (all member keys + creator keys + history) */
    private rRoomDestroy;
    /** Get current creator socketId */
    private rCreatorGet;
    /** Set current creator socketId */
    private rCreatorSet;
    /** Delete current creator entry */
    private rCreatorDel;
    /** Get original creator userId */
    private rOriginalCreatorGet;
    /** Set original creator userId (only set once — NX) */
    private rOriginalCreatorSet;
    /** Append a message to history, trimming to ROOM_HISTORY_LIMIT */
    private rHistoryPush;
    /** Fetch all history entries for a room */
    private rHistoryGet;
    constructor();
    initialize(server: HTTPServer): Promise<void>;
    initializeRedisAdapter(): Promise<void>;
    cleanup(): Promise<void>;
    authMiddleware(socket: SocketWithAuth, next: (err?: Error) => void): Promise<void>;
    authenticateUser(decoded: any): Promise<UserInfo>;
    handleConnection(socket: SocketWithAuth): Promise<void>;
    handleSubscribe(socket: SocketWithAuth, { collection }: {
        collection: string;
    }): Promise<void>;
    handleUnsubscribe(socket: SocketWithAuth, { collection }: {
        collection: string;
    }): void;
    handleDisconnect(socket: SocketWithAuth): void;
    private handleDisconnectRoomCleanup;
    /**
     * Handle a user joining a custom room
     */
    handleRoomJoin(socket: SocketWithAuth, roomName: string, metadata?: Record<string, any>): Promise<void>;
    /**
     * Handle a user leaving a custom room
     */
    handleRoomLeave(socket: SocketWithAuth, roomName: string): Promise<void>;
    /**
     * Handle a message sent to a custom room
     */
    handleRoomMessage(socket: SocketWithAuth, roomName: string, event: string, payload: any, storeInHistory?: boolean): Promise<void>;
    /**
     * Return member list for a room. Only callable by current room members.
     * Returns userId, socketId, metadata, and whether each member is the room creator.
     */
    handleRoomMembers(socket: SocketWithAuth, roomName: string): Promise<Array<{
        socketId: string;
        userId: string | number;
        isCreator: boolean;
        metadata: Record<string, any>;
    }>>;
    /**
     * Kick a user from a custom room. Only the room creator may do this.
     * All sockets belonging to the target user that are in the room will be removed.
     */
    handleRoomKick(socket: SocketWithAuth, roomName: string, targetUserId: string | number): Promise<void>;
    /**
     * Handle custom events with registered handlers
     */
    handleCustomEvent(socket: SocketWithAuth, event: string, payload: any, callback?: (response: any) => void): Promise<void>;
    /**
     * Register a custom message handler for a specific event
     * Can be used by extensions to handle custom socket events
     *
     * @example
     * socketService.registerMessageHandler("game:move", async (socket, data, callback) => {
     *   // Handle game move logic
     *   callback?.({ status: "success", result: { ... } });
     * });
     */
    registerMessageHandler(event: string, handler: CustomMessageHandler): void;
    /**
     * Unregister a custom message handler
     */
    unregisterMessageHandler(event: string): void;
    /**
     * Register a validator for room join requests
     * Validator is called when a user tries to join a room matching the pattern
     *
     * @example
     * socketService.registerRoomValidator("game:", async (socket, roomName) => {
     *   // Only allow admins to join game rooms
     *   return socket.userRole.name === "administrator";
     * });
     */
    registerRoomValidator(roomPattern: string, validator: RoomValidator): void;
    /**
     * Unregister a room validator
     */
    unregisterRoomValidator(roomPattern: string): void;
    /**
     * Broadcast a message to a custom room from server side
     *
     * @example
     * socketService.broadcastToRoom("game:123", "game:state", { players: [...] });
     */
    broadcastToRoom(roomName: string, event: string, payload: any): void;
    /**
     * Broadcast a message to all connected users
     *
     * @example
     * socketService.broadcastToAll("system:announcement", { message: "Server maintenance in 5 minutes" });
     */
    broadcastToAll(event: string, payload: any): void;
    /**
     * Send a message to a specific user (all their connected sockets)
     *
     * @example
     * socketService.sendToUser(userId, "private:message", { from: "admin", text: "Hello!" });
     */
    sendToUser(userId: string | number, event: string, payload: any): void;
    /**
     * Get list of users in a custom room (server-side utility for extensions)
     */
    getRoomMembers(roomName: string): Promise<Array<{
        socketId: string;
        userId: string | number;
        metadata: Record<string, any>;
    }>>;
    /**
     * Get count of users in a custom room
     */
    getRoomMemberCount(roomName: string): Promise<number>;
    /**
     * Check if a room exists
     */
    roomExists(roomName: string): Promise<boolean>;
    /**
     * Get all custom rooms with member counts
     */
    getCustomRooms(): Promise<Map<string, number>>;
    checkCollectionAccess(socket: SocketWithAuth, collection: string): Promise<boolean>;
    /**
     * Whether this socket may join a workflow execution room. Allowed when the user
     * is an administrator, or triggered the execution, within the same tenant.
     */
    canJoinExecutionRoom(socket: SocketWithAuth, executionId: string | number): Promise<boolean>;
    getCollectionRoom(collection: string, tenantId?: string | number): string;
    broadcastChange(collection: string, action: string, data: any, accountability?: any): void;
    /**
     * A12 hybrid row-level scoping. For each subscriber in the room, decide whether
     * they may receive the changed row(s) based on their read permission:
     *  - admin, or a role with NO read conditions/relConditions → allowed (in-memory
     *    fast path, no DB).
     *  - otherwise → authoritative DB existence check via ItemsService.readByQuery
     *    with that subscriber's accountability (covers flat conditions, nested
     *    relConditions, tenant scoping, and field perms exactly like a normal read).
     * Subscribers are BUCKETED by role so each distinct role does at most one
     * existence query per changed id, not one per subscriber.
     */
    private broadcastChangeScoped;
    /**
     * Whether a subscriber's role may read the given record ids. Returns true if no
     * read restrictions apply (fast path), otherwise confirms via a permission-scoped
     * existence query. If there are multiple ids and they don't all pass, this returns
     * true (the bucket gets the event) — per-record splitting is a future refinement;
     * the common realtime case is a single changed record.
     */
    private canRoleSeeRecords;
    /**
     * Remove hidden schema fields (password, tokens, secrets) from a broadcast
     * payload. Handles a single record or an array of records; non-objects pass
     * through unchanged.
     */
    private stripHiddenFromBroadcast;
    /**
     * Emit workflow execution updates to connected clients
     */
    emitWorkflowExecutionUpdate(executionId: string | number, data: any): void;
    /**
     * Emit workflow execution completion
     */
    emitWorkflowExecutionComplete(executionId: string | number, data: any): void;
    getStats(): any;
}
declare global {
    var __baasix_socketService: SocketService | undefined;
}
declare const socketService: SocketService;
export default socketService;
//# sourceMappingURL=SocketService.d.ts.map