import { FollowFriendMessageComposer, GetSessionDataManager } from '@octane/renderer';
import { FC, KeyboardEvent, UIEvent, useEffect, useMemo, useRef, useState } from 'react';
import { FaArrowLeft, FaCopy, FaExclamationTriangle, FaSearch, FaTimes, FaUsers } from 'react-icons/fa';
import { CopyToClipboard, GetUserProfile, LocalizeText, MessengerConversation, MessengerMessage, MessengerThread, ReportType, SendMessageComposer, selectConversations, selectMessages } from '../../../../api';
import { DraggableWindowPosition, LayoutAvatarImageView, OctaneCardContentView, OctaneCardHeaderView, OctaneCardView } from '../../../../common';
import { useFriends, useHelp } from '../../../../hooks';
import { STAFF_CHAT_FIGURE } from '../../staffChatIdentity';
import { resolveAvatarFigure } from '../friends-list/resolveAvatarFigure';
import { MessengerMessageStatusView } from './MessengerMessageStatusView';
import { FriendsMessengerThreadView } from './messenger-thread/FriendsMessengerThreadView';
import { canFollowMessengerConversation, filterMessengerConversations, resolveConversationAfterClose, restoreConversationsWithNewMessages } from './persistentMessenger.helpers';
import { BaseButton } from '../../../../common/BaseButton';
import { TabBar, TabBarTab } from '../../../../common/TabBar';
import { Hint } from '../../../../common/Tooltip';
import { NativeTextTree } from '../../../../common/native-text/NativeTextTree';

interface PersistentMessengerApi
{
    state: any;
    history: { loadInitial: (conversationId: number) => void; loadOlder: (conversationId: number) => void };
    actions: { openDirectConversation: (participantId: number, name: string) => void; sendMessage: (conversationId: number, recipientId: number, text: string) => any; retryMessage: (clientId: string) => any; markRead: (conversationId: number, messageId: number) => void };
}

interface FriendsPersistentMessengerViewProps
{
    messenger: PersistentMessengerApi;
    legacyStaffThread?: MessengerThread;
    onClose: () => void;
    onCloseStaff: () => void;
    onSendStaff: (text: string) => void;
}

export const FriendsPersistentMessengerView: FC<FriendsPersistentMessengerViewProps> = ({ messenger, legacyStaffThread = null, onClose, onCloseStaff, onSendStaff }) =>
{
    const conversations = useMemo(() => selectConversations(messenger.state), [ messenger.state.conversationIds, messenger.state.conversationsById ]);
    const [ activeId, setActiveId ] = useState(conversations[0]?.id || 0);
    const [ mode, setMode ] = useState<'conversation' | 'inbox'>('conversation');
    const [ query, setQuery ] = useState('');
    const [ text, setText ] = useState('');
    const [ staffActive, setStaffActive ] = useState(false);
    const [ hiddenConversationIds, setHiddenConversationIds ] = useState<number[]>([]);
    const previousConversationsRef = useRef(conversations);
    const { getFriend } = useFriends();
    const { report } = useHelp();
    const active = conversations.find(item => item.id === activeId) || conversations[0] || null;
    const messages = active ? selectMessages(messenger.state, active.id) : [];
    const filtered = useMemo(() => filterMessengerConversations(conversations, query), [ conversations, query ]);
    const visibleConversations = useMemo(() => conversations.filter(conversation => hiddenConversationIds.indexOf(conversation.id) === -1), [ conversations, hiddenConversationIds ]);

    useEffect(() => { if(active) messenger.history.loadInitial(active.id); }, [ active?.id ]);
    useEffect(() => setStaffActive(!!legacyStaffThread), [ legacyStaffThread ]);
    useEffect(() =>
    {
        if(!messenger.state.selectedConversationId) return;
        setHiddenConversationIds(current => current.filter(id => id !== messenger.state.selectedConversationId));
        setStaffActive(false);
        setActiveId(messenger.state.selectedConversationId);
        setMode('conversation');
    }, [ messenger.state.selectedConversationId ]);
    useEffect(() =>
    {
        setHiddenConversationIds(current => restoreConversationsWithNewMessages(current, previousConversationsRef.current, conversations));
        previousConversationsRef.current = conversations;
    }, [ conversations ]);
    useEffect(() =>
    {
        const last = messages.filter(message => message.id > 0).at(-1);
        if(active && last) messenger.actions.markRead(active.id, last.id);
    }, [ active?.id, messages.length ]);

    const selectConversation = (conversation: MessengerConversation) =>
    {
        setStaffActive(false);
        setHiddenConversationIds(current => current.filter(id => id !== conversation.id));
        setActiveId(conversation.id);
        setMode('conversation');
    };
    const closeConversation = () =>
    {
        if(!active) return;
        const nextHiddenIds = hiddenConversationIds.indexOf(active.id) === -1 ? [ ...hiddenConversationIds, active.id ] : hiddenConversationIds;
        const nextId = resolveConversationAfterClose(conversations, nextHiddenIds, active.id);
        setHiddenConversationIds(nextHiddenIds);
        setActiveId(nextId);
        setMode(nextId ? 'conversation' : 'inbox');
    };
    const send = () =>
    {
        const value = text.trim();
        if(staffActive)
        {
            if(!legacyStaffThread || !value) return;
            onSendStaff(value);
            setText('');
            return;
        }
        if(!active || !value) return;
        messenger.actions.sendMessage(active.id, active.participantId, value);
        setText('');
    };
    const keyDown = (event: KeyboardEvent<HTMLInputElement>) => { if(event.key === 'Enter') send(); };
    const scroll = (event: UIEvent<HTMLDivElement>) => { if(active && event.currentTarget.scrollTop < 48) messenger.history.loadOlder(active.id); };

    const avatar = (conversation: MessengerConversation, size: number, padding: number = 1) =>
    {
        if(conversation.type === 1) return <LayoutAvatarImageView figure={STAFF_CHAT_FIGURE} headOnly compactHead compactHeadSize={size} compactHeadPadding={padding} direction={3} />;
        const friend = conversation.participantId > 0 ? getFriend(conversation.participantId) : null;
        if(!friend) return null;
        return <LayoutAvatarImageView figure={resolveAvatarFigure(friend.figure, friend.gender)} headOnly compactHead compactHeadSize={size} compactHeadPadding={padding} direction={2} />;
    };

    return (
        <NativeTextTree>
            <OctaneCardView className="messenger-card messenger-persistent" theme="primary-slim" uniqueKey="persistent-messenger" windowPosition={DraggableWindowPosition.TOP_CENTER} offsetTop={8} isResizable={false}>
                <OctaneCardHeaderView headerText={LocalizeText('messenger.window.title', [ 'OPEN_CHAT_COUNT' ], [ (conversations.length + (legacyStaffThread ? 1 : 0)).toString() ])} onCloseClick={onClose} />
                <OctaneCardContentView className="p-0" gap={0} overflow="hidden">
                    <div className="messenger-card-body">
                        <TabBar className="messenger-avatar-bar" value={staffActive ? 'staff' : active ? `conversation-${ active.id }` : null}>
                            {legacyStaffThread && <TabBarTab value="staff" className={`messenger-avatar-tab ${ staffActive ? 'active' : '' } ${ legacyStaffThread.unread ? 'unread' : '' }`} onClick={() => { setStaffActive(true); setMode('conversation'); }} aria-label={legacyStaffThread.participant.name}><LayoutAvatarImageView figure={STAFF_CHAT_FIGURE} headOnly compactHead compactHeadSize={36} compactHeadPadding={0} direction={3} /></TabBarTab>}
                            {visibleConversations.slice(0, 7).map(conversation => (
                                <TabBarTab key={conversation.id} value={`conversation-${ conversation.id }`} className={`messenger-avatar-tab ${ active?.id === conversation.id ? 'active' : '' } ${ conversation.unreadCount ? 'unread' : '' }`} onClick={() => selectConversation(conversation)} aria-label={conversation.name}>
                                    {avatar(conversation, 36, 0)}
                                    {conversation.unreadCount > 0 && <span className="messenger-unread-badge">{conversation.unreadCount}</span>}
                                </TabBarTab>
                            ))}
                            <BaseButton className="messenger-all-button" onClick={() => { setStaffActive(false); setMode('inbox'); }}>{LocalizeText('generic.all')} {conversations.length + (legacyStaffThread ? 1 : 0)}</BaseButton>
                        </TabBar>

                        {mode === 'inbox' && (
                            <div className="messenger-inbox" data-testid="messenger-inbox">
                                <label className="messenger-search"><FaSearch /><input value={query} onChange={event => setQuery(event.target.value)} placeholder={LocalizeText('generic.search')} /></label>
                                <div className="messenger-inbox-list">
                                    {filtered.map(conversation => <BaseButton key={conversation.id} className="messenger-inbox-row" onClick={() => selectConversation(conversation)}><span className="messenger-inbox-avatar">{avatar(conversation, 24) || <FaUsers />}</span><span><b>{conversation.name}</b><small>{conversation.unreadCount ? `${ conversation.unreadCount } ${ LocalizeText('notifications.messages') }` : LocalizeText('messenger.window.input.default', [ 'FRIEND_NAME' ], [ conversation.name ])}</small></span></BaseButton>)}
                                </div>
                            </div>
                        )}

                        {mode === 'conversation' && staffActive && legacyStaffThread && (
                            <div className="messenger-conversation" data-testid="messenger-staff-conversation">
                                <div className="messenger-thread-header"><span className="messenger-thread-name">{legacyStaffThread.participant.name}</span><div className="messenger-actions"><Hint content={LocalizeText('generic.close')}><BaseButton className="messenger-btn close-conversation" onClick={onCloseStaff} aria-label={LocalizeText('generic.close')}><FaTimes /></BaseButton></Hint></div></div>
                                <div className="chat-messages"><FriendsMessengerThreadView thread={legacyStaffThread} /></div>
                                <div className="messenger-input-row"><input maxLength={255} value={text} onChange={event => setText(event.target.value)} onKeyDown={keyDown} placeholder={LocalizeText('messenger.window.input.default', [ 'FRIEND_NAME' ], [ legacyStaffThread.participant.name ])} /><BaseButton className="messenger-btn send" onClick={send}>{LocalizeText('widgets.chatinput.say')}</BaseButton></div>
                            </div>
                        )}

                        {mode === 'conversation' && !staffActive && active && (
                            <div className="messenger-conversation" data-testid="messenger-conversation">
                                <div className="messenger-thread-header">
                                    <BaseButton className="messenger-mobile-back" onClick={() => setMode('inbox')}><FaArrowLeft /></BaseButton>
                                    <span className="messenger-thread-name">{active.name}</span>
                                    <div className="messenger-actions">
                                        {canFollowMessengerConversation(active) && <BaseButton className="messenger-btn" onClick={() => SendMessageComposer(new FollowFriendMessageComposer(active.participantId))}>{LocalizeText('friendlist.follow')}</BaseButton>}
                                        {active.participantId > 0 && <BaseButton className="messenger-btn" onClick={() => GetUserProfile(active.participantId)}>{LocalizeText('extendedprofile.caption')}</BaseButton>}
                                        {active.participantId > 0 && <BaseButton className="messenger-btn danger" onClick={() => report(ReportType.IM, { reportedUserId: active.participantId })}><FaExclamationTriangle /></BaseButton>}
                                        <Hint content={LocalizeText('generic.close')}><BaseButton className="messenger-btn close-conversation" onClick={closeConversation} aria-label={LocalizeText('generic.close')}><FaTimes /></BaseButton></Hint>
                                    </div>
                                </div>
                                <div className="chat-messages persistent-message-list" onScroll={scroll}>
                                    {messenger.state.historyByConversation[active.id]?.loading && <div className="messenger-history-state">{LocalizeText('generic.loading')}</div>}
                                    {messages.map((message: MessengerMessage) => {
                                        const own = message.senderId === GetSessionDataManager().userId;
                                        return <div key={message.id || message.clientId} className={`persistent-message ${ own ? 'own' : 'incoming' }`}><div className="persistent-message-bubble"><span>{message.message}</span><Hint content="Copy"><BaseButton aria-label="Copy" onClick={() => void CopyToClipboard(message.message)}><FaCopy /></BaseButton></Hint></div><small className="persistent-message-meta"><span>{new Date(message.createdAt * 1000).toLocaleTimeString()}</span>{own && message.status !== 'failed' && message.status !== 'sending' && <MessengerMessageStatusView isRead={message.status === 'read'} />}{own && message.status === 'failed' && <span className="messenger-message-failed">!</span>}{own && message.status === 'sending' && <span className="messenger-message-sending">…</span>}</small>{message.status === 'failed' && message.clientId && <BaseButton className="messenger-retry" onClick={() => messenger.actions.retryMessage(message.clientId)}>Retry</BaseButton>}</div>;
                                    })}
                                </div>
                                <div className="messenger-input-row"><input maxLength={255} value={text} onChange={event => setText(event.target.value)} onKeyDown={keyDown} placeholder={LocalizeText('messenger.window.input.default', [ 'FRIEND_NAME' ], [ active.name ])} /><BaseButton className="messenger-btn send" onClick={send}>{LocalizeText('widgets.chatinput.say')}</BaseButton></div>
                            </div>
                        )}
                    </div>
                </OctaneCardContentView>
            </OctaneCardView>
        </NativeTextTree>
    );
};
