import type Redis from "ioredis";
import type { Call, CallContact, CallKind, CallRecordingStatus, CallTranscriptStatus } from "@slackwsh/contracts";
import { type EntityIdInput } from "./channels";
/**
 * How long a call may sit unanswered before it counts as missed.
 *
 * A ringing call has no natural end — the caller's browser may be closed
 * mid-ring, and nothing else would ever write the row. Rather than depend on
 * a worker that does not exist yet, reads sweep expired rows (see
 * sweepStaleCalls below), which keeps the invariant "no call is ringing
 * forever" true without a scheduler.
 */
export declare const CALL_RING_TIMEOUT_SECONDS = 45;
/**
 * How long an answered call may run with nobody sending a keepalive before it
 * is force-ended. This only catches calls whose participants all vanished
 * without a clean leave (a crashed tab, a killed laptop); a normal hangup
 * ends the call immediately via leaveCall.
 */
export declare const CALL_STALE_TIMEOUT_SECONDS: number;
export interface StartCallInput {
    kind?: CallKind;
    inviteeUserIds: EntityIdInput[];
    channelId?: EntityIdInput | null;
    eventId?: EntityIdInput | null;
    title?: string | null;
}
export interface ListCallsFilters {
    active?: boolean;
    missed?: boolean;
    direction?: "incoming" | "outgoing";
    withUserId?: EntityIdInput;
    channelId?: EntityIdInput;
    limit?: number;
    before?: string;
}
export declare function startCall(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, input: StartCallInput): Promise<Call>;
export declare function livekitRoomName(workspaceId: number, callId: number): string;
/**
 * Join-or-start the persistent audio room for a channel (feature 8.1).
 *
 * There is at most one live Connect per channel (enforced by a unique index).
 * A collision from two people clicking at once joins the winner rather than
 * 500ing — the user asked to be in the room, not to be the one who created it.
 */
export declare function startOrJoinConnect(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput): Promise<Call>;
export declare function bindLivekitRoom(workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput, roomName: string): Promise<Call>;
export declare function setCallRecording(workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput, patch: {
    recordingStatus?: CallRecordingStatus;
    recordingEgressId?: string | null;
    recordingObjectKey?: string | null;
}): Promise<Call>;
export declare function setCallTranscript(workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput, patch: {
    transcriptStatus?: CallTranscriptStatus;
    transcript?: string | null;
    summary?: string | null;
}): Promise<Call>;
/** Resolve an inbound PSTN number to a workspace. Env mapping is the
 * Phase 6 path (one number per deployment); the workspace_phone_numbers
 * table is the multi-tenant follow-up. */
export declare function workspaceIdForInboundPstn(e164: string): number | null;
/**
 * Reads call history.
 *
 * Scoped to calls the actor took part in, which is stricter than the policy
 * table can express: `call:read` grants access to the feature, not to other
 * people's call logs. The one exception is the live-calls read, where a call
 * attached to a channel the actor belongs to is joinable by them and therefore
 * has to be visible — the same "a Connect in your room is yours to join" rule
 * the chat UI implies.
 */
export declare function listCalls(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, filters?: ListCallsFilters): Promise<Call[]>;
export declare function getCall(workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput): Promise<Call>;
/** Whether a user may exchange media with a call — the authorisation the
 * gateway's signalling relay needs. Only someone currently on the call can
 * signal, so a declined or departed participant cannot keep negotiating. */
export declare function isCallParticipant(workspaceId: EntityIdInput, callId: EntityIdInput, userId: EntityIdInput): Promise<boolean>;
/**
 * Answers a call, or walks into a channel call uninvited.
 *
 * The second case is why this can add a participant row rather than only
 * update one: a call attached to a room is open to that room's members, so
 * joining one is a legitimate first contact with the call. A call with no
 * channel is closed — only the people who were rung may answer.
 */
export declare function joinCall(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput): Promise<Call>;
/** Turns down a call. Deliberately takes no target user — you may only decline
 * for yourself, which the role table can't express (it has no notion of
 * "subject equals actor"), so it is enforced here by construction. */
export declare function declineCall(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput): Promise<Call>;
/**
 * Hangs up for one person, and ends the call if they were the last one on it.
 *
 * "Last one" counts people still ringing too: a caller who gives up while the
 * phone is still ringing has ended the call, not left the others to talk among
 * themselves.
 */
export declare function leaveCall(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput): Promise<Call>;
/** Ends the call for everyone. Distinct from leaveCall, which only ends it as
 * a side effect of the room emptying — this is the deliberate "hang up on all
 * of us", available to anyone on the call. */
export declare function endCall(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput): Promise<Call>;
/** Adds people to a call in progress. Additive, unlike inviteToEvent's
 * replace-wholesale: the people already on the call are not the caller's rows
 * to rewrite. */
export declare function inviteToCall(redis: Redis, workspaceId: EntityIdInput, actorUserId: EntityIdInput, callId: EntityIdInput, inviteeUserIds: EntityIdInput[]): Promise<Call>;
/** Clears the missed-call badge. Only ever touches the actor's own rows, so
 * there is no target user to authorise. */
export declare function markCallsSeen(workspaceId: EntityIdInput, actorUserId: EntityIdInput, callIds?: EntityIdInput[]): Promise<{
    seen: number;
}>;
/** How many calls the actor has never acknowledged — the nav badge's number. */
export declare function unseenMissedCallCount(workspaceId: EntityIdInput, actorUserId: EntityIdInput): Promise<number>;
/**
 * The "people to call" ranking: everyone the actor has actually called,
 * with how often and how recently.
 *
 * Aggregated in SQL over a self-join of call_participants (my calls, then
 * everyone else on them) rather than by paging history client-side — the
 * ranking needs every call ever, which is exactly the read a page size can't
 * give you. Members with no shared call history are absent from the result;
 * the client unions this with the member roster so a new user still sees
 * someone to call.
 */
export declare function callContacts(workspaceId: EntityIdInput, actorUserId: EntityIdInput): Promise<CallContact[]>;
//# sourceMappingURL=calls.d.ts.map