import { CSSProperties } from 'react';
import { JSX } from 'react/jsx-runtime';
import { ReactNode } from 'react';
import Talk from 'talkjs';

export declare function Chatbox(props: ChatboxProps): JSX.Element;

declare type ChatboxProps = UIBoxProps<Talk.Chatbox> & Talk.ChatboxOptions & {
    highlightedWords?: FirstParameter<Talk.Chatbox["setHighlightedWords"]>;
    chatboxRef?: React.MutableRefObject<Talk.Chatbox | undefined>;
    loadingComponent?: ReactNode;
    style?: CSSProperties;
    className?: string;
};

declare type ConversationProps = {
    conversationId: string | null | undefined;
    syncConversation?: never;
} | {
    conversationId?: never;
    syncConversation: Talk.ConversationBuilder | ((session: Talk.Session) => Talk.ConversationBuilder);
};

declare type EventNames<T extends Talk.UIBox | Talk.Session> = Exclude<Extract<keyof T, `on${string}`>, "on" | "onCustomConversationAction" | "onCustomMessageAction">;

declare type Events<T extends Talk.UIBox | Talk.Session> = {
    [P in EventNames<T>]?: T[P] extends Func ? FirstParameter<T[P]> : never;
};

declare type FirstParameter<T extends Func> = Parameters<T>[0];

declare type Func = (...args: any) => any;

export declare function Inbox(props: InboxProps): JSX.Element;

declare type InboxProps = Partial<UIBoxProps<Talk.Inbox>> & Omit<Talk.InboxOptions, "selected"> & {
    highlightedWords?: FirstParameter<Talk.Inbox["setHighlightedWords"]>;
    inboxRef?: React.MutableRefObject<Talk.Inbox | undefined>;
    loadingComponent?: ReactNode;
    style?: CSSProperties;
    className?: string;
};

export declare function Popup(props: PopupProps): JSX.Element | null;

declare type PopupProps = UIBoxProps<Talk.Popup> & Talk.PopupOptions & {
    highlightedWords?: Parameters<Talk.Popup["setHighlightedWords"]>[0];
    popupRef?: React.MutableRefObject<Talk.Popup | undefined>;
    show?: boolean;
};

export declare function Session(props: SessionProps): JSX.Element;

declare type SessionEvents = Events<Talk.Session> & {
    onUnreadsChange?: (messages: Talk.UnreadConversation[]) => void;
};

declare type SessionProps = UserProps & SessionEvents & Pick<Talk.Session, "appId"> & {
    sessionRef?: React.MutableRefObject<Talk.Session | undefined>;
    desktopNotificationEnabled?: boolean;
    children?: ReactNode;
    token?: string;
    tokenFetcher?: () => Promise<string>;
    signature?: string;
};

declare type UIBoxEvents<T extends Talk.UIBox> = Events<T> & {
    onCustomMessageAction?: (event: Talk.MessageActionEvent) => void;
    onCustomConversationAction?: (event: Talk.ConversationActionEvent) => void;
};

declare type UIBoxProps<T extends Talk.UIBox> = UIBoxEvents<T> & ConversationProps & {
    asGuest?: boolean;
};

declare type UserProps = {
    userId: string;
    syncUser?: undefined;
} | {
    userId?: undefined;
    syncUser: Talk.User | (() => Talk.User);
};

/**
 * Returns the currently active TalkJS Session.
 *
 * @remarks
 * Can only be used in child components of <Session>. This hook lets you
 * directly access the TalkJS
 * {@link https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Session/ | Session}
 * object.
 *
 * @returns The currently active session, or undefined if there is no current
 * session (either because it's still loading, or because it has been destroyed)
 *
 * Note: If you use the returned session asynchonously (eg inside a useEffect),
 * you must check its
 * {@link https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Session/#Session__isAlive | isAlive}
 * property just before using it. Trying to use a session that is no longer
 * alive will throw an error.
 *
 * During development in particular, sessions can be destroyed and recreated a
 * lot, eg due to React.StrictMode or due to hot-module reloading. Your effect
 * may run after a session has been destroyed but before it has been recreated.
 */
export declare function useSession(): Talk.Session | undefined;

/**
 * Returns conversations with unread messages.
 *
 * @remarks
 * Can only be used in child components of <Session>.
 *
 * @returns A list of {@link https://talkjs.com/docs/Reference/JavaScript_Chat_SDK/Session/#UnreadConversation | UnreadConversation} objects,
 * or undefined if this hook is used outside of the <Session> component, or the session is not alive for any other reason.
 * During development in particular, sessions can be destroyed and recreated a
 * lot, eg due to React.StrictMode or due to hot-module reloading.
 */
export declare function useUnreads(): Talk.UnreadConversation[] | undefined;

export { }
