import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
import { OnGatewayConnection, OnGatewayDisconnect } from "@nestjs/websockets";
import Redis from "ioredis";
import { Server, Socket } from "socket.io";
import { PresenceService } from "./presence.service";
interface JoinPayload {
    workspaceId: string;
    channelId: string;
}
interface WorkspaceJoinPayload {
    workspaceId: string;
}
interface SendPayload {
    workspaceId: string;
    channelId: string;
    clientMsgId: string;
    text: string;
    blocks?: unknown;
    parentId?: string | null;
    isBroadcast?: boolean;
}
interface AckReadPayload {
    workspaceId: string;
    channelId: string;
    seq: number;
}
interface AckThreadPayload {
    workspaceId: string;
    channelId: string;
    rootMessageId: string;
    seq: number;
}
interface CallSignalPayload {
    workspaceId: string | number;
    callId: string | number;
    toUserId: string | number;
    signal: "offer" | "answer" | "ice";
    /** Opaque SDP blob or ICE candidate — see CallSignalRequest in contracts. */
    data: unknown;
}
/**
 * Connection lifecycle per §4.3: tenant scope (here, just `userId` — see
 * ws-auth.ts for the ticket-vs-JWT simplification) is fixed at connect and
 * never re-read from client input. Handlers stay trivial — validate, then
 * delegate to libs/messaging, which is the same code apps/api's HTTP routes
 * call. Nothing is broadcast directly from a handler; every mutation
 * publishes to Redis (events-bus.ts) and onModuleInit's subscription below
 * is the only thing that ever calls `server.to(room).emit(...)`, so the
 * send-via-HTTP and send-via-socket paths broadcast identically.
 */
export declare class AppGateway implements OnGatewayConnection, OnGatewayDisconnect, OnModuleInit, OnModuleDestroy {
    private readonly redis;
    private readonly presence;
    private readonly logger;
    private eventsSub?;
    server: Server;
    constructor(redis: Redis, presence: PresenceService);
    onModuleInit(): Promise<void>;
    onModuleDestroy(): Promise<void>;
    handleConnection(client: Socket): void;
    /** Presence is unregistered here for every workspace this socket ever
     * joined (§4.4) — device-count only drops to zero, and `offline` only
     * broadcasts, once every workspace the user was active in agrees. */
    handleDisconnect(client: Socket): Promise<void>;
    handleJoinWorkspace(client: Socket, payload: WorkspaceJoinPayload): Promise<{
        error: string;
        ok?: undefined;
        workspaceId?: undefined;
    } | {
        ok: boolean;
        workspaceId: string;
        error?: undefined;
    }>;
    handleJoinChannel(client: Socket, payload: JoinPayload): Promise<{
        error: string;
        ok?: undefined;
        joined?: undefined;
    } | {
        ok: boolean;
        joined: string;
        error?: undefined;
    }>;
    handleHeartbeat(client: Socket, payload: {
        active?: boolean;
    }): Promise<{
        ok: boolean;
    }>;
    handleTyping(client: Socket, payload: {
        workspaceId: string | number;
        channelId: string | number;
    }): Promise<{
        error: string;
        ok?: undefined;
        throttled?: undefined;
    } | {
        ok: boolean;
        throttled: boolean;
        error?: undefined;
    } | {
        ok: boolean;
        error?: undefined;
        throttled?: undefined;
    }>;
    /**
     * Relays one WebRTC offer/answer/ICE candidate to one other participant.
     *
     * The only handler that forwards a client's payload rather than acting on it,
     * because SDP and ICE belong to the browser's WebRTC implementation, not to
     * this protocol. What the gateway *does* enforce is who may talk to whom:
     * both ends must currently be on the named call, checked against the
     * database rather than trusted from the payload, and `fromUserId` is stamped
     * from the authenticated socket so a sender cannot impersonate a peer.
     *
     * Emitted directly instead of through the Redis events bus: this is a
     * point-to-point message with nothing to persist and no other subscriber, and
     * the negotiation is latency-sensitive enough that the extra hop isn't worth
     * it. `u:{userId}` spans nodes via the Redis socket.io adapter regardless,
     * so a peer on another gateway instance still receives it.
     */
    handleCallSignal(client: Socket, payload: CallSignalPayload): Promise<{
        error: string;
        ok?: undefined;
    } | {
        ok: boolean;
        error?: undefined;
    }>;
    handleLeaveChannel(client: Socket, payload: {
        channelId: string | number;
    }): {
        left: string | number;
    };
    handleSendMessage(client: Socket, payload: SendPayload): Promise<{
        ok: boolean;
        messageId: number;
        seq: number;
    }>;
    handleReadAck(client: Socket, payload: AckReadPayload): Promise<{
        ok: boolean;
    }>;
    handleThreadAck(client: Socket, payload: AckThreadPayload): Promise<{
        ok: boolean;
    }>;
}
export {};
//# sourceMappingURL=app.gateway.d.ts.map