import type Redis from "ioredis";
export type EntityIdInput = number | string;
export declare function asEntityId(value: EntityIdInput): number;
/**
 * Every member-scoped action fetches the actor's own workspace membership
 * row first — never trust a client-supplied role. Shared with
 * apps/api/src/workspaces/workspaces.service.ts's pattern.
 */
export declare function requireWorkspaceMembership(tx: any, workspaceId: EntityIdInput, userId: EntityIdInput): Promise<any>;
export declare function requireChannelMembership(tx: any, channelId: EntityIdInput, userId: EntityIdInput): Promise<any>;
export declare function createChannel(workspaceId: EntityIdInput, actorUserId: EntityIdInput, input: {
    name: string;
    type: "public" | "private";
    topic?: string;
}): Promise<{
    type: "public" | "private" | "dm" | "group_dm";
    workspaceId: number;
    name: string | null;
    id: number;
    topic: string | null;
    purpose: string | null;
    createdBy: number;
    isArchived: boolean;
    lastMessageAt: Date | null;
    memberCount: number;
    createdAt: Date;
}>;
export declare function listMyChannels(workspaceId: EntityIdInput, actorUserId: EntityIdInput): Promise<{
    channel: {
        type: "public" | "private" | "dm" | "group_dm";
        workspaceId: number;
        name: string | null;
        id: number;
        topic: string | null;
        purpose: string | null;
        createdBy: number;
        isArchived: boolean;
        lastMessageAt: Date | null;
        memberCount: number;
        createdAt: Date;
    };
    member: {
        unreadCount: number;
        userId: number;
        role: string;
        joinedAt: Date;
        channelId: number;
        lastReadSeq: number;
        lastReadAt: Date | null;
        mentionCount: number;
        notifPref: string;
        isMuted: boolean;
        isStarred: boolean;
        isClosed: boolean;
        sectionId: string | null;
    };
    dmPeer: null | {
        id: number;
        name: string;
        email: string;
        avatarUrl: string | null;
    };
    dmPeers: Array<{
        id: number;
        name: string;
        email: string;
        avatarUrl: string | null;
    }>;
}[]>;
export declare function joinChannel(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput): Promise<{
    type: "public" | "private" | "dm" | "group_dm";
    workspaceId: number;
    name: string | null;
    id: number;
    topic: string | null;
    purpose: string | null;
    createdBy: number;
    isArchived: boolean;
    lastMessageAt: Date | null;
    memberCount: number;
    createdAt: Date;
}>;
export declare function leaveChannel(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput): Promise<void>;
/** Add existing workspace members into a text channel. Actor must already be
 * in the channel; targets must be active workspace members (not guests from
 * outside the workspace — those join via workspace invite first). */
export declare function addChannelMembers(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, userIds: EntityIdInput[]): Promise<{
    added: number[];
}>;
export declare function listChannelMembers(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput): Promise<{
    member: {
        userId: number;
        role: string;
        joinedAt: Date;
        channelId: number;
        lastReadSeq: number;
        lastReadAt: Date | null;
        mentionCount: number;
        notifPref: string;
        isMuted: boolean;
        isStarred: boolean;
        isClosed: boolean;
        sectionId: string | null;
    };
    user: {
        name: string;
        id: number;
        email: string;
        avatarUrl: string | null;
    };
}[]>;
/** Removes one person from a text channel without removing them from the
 * workspace. Workspace admins or the channel owner may do this. */
export declare function removeChannelMember(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, memberUserId: EntityIdInput): Promise<{
    removed: number;
}>;
/** Irreversibly removes a text channel and its conversation data. Workspace-
 * wide tasks/events/calls are preserved but detached from the deleted room. */
export declare function permanentlyDeleteChannel(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput): Promise<{
    deleted: boolean;
    objectKeys: string[];
}>;
export declare function archiveChannel(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, archived: boolean): Promise<void>;
/**
 * Renames/updates topic/purpose — Feature 3.5. `purpose` is what the About
 * panel shows as the room description; `topic` stays the short header line.
 *
 * Publishes `channel:updated` so a description edited in one client shows up
 * in everyone else's About panel without a reload — the same full-row event
 * shape archiveChannel's consumers already expect.
 */
export declare function updateChannel(redis: Redis | null, workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, input: {
    name?: string;
    topic?: string | null;
    purpose?: string | null;
}): Promise<{
    type: "public" | "private" | "dm" | "group_dm";
    id: number;
    name: string | null;
    workspaceId: number;
    topic: string | null;
    purpose: string | null;
    createdBy: number;
    isArchived: boolean;
    lastMessageAt: string | null;
    memberCount: number;
}>;
/**
 * Per-user notification settings for one room. Deliberately scoped to the
 * caller's own channel_members row — there is no "set someone else's
 * preference" path, so no role check is needed beyond being in the room.
 */
export declare function updateChannelPrefs(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, input: {
    notifPref?: "all" | "mentions" | "none";
    isMuted?: boolean;
    isStarred?: boolean;
}): Promise<{
    channelId: number;
    userId: number;
    role: string;
    joinedAt: Date;
    lastReadSeq: number;
    lastReadAt: Date | null;
    mentionCount: number;
    notifPref: string;
    isMuted: boolean;
    isStarred: boolean;
    isClosed: boolean;
    sectionId: string | null;
}>;
/** Links an app into a room. Re-linking the same provider updates it in
 * place — the unique (channel_id, provider) index is what makes that an
 * upsert rather than a duplicate row. */
export declare function addChannelIntegration(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, input: {
    provider: string;
    label?: string;
    externalUrl?: string;
}): Promise<{
    channelId: number;
    id: number;
    provider: string;
    label: string | null;
    externalUrl: string | null;
    addedBy: number;
    addedAt: Date;
}>;
export declare function removeChannelIntegration(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, integrationId: EntityIdInput): Promise<void>;
export interface ChannelAboutPin {
    messageId: number;
    text: string;
    authorId: number;
    authorName: string;
    pinnedBy: number;
    pinnedByName: string;
    pinnedAt: string;
    createdAt: string;
}
/**
 * Everything the About-this-room panel renders, in one round trip: the room
 * row itself, the caller's own membership (role + notification prefs), the
 * pinned messages, and the linked apps. One endpoint rather than four because
 * the panel is all-or-nothing — it opens as a unit.
 */
export declare function getChannelAbout(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput): Promise<{
    channel: {
        type: "public" | "private" | "dm" | "group_dm";
        id: number;
        name: string | null;
        workspaceId: number;
        topic: string | null;
        purpose: string | null;
        createdBy: number;
        isArchived: boolean;
        lastMessageAt: string | null;
        memberCount: number;
    };
    membership: {
        role: any;
        notifPref: any;
        isMuted: any;
        isStarred: any;
        joinedAt: any;
    };
    capabilities: {
        canEditRoom: boolean;
        canPin: boolean;
        canArchive: boolean;
        canLeave: boolean;
        canRemoveMembers: boolean;
        canDeletePermanently: boolean;
    };
    pins: ChannelAboutPin[];
    integrations: {
        id: number;
        provider: string;
        label: string | null;
        externalUrl: string | null;
        addedBy: number;
        addedAt: string;
    }[];
}>;
/** DMs are channels of type `dm` with exactly the two participants (§4.7). */
export declare function getOrCreateDirectMessage(workspaceId: EntityIdInput, actorUserId: EntityIdInput, otherUserId: EntityIdInput): Promise<{
    type: "public" | "private" | "dm" | "group_dm";
    workspaceId: number;
    name: string | null;
    id: number;
    topic: string | null;
    purpose: string | null;
    createdBy: number;
    isArchived: boolean;
    lastMessageAt: Date | null;
    memberCount: number;
    createdAt: Date;
}>;
/**
 * Group DMs (Feature 3.4, up to 9 participants). Unlike 1:1 DMs, this does
 * not dedupe against an existing channel with the same member set — Slack
 * itself allows creating a fresh group DM for the same set, and computing
 * "the" canonical channel for an arbitrary member set adds real complexity
 * for a Phase 2 MVP. Documented simplification, not an oversight.
 */
export declare function createGroupDirectMessage(workspaceId: EntityIdInput, actorUserId: EntityIdInput, memberUserIds: EntityIdInput[]): Promise<{
    type: "public" | "private" | "dm" | "group_dm";
    workspaceId: number;
    name: string | null;
    id: number;
    topic: string | null;
    purpose: string | null;
    createdBy: number;
    isArchived: boolean;
    lastMessageAt: Date | null;
    memberCount: number;
    createdAt: Date;
}>;
/** Hides/reopens a DM only for the caller. History and other participants are
 * untouched, which mirrors Slack's "close conversation" behaviour. */
export declare function setDirectMessageClosed(workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, closed: boolean): Promise<{
    closed: boolean;
}>;
/** Promotes an existing group DM into a private channel in place, preserving
 * every message, attachment and participant. */
export declare function convertGroupDirectMessage(redis: Redis | null, workspaceId: EntityIdInput, actorUserId: EntityIdInput, channelId: EntityIdInput, name: string): Promise<{
    type: "public" | "private" | "dm" | "group_dm";
    id: number;
    name: string | null;
    workspaceId: number;
    topic: string | null;
    purpose: string | null;
    createdBy: number;
    isArchived: boolean;
    lastMessageAt: string | null;
    memberCount: number;
}>;
/** Standalone membership check (opens its own transaction) — for callers
 * like the gateway's `channel:join` handler that don't already have a `tx`. */
export declare function checkChannelMembership(workspaceId: EntityIdInput, channelId: EntityIdInput, userId: EntityIdInput): Promise<boolean>;
export declare function checkWorkspaceMembership(workspaceId: EntityIdInput, userId: EntityIdInput): Promise<boolean>;
//# sourceMappingURL=channels.d.ts.map