import { PostHog } from './module';
import { ConversationsRemoteConfig } from '@posthog/browser-common';

/**
 * Author types for messages in a conversation
 */
type MessageAuthorType = 'customer' | 'AI' | 'human';
/**
 * TipTap mark types for inline formatting
 */
interface TipTapMark {
    type: 'bold' | 'italic' | 'underline' | 'strike' | 'code' | 'link';
    attrs?: {
        href?: string;
        target?: string;
        [key: string]: unknown;
    };
}
/**
 * TipTap node representing content in the document tree
 */
interface TipTapNode {
    type: string;
    attrs?: Record<string, unknown>;
    content?: TipTapNode[];
    marks?: TipTapMark[];
    text?: string;
}
/**
 * TipTap document - the root node of rich content
 */
interface TipTapDoc {
    type: 'doc';
    content?: TipTapNode[];
}
/**
 * A message in a conversation
 */
interface Message {
    /**
     * Unique identifier for the message
     */
    id: string;
    /**
     * The message content as plain text (fallback)
     */
    content: string;
    /**
     * Rich content in TipTap JSON format (preferred for rendering)
     * Falls back to `content` if missing or invalid
     */
    rich_content?: TipTapDoc;
    /**
     * Type of the message author
     */
    author_type: MessageAuthorType;
    /**
     * Display name of the message author
     */
    author_name?: string;
    /**
     * ISO timestamp when the message was created
     */
    created_at: string;
    /**
     * Whether this is an internal note (not shown to customer)
     */
    is_private: boolean;
}
/**
 * Status of a support ticket
 */
type TicketStatus = 'new' | 'open' | 'pending' | 'on_hold' | 'resolved';
/**
 * A support ticket in the conversations system
 */
interface Ticket {
    /**
     * Unique identifier for the ticket
     */
    id: string;
    /**
     * Current status of the ticket
     */
    status: TicketStatus;
    /**
     * Preview of the last message
     */
    last_message?: string;
    /**
     * ISO timestamp of the last message
     */
    last_message_at?: string;
    /**
     * Total number of messages in this ticket
     */
    message_count: number;
    /**
     * ISO timestamp when the ticket was created
     */
    created_at: string;
    /**
     * Array of messages (only present in detailed ticket view)
     */
    messages?: Message[];
    /**
     * Number of unread messages from the team
     */
    unread_count?: number;
}
/**
 * Response from sending a message
 */
interface SendMessageResponse {
    /**
     * ID of the ticket this message belongs to
     */
    ticket_id: string;
    /**
     * ID of the newly created message
     */
    message_id: string;
    /**
     * Current status of the ticket
     */
    ticket_status: TicketStatus;
    /**
     * ISO timestamp when the message was created
     */
    created_at: string;
    /**
     * Number of unread messages from the team
     * After customer sends a message, this is always 0
     */
    unread_count: number;
}
/**
 * Response from fetching messages
 */
interface GetMessagesResponse {
    /**
     * ID of the ticket
     */
    ticket_id: string;
    /**
     * Current status of the ticket
     */
    ticket_status: TicketStatus;
    /**
     * Array of messages
     */
    messages: Message[];
    /**
     * Whether there are more messages to fetch
     */
    has_more: boolean;
    /**
     * Number of unread messages from the team
     */
    unread_count: number;
}
/**
 * Response from marking messages as read
 */
interface MarkAsReadResponse {
    /**
     * Whether the operation was successful
     */
    success: boolean;
    /**
     * Number of unread messages (should be 0 after marking as read)
     */
    unread_count: number;
}
/**
 * Options for fetching tickets list
 */
interface GetTicketsOptions {
    /**
     * Filter by ticket status (e.g., 'open', 'closed')
     */
    status?: string;
    /**
     * Number of tickets to return (default: 20)
     */
    limit?: number;
    /**
     * Pagination offset (default: 0)
     */
    offset?: number;
}
/**
 * Response from fetching tickets list
 */
interface GetTicketsResponse {
    /**
     * Total count of tickets
     */
    count: number;
    /**
     * Array of tickets
     */
    results: Ticket[];
}
type RestoreFromTokenStatus = 'success' | 'expired' | 'invalid' | 'used';
/**
 * Response from restore token exchange endpoint
 */
interface RestoreFromTokenResponse {
    /**
     * Restore result
     */
    status: RestoreFromTokenStatus;
    /**
     * Canonical widget session ID to use after restore
     */
    widget_session_id?: string;
    /**
     * Migrated ticket IDs, if any
     */
    migrated_ticket_ids?: string[];
    /**
     * Optional machine-readable backend code
     */
    code?: string;
}
/**
 * Response from self-service restore link request
 */
interface RequestRestoreLinkResponse {
    ok: true;
}
/**
 * User-provided identification data (collected via the widget form)
 */
interface UserProvidedTraits {
    name?: string;
    email?: string;
}

interface LazyLoadedConversationsInterface {
    show: () => void;
    hide: () => void;
    isVisible: () => boolean;
    reset: () => void;
    setIdentity: () => void;
    clearIdentity: () => void;
    sendMessage: (message: string, userTraits?: UserProvidedTraits, newTicket?: boolean) => Promise<SendMessageResponse>;
    getMessages: (ticketId?: string, after?: string) => Promise<GetMessagesResponse>;
    markAsRead: (ticketId?: string) => Promise<MarkAsReadResponse>;
    getTickets: (options?: GetTicketsOptions) => Promise<GetTicketsResponse>;
    requestRestoreLink: (email: string) => Promise<RequestRestoreLinkResponse>;
    restoreFromToken: (restoreToken: string) => Promise<RestoreFromTokenResponse>;
    restoreFromUrlToken: () => Promise<RestoreFromTokenResponse | null>;
    getCurrentTicketId: () => string | null;
    getWidgetSessionId: () => string;
}

type ConversationsManager$1 = LazyLoadedConversationsInterface;

declare class ConversationsManager implements ConversationsManager$1 {
    private readonly _posthog;
    private _config;
    private _persistence;
    private _widgetRef;
    private _containerElement;
    private _currentTicketId;
    private _pollTimeoutId;
    private _pollLoopRunning;
    private _pollGeneration;
    private _consecutivePollingRateLimitFailures;
    private _reattachIntervalId;
    private _lastMessageTimestamp;
    private _isPollingMessages;
    private _isPollingTickets;
    private _unsubscribeIdentifyListener;
    private _unreadCount;
    private _widgetSessionId;
    private _isWidgetEnabled;
    private _isDomainAllowed;
    private _widgetState;
    private _isWidgetRendered;
    private _hasProcessedRestoreToken;
    private _initializeWidgetPromise;
    private _currentView;
    private _tickets;
    private _showTicketList;
    private _consecutivePollingStatusZeroFailures;
    constructor(config: ConversationsRemoteConfig, _posthog: PostHog);
    private _onOnline;
    private _currentUrl;
    /**
     * Send a message programmatically via the API
     * Creates a new ticket if none exists or if newTicket is true
     *
     * @param message - The message text to send
     * @param userTraits - Optional user identification data (name, email)
     * @param newTicket - If true, forces creation of a new ticket (ignores current ticket)
     * @returns Promise with the response including ticket_id and message_id
     */
    sendMessage(message: string, userTraits?: UserProvidedTraits, newTicket?: boolean): Promise<SendMessageResponse>;
    /**
     * Switch to a different ticket if an explicit ticketId is provided
     * This ensures subsequent operations (sendMessage, etc.) use the correct ticket
     */
    private _switchToTicketIfNeeded;
    /** Fetch messages via the API */
    getMessages(ticketId?: string, after?: string): Promise<GetMessagesResponse>;
    /** Mark messages as read via the API */
    markAsRead(ticketId?: string): Promise<MarkAsReadResponse>;
    /**
     * Initialize the conversations manager.
     * Always initializes persistence and event listeners for API usage.
     * Only renders the widget if widgetEnabled is true AND domain is allowed.
     */
    private _initialize;
    private _completeInitialization;
    private _restoreFromTokenWithRetry;
    private _restoreFromToken;
    /**
     * Initialize and render the widget UI
     * Uses a promise guard to prevent race conditions from concurrent calls
     */
    private _initializeWidget;
    private _doInitializeWidget;
    /**
     * Extract name and email from PostHog's stored person properties.
     *
     * Person properties set via posthog.identify() are stored under the
     * $stored_person_properties persistence key, not as top-level props.
     * We check both locations plus the super-properties for completeness.
     */
    private _getPersonTraits;
    /**
     * Get initial user traits from PostHog or localStorage
     */
    private _getInitialUserTraits;
    /**
     * Handle user identification from the widget form
     */
    private _handleIdentify;
    private _handleRequestRestoreLink;
    /**
     * Handle sending a message from the widget
     */
    private _handleSendMessage;
    /**
     * Handle widget state changes
     */
    private _handleStateChange;
    /**
     * Mark messages as read
     */
    private _markMessagesAsRead;
    /**
     * Load messages for the current ticket
     */
    private _loadMessages;
    private _isWidgetOpen;
    /**
     * Poll for new messages
     */
    private _pollMessages;
    /**
     * Poll for tickets list
     */
    private _pollTickets;
    /**
     * Load tickets list from API
     */
    private _loadTickets;
    private _computeShowTicketList;
    private _isCurrentTicketResolved;
    /**
     * Patch the local _tickets cache with a new status for a given ticket and push
     * any UI-relevant changes (resolved lock + list visibility) to the widget.
     */
    private _applyTicketStatusUpdate;
    /**
     * Main poll function that polls based on current view
     */
    private _poll;
    private _trackPollingEndpointReachability;
    /**
     * Track HTTP 429s on polling requests so the loop can back off. Any 2xx
     * clears the streak; a 429 lengthens the next poll delay exponentially.
     */
    private _trackPollingRateLimit;
    /**
     * Handle view changes from the widget
     */
    private _handleViewChange;
    /**
     * Handle ticket selection from the list
     */
    private _handleSelectTicket;
    /**
     * Handle new conversation request
     */
    private _handleNewConversation;
    /**
     * Handle back to tickets request
     */
    private _handleBackToTickets;
    /**
     * Determine initial view based on ticket count
     */
    private _determineInitialView;
    /**
     * Apply a fetched ticket list to internal state and return the appropriate view.
     * Shared by _determineInitialView (widget boot) and _loadTicketsAndReconcileView
     * (identity change at runtime).
     */
    private _applyTicketsToState;
    /**
     * Start the self-scheduling poll loop.
     *
     * Idempotent: safe to call from any state-changing handler (send, restore,
     * identity change, ticket selection). The loop polls immediately, then
     * reschedules itself with a delay derived from widget state and rate-limit
     * backoff (see _nextPollDelayMs), and stops itself when there is nothing to
     * poll (see _hasSomethingToPoll).
     */
    private _startPolling;
    private _pollThenSchedule;
    /**
     * Schedule the next poll, or stop the loop when there is nothing to poll.
     * An idle widget with no conversation generates zero polling traffic.
     */
    private _scheduleNextPoll;
    /**
     * Delay before the next poll: exponential backoff while rate limited,
     * otherwise the open/closed cadence.
     */
    private _nextPollDelayMs;
    /**
     * Whether the current view has anything worth polling for. Messages view
     * needs an active ticket; tickets view needs at least one ticket. The
     * restore-request view never polls.
     */
    private _hasSomethingToPoll;
    /**
     * Stop the poll loop.
     */
    private _stopPolling;
    /**
     * Re-attach the widget container if it has been detached from the DOM.
     *
     * SPA frameworks such as Turbo Drive (Hotwire) and Rails Turbo replace the
     * entire document.body on navigation. That removes our container — which was
     * appended to body in _renderWidget — without any teardown call, so the
     * widget silently disappears until a full page reload. Re-attaching the same
     * element (rather than re-rendering) preserves widget state and avoids
     * refetching messages.
     */
    private _reattachWidgetIfDetached;
    /**
     * Start watching for the widget being detached from the DOM (e.g. by an SPA
     * navigation that replaces document.body) and re-attach it when it happens.
     */
    private _startReattachWatcher;
    /**
     * Stop the DOM re-attach watcher.
     */
    private _stopReattachWatcher;
    /**
     * Setup listener for identify events.
     * When user calls posthog.identify(), hide the identification form
     * since we now know who they are.
     */
    private _setupIdentifyListener;
    /**
     * Show the widget (render it to DOM).
     * The widget respects its saved state (open/closed).
     * Note: Domain restrictions still apply - widget won't render on disallowed domains.
     */
    show(): void;
    /**
     * Hide and remove the widget from the DOM.
     * Conversation data is preserved - call show() to re-render.
     */
    hide(): void;
    /**
     * Check if the widget is currently visible (rendered in DOM)
     */
    isVisible(): boolean;
    /** Get tickets list for the current widget session or verified identity */
    getTickets(options?: GetTicketsOptions): Promise<GetTicketsResponse>;
    requestRestoreLink(email: string): Promise<RequestRestoreLinkResponse>;
    restoreFromToken(restoreToken: string): Promise<RestoreFromTokenResponse>;
    restoreFromUrlToken(): Promise<RestoreFromTokenResponse | null>;
    /**
     * Get the current active ticket ID
     * Returns null if no conversation has been started yet
     */
    getCurrentTicketId(): string | null;
    /**
     * Get the widget session ID (persistent browser identifier)
     * This ID is used for access control and stays the same across page loads
     */
    getWidgetSessionId(): string;
    private _identityFields;
    setIdentity(): void;
    clearIdentity(): void;
    private _resetConversationState;
    private _loadTicketsAndReconcileView;
    /**
     * Clean up the widget
     */
    destroy(): void;
    /**
     * Reset all conversation data and destroy the widget.
     * Called on posthog.reset() to start fresh.
     */
    reset(): void;
    /**
     * Render the widget to the DOM
     */
    private _renderWidget;
}
/**
 * Initialize the conversations widget.
 * This is the entry point called from the lazy-loaded bundle.
 *
 * Singleton guard: only one ConversationsManager per page. The toolbar's
 * internal PostHog instance is excluded upstream (see loadIfEnabled), so
 * this always belongs to the customer's main instance.
 */
declare function initConversations(config: ConversationsRemoteConfig, posthog: PostHog): ConversationsManager;

export { initConversations as default };
