/**
 * Slack Service
 *
 * Manages Slack bot connection and messaging using Bolt SDK.
 * Enables bidirectional communication between Slack and Crewly.
 *
 * @module services/slack
 */
import { EventEmitter } from 'events';
import type { SlackConfig, SlackIncomingMessage, SlackOutgoingMessage, SlackServiceStatus, SlackConversationContext, SlackNotification, SlackBlock } from '../../types/slack.types.js';
/**
 * Events emitted by SlackService
 */
export interface SlackServiceEvents {
    message: (message: SlackIncomingMessage) => void;
    connected: () => void;
    disconnected: (error?: Error) => void;
    error: (error: Error) => void;
}
/**
 * Options for uploading a file to Slack.
 * Used by both uploadImage() and uploadFile().
 */
interface FileUploadOptions {
    channelId: string;
    filePath: string;
    filename?: string;
    title?: string;
    initialComment?: string;
    threadTs?: string;
}
/**
 * SlackService class for managing Slack bot operations
 */
export declare class SlackService extends EventEmitter {
    private logger;
    private app;
    private client;
    private config;
    private status;
    private conversationContexts;
    /** Cached Bolt App constructor from the first successful dynamic import */
    private cachedAppConstructor;
    /** Cached Bolt LogLevel enum from the first successful dynamic import */
    private cachedLogLevelEnum;
    /**
     * Message deduplication tracker.
     * Maps a fingerprint (channelId:threadTs:textHash) to the timestamp it was sent.
     * Prevents identical messages from being sent to the same thread within a time window.
     */
    private recentMessageFingerprints;
    /** Whether a reconnection attempt is currently in progress */
    private reconnecting;
    /** Timer handle for the reconnection grace period */
    private reconnectGraceTimer;
    /** Timer handle for the periodic health check */
    private healthCheckTimer;
    /** Current consecutive reconnection attempt count */
    private reconnectAttempts;
    /** Whether the service has been intentionally shut down (skip auto-reconnect) */
    private intentionalDisconnect;
    /** Timestamp of the last successful Slack API ping */
    private lastPingAt;
    /** Number of consecutive health-check ping failures */
    private consecutivePingFailures;
    /**
     * Initialize the Slack service with configuration
     *
     * @param config - Slack bot configuration
     * @returns Promise that resolves when connected
     */
    initialize(config: SlackConfig): Promise<void>;
    /**
     * Set up Slack event handlers
     */
    private setupEventHandlers;
    /**
     * Attach listeners to the underlying SocketModeClient to track
     * connection lifecycle (disconnected, reconnecting, connected).
     * Updates `status.connected` so health checks reflect actual state.
     *
     * The Bolt App stores the receiver at `app.receiver`, and the
     * SocketModeReceiver exposes `client` (SocketModeClient), which
     * is an EventEmitter emitting: connected, reconnecting, disconnected, close.
     */
    private setupConnectionMonitoring;
    /**
     * Start a periodic health check that verifies the Socket Mode connection
     * is alive and triggers reconnection if it has silently died.
     *
     * Uses an active Slack API ping (auth.test) to detect half-open sockets
     * that appear connected but are actually dead after WiFi/network drops.
     */
    private startHealthCheck;
    /**
     * Stop the periodic health check timer.
     */
    private stopHealthCheck;
    /**
     * Schedule a reconnection attempt after a grace period.
     * The grace period gives Bolt's built-in reconnect a chance to recover first.
     * If the connection comes back within the grace period, the reconnect is cancelled.
     */
    private scheduleReconnect;
    /**
     * Cancel the reconnection grace period timer.
     */
    private cancelReconnectGrace;
    /**
     * Attempt to reconnect by tearing down the current Bolt app and
     * re-initializing from scratch with the saved config.
     * Uses exponential backoff between attempts.
     */
    private attemptReconnect;
    /**
     * Classify whether a reconnection error is fatal (non-recoverable)
     * or transient (may succeed on retry).
     *
     * Fatal errors: missing constructor, invalid config, auth errors.
     * Transient errors: network timeouts, temporary server errors.
     *
     * @param error - The error caught during reconnection
     * @returns True if the error is fatal and retrying would be futile
     */
    private isFatalReconnectError;
    /**
     * Send a message to Slack with content-based deduplication.
     *
     * Generates a fingerprint from (channelId, threadTs, text) and suppresses
     * duplicate sends within a configurable time window (default 30s).
     * This is the single chokepoint for all Slack message sends — skill delivery,
     * bridge fallback, and direct API calls all converge here.
     *
     * @param message - Message to send
     * @returns Promise with message timestamp (empty string if deduplicated)
     */
    sendMessage(message: SlackOutgoingMessage): Promise<string>;
    /**
     * Mirror an outbound Slack reply into the canonical chat-v2 store so the
     * Slack thread shows both inbound (user) and outbound (agent) messages in
     * the consolidated /team-chat surface. Records against the SAME channel the
     * inbound persisted to — `synthesizeSlackConversationId(channelId, threadTs)`.
     *
     * Best-effort: dynamic imports + try/catch so a chat-v2 failure never
     * affects Slack delivery. Only called for threaded replies (threadTs set).
     *
     * @param message - The outbound Slack message that was just sent.
     */
    private recordOutboundToChatV2;
    /**
     * Build a deduplication fingerprint for a Slack message.
     * Combines channelId, threadTs, and a simple hash of the message text.
     *
     * @param message - Outgoing Slack message
     * @returns Fingerprint string
     */
    private buildMessageFingerprint;
    /**
     * Track a message fingerprint and evict stale entries.
     *
     * @param fingerprint - Message fingerprint to track
     * @param timestamp - Current timestamp in ms
     */
    private trackMessageFingerprint;
    /**
     * Send a notification to the default channel
     *
     * @param notification - Notification to send
     */
    sendNotification(notification: SlackNotification): Promise<void>;
    /**
     * Format notification as Slack blocks
     *
     * @param notification - Notification to format
     * @returns Slack blocks for the notification
     */
    private formatNotificationBlocks;
    /**
     * Update a message
     *
     * @param channelId - Channel ID
     * @param messageTs - Message timestamp
     * @param text - New text
     * @param blocks - Optional new blocks
     */
    updateMessage(channelId: string, messageTs: string, text: string, blocks?: SlackBlock[]): Promise<void>;
    /**
     * Add a reaction to a message
     *
     * @param channelId - Channel ID
     * @param messageTs - Message timestamp
     * @param emoji - Emoji name (without colons)
     */
    addReaction(channelId: string, messageTs: string, emoji: string): Promise<void>;
    /**
     * Get or create conversation context for a thread
     *
     * @param threadTs - Thread timestamp
     * @param channelId - Channel ID
     * @param userId - User ID
     * @returns Conversation context
     */
    getConversationContext(threadTs: string, channelId: string, userId: string): SlackConversationContext;
    /**
     * Get service status
     *
     * @returns Current service status
     */
    getStatus(): SlackServiceStatus;
    /**
     * Check if service is connected
     *
     * @returns True if connected
     */
    isConnected(): boolean;
    /**
     * Get the bot token used to initialize the Slack client.
     * Required for downloading private files from Slack.
     *
     * @returns The bot token string, or null if not initialized
     */
    getBotToken(): string | null;
    /**
     * Upload an image file to a Slack channel.
     *
     * Delegates to the shared upload-with-retry logic.
     * Kept as a separate public method for backward compatibility
     * with callers that use the image-specific endpoint.
     *
     * @param options - Upload configuration
     * @returns Object with the uploaded file ID
     * @throws Error if the client is not initialized or upload fails
     */
    uploadImage(options: FileUploadOptions): Promise<{
        fileId?: string;
    }>;
    /**
     * Upload any supported file to a Slack channel.
     *
     * Delegates to the shared upload-with-retry logic.
     * Works for PDFs, images, documents, and other file types supported by Slack.
     *
     * @param options - Upload configuration
     * @returns Object with the uploaded file ID
     * @throws Error if the client is not initialized or upload fails
     */
    uploadFile(options: FileUploadOptions): Promise<{
        fileId?: string;
    }>;
    /**
     * Shared upload logic with retry/backoff for Slack rate limits.
     *
     * Reads the file from disk and uploads via files.uploadV2.
     * Retries on 429 responses with exponential backoff capped at 60s.
     *
     * @param options - Upload configuration (channel, path, filename, etc.)
     * @param constants - Constants object providing UPLOAD_MAX_RETRIES and UPLOAD_DEFAULT_BACKOFF_MS
     * @returns Object with the uploaded file ID
     * @throws Error if the client is not initialized, retries exhausted, or a non-rate-limit error occurs
     */
    private uploadWithRetry;
    /**
     * Check if an error is a Slack 429 rate limit error.
     *
     * @param error - The caught error
     * @returns True if the error represents a 429 rate limit response
     */
    private isRateLimitError;
    /**
     * Extract the retry-after delay from a Slack rate limit error.
     *
     * @param error - The caught error
     * @returns Retry delay in milliseconds, or null if not found
     */
    private extractRetryAfterMs;
    /**
     * Disconnect from Slack
     */
    disconnect(): Promise<void>;
    /**
     * Get file info from Slack via the files.info API.
     * Requires the bot token to have `files:read` scope.
     *
     * @param fileId - Slack file ID (e.g., F0123ABC456)
     * @returns Object with private download URLs
     * @throws Error if the client is not initialized or API call fails
     */
    getFileInfo(fileId: string): Promise<{
        url_private: string;
        url_private_download: string;
    }>;
    /**
     * Get user info from Slack
     *
     * @param userId - Slack user ID
     * @returns User info object
     */
    getUserInfo(userId: string): Promise<{
        name: string;
        realName: string;
        email?: string;
    }>;
}
/**
 * Get the SlackService singleton instance
 *
 * @returns SlackService instance
 */
export declare function getSlackService(): SlackService;
/**
 * Reset the SlackService instance (for testing)
 */
export declare function resetSlackService(): void;
export {};
//# sourceMappingURL=slack.service.d.ts.map