import {
    AddLinkEventTracker,
    GetSessionDataManager,
    GuideOnDutyStatusMessageEvent,
    GuideSessionAttachedMessageEvent,
    GuideSessionCreateMessageComposer,
    GuideSessionDetachedMessageEvent,
    GuideSessionEndedMessageEvent,
    GuideSessionErrorMessageEvent,
    GuideSessionInvitedToGuideRoomMessageEvent,
    GuideSessionMessageMessageEvent,
    GuideSessionOnDutyUpdateMessageComposer,
    GuideSessionPartnerIsTypingMessageEvent,
    GuideSessionStartedMessageEvent,
    ILinkEventTracker,
    PerkAllowancesMessageEvent,
    PerkEnum,
    RemoveLinkEventTracker
} from '@octane/renderer';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { GetConfigurationValue, guardianText, GuideSessionState, GuideToolMessage, GuideToolMessageGroup, isSafeExternalUrl, LocalizeText, SendMessageComposer } from '../../api';
import { OctaneCardContentView, OctaneCardHeaderView, OctaneCardView } from '../../common';
import { GuideToolEvent } from '../../events';
import { useGuardianChatReview, useMessageEvent, useNotification, useUiEvent } from '../../hooks';
import { GuardianChatReviewAcceptView } from './views/GuardianChatReviewAcceptView';
import { GuardianChatReviewResultsView } from './views/GuardianChatReviewResultsView';
import { GuardianChatReviewVoteView } from './views/GuardianChatReviewVoteView';
import { GuideToolAcceptView } from './views/GuideToolAcceptView';
import { GuideToolMenuView } from './views/GuideToolMenuView';
import { GuideToolOngoingView } from './views/GuideToolOngoingView';
import { GuideToolReportView } from './views/GuideToolReportView';
import { GuideToolUserCreateRequestView } from './views/GuideToolUserCreateRequestView';
import { GuideToolUserFeedbackView } from './views/GuideToolUserFeedbackView';
import { GuideToolUserNoHelpersView } from './views/GuideToolUserNoHelpersView';
import { GuideToolUserPendingView } from './views/GuideToolUserPendingView';
import { GuideToolUserSomethingWrogView } from './views/GuideToolUserSomethingWrogView';
import { GuideToolUserThanksView } from './views/GuideToolUserThanksView';
import { NativeTextTree } from '../../common/native-text/NativeTextTree';

export const GuideToolView: FC<{}> = (props) => {
    const [isVisible, setIsVisible] = useState<boolean>(false);
    const [headerText, setHeaderText] = useState<string>(LocalizeText('guide.help.guide.tool.title'));
    const [noCloseButton, setNoCloseButton] = useState<boolean>(false);
    const [sessionState, setSessionState] = useState<string>(GuideSessionState.GUIDE_TOOL_MENU);

    const [isOnDuty, setIsOnDuty] = useState<boolean>(false);
    const isOnDutyRef = useRef(false);
    isOnDutyRef.current = isOnDuty;
    const [isHandlingBullyReports, setIsHandlingBullyReports] = useState<boolean>(false);
    const [isHandlingGuideRequests, setIsHandlingGuideRequests] = useState<boolean>(false);
    const [isHandlingHelpRequests, setIsHandlingHelpRequests] = useState<boolean>(false);

    const [helpersOnDuty, setHelpersOnDuty] = useState<number>(0);
    const [guidesOnDuty, setGuidesOnDuty] = useState<number>(0);
    const [guardiansOnDuty, setGuardiansOnDuty] = useState<number>(0);

    const [userRequest, setUserRequest] = useState<string>('');

    const [helpRequestDescription, setHelpRequestDescription] = useState<string>(null);
    const [helpRequestAverageTime, setHelpRequestAverageTime] = useState<number>(0);

    const [ongoingUserId, setOngoingUserId] = useState<number>(0);
    const [ongoingUsername, setOngoingUsername] = useState<string>(null);
    const [ongoingFigure, setOngoingFigure] = useState<string>(null);
    const [ongoingIsTyping, setOngoingIsTyping] = useState<boolean>(false);
    const [ongoingMessageGroups, setOngoingMessageGroups] = useState<GuideToolMessageGroup[]>([]);
    const [isReporting, setIsReporting] = useState<boolean>(false);
    const [canJudgeChatReviews, setCanJudgeChatReviews] = useState<boolean>(true);

    const { simpleAlert = null } = useNotification();
    const { review, accept: acceptReview, skip: skipReview, vote: voteReview, close: closeReview } = useGuardianChatReview();

    // A review offered to the guardian opens the guide tool on it, as AIR opens the chat review window.
    useEffect(() => {
        if (review.state) setIsVisible(true);
    }, [review.state]);

    const updateSessionState = useCallback((newState: string, replacement?: string) => {
        switch (newState) {
            case GuideSessionState.GUIDE_TOOL_MENU:
                setHeaderText(LocalizeText('guide.help.guide.tool.title'));
                setNoCloseButton(false);
                break;
            case GuideSessionState.GUIDE_ACCEPT:
                setHeaderText(LocalizeText('guide.help.request.guide.accept.title'));
                setNoCloseButton(true);
                break;
            case GuideSessionState.GUIDE_ONGOING:
                setHeaderText(LocalizeText('guide.help.request.guide.ongoing.title', ['name'], [replacement]));
                setNoCloseButton(true);
                break;
            case GuideSessionState.USER_CREATE:
                setHeaderText(LocalizeText('guide.help.request.user.create.title'));
                setNoCloseButton(false);
                break;
            case GuideSessionState.USER_PENDING:
                setHeaderText(LocalizeText('guide.help.request.user.pending.title'));
                setNoCloseButton(true);
                break;
            case GuideSessionState.USER_ONGOING:
                setHeaderText(LocalizeText('guide.help.request.user.ongoing.title', ['name'], [replacement]));
                setNoCloseButton(true);
                break;
            case GuideSessionState.USER_FEEDBACK:
                setHeaderText(LocalizeText('guide.help.request.user.feedback.title'));
                setNoCloseButton(true);
                break;
            case GuideSessionState.USER_THANKS:
                setHeaderText(LocalizeText('guide.help.request.user.thanks.title'));
                setNoCloseButton(false);
                break;
            case GuideSessionState.USER_NO_HELPERS:
                setHeaderText(LocalizeText('guide.help.request.no_tour_guides.heading'));
                setNoCloseButton(false);
                break;
            case GuideSessionState.USER_SOMETHING_WRONG:
                setHeaderText(LocalizeText('guide.help.request.user.guide.disconnected.error.heading'));
                setNoCloseButton(false);
                break;
        }

        setSessionState(newState);
        setIsVisible(true);
    }, []);

    const onGuideToolEvent = useCallback(
        (event: GuideToolEvent) => {
            switch (event.type) {
                case GuideToolEvent.SHOW_GUIDE_TOOL:
                    setIsVisible(true);
                    return;
                case GuideToolEvent.HIDE_GUIDE_TOOL:
                    setIsVisible(false);
                    return;
                case GuideToolEvent.TOGGLE_GUIDE_TOOL:
                    setIsVisible((value) => !value);
                    return;
                case GuideToolEvent.CREATE_HELP_REQUEST:
                    updateSessionState(GuideSessionState.USER_CREATE);
                    return;
            }
        },
        [updateSessionState]
    );

    useUiEvent(GuideToolEvent.SHOW_GUIDE_TOOL, onGuideToolEvent);
    useUiEvent(GuideToolEvent.HIDE_GUIDE_TOOL, onGuideToolEvent);
    useUiEvent(GuideToolEvent.TOGGLE_GUIDE_TOOL, onGuideToolEvent);
    useUiEvent(GuideToolEvent.CREATE_HELP_REQUEST, onGuideToolEvent);

    useMessageEvent<PerkAllowancesMessageEvent>(PerkAllowancesMessageEvent, (event) => {
        const parser = event.getParser();

        setCanJudgeChatReviews(parser.isAllowed(PerkEnum.JUDGE_CHAT_REVIEWS));

        if (!parser.isAllowed(PerkEnum.USE_GUIDE_TOOL) && isOnDuty) {
            setIsOnDuty(false);
            SendMessageComposer(new GuideSessionOnDutyUpdateMessageComposer(false, false, false, false));
        }
    });

    useMessageEvent<GuideOnDutyStatusMessageEvent>(GuideOnDutyStatusMessageEvent, (event) => {
        const parser = event.getParser();

        setIsOnDuty(parser.onDuty);
        setGuidesOnDuty(parser.guidesOnDuty);
        setHelpersOnDuty(parser.helpersOnDuty);
        setGuardiansOnDuty(parser.guardiansOnDuty);
    });

    useMessageEvent<GuideSessionAttachedMessageEvent>(GuideSessionAttachedMessageEvent, (event) => {
        const parser = event.getParser();

        setHelpRequestDescription(parser.helpRequestDescription);
        setHelpRequestAverageTime(parser.roleSpecificWaitTime);

        if (parser.asGuide && isOnDuty) updateSessionState(GuideSessionState.GUIDE_ACCEPT);

        if (!parser.asGuide) updateSessionState(GuideSessionState.USER_PENDING);
    });

    useMessageEvent<GuideSessionStartedMessageEvent>(GuideSessionStartedMessageEvent, (event) => {
        const parser = event.getParser();

        if (isOnDuty) {
            setOngoingUserId(parser.requesterUserId);
            setOngoingUsername(parser.requesterName);
            setOngoingFigure(parser.requesterFigure);
            updateSessionState(GuideSessionState.GUIDE_ONGOING, parser.requesterName);
        } else {
            setOngoingUserId(parser.guideUserId);
            setOngoingUsername(parser.guideName);
            setOngoingFigure(parser.guideFigure);
            updateSessionState(GuideSessionState.USER_ONGOING, parser.guideName);
        }
    });

    useMessageEvent<GuideSessionPartnerIsTypingMessageEvent>(GuideSessionPartnerIsTypingMessageEvent, (event) => {
        const parser = event.getParser();

        setOngoingIsTyping(parser.isTyping);
    });

    useMessageEvent<GuideSessionMessageMessageEvent>(GuideSessionMessageMessageEvent, (event) => {
        const parser = event.getParser();

        const messageGroups = [...ongoingMessageGroups];

        let lastGroup = messageGroups[messageGroups.length - 1];

        if (!lastGroup || lastGroup.userId !== parser.senderId) {
            lastGroup = new GuideToolMessageGroup(parser.senderId);
            messageGroups.push(lastGroup);
        }

        lastGroup.addChat(new GuideToolMessage(parser.chatMessage));
        setOngoingMessageGroups(messageGroups);
    });

    useMessageEvent<GuideSessionInvitedToGuideRoomMessageEvent>(GuideSessionInvitedToGuideRoomMessageEvent, (event) => {
        const parser = event.getParser();

        if (parser.roomId !== 0) {
            const messageGroups = [...ongoingMessageGroups];

            let lastGroup = messageGroups[messageGroups.length - 1];

            const guideId = isOnDuty ? GetSessionDataManager().userId : ongoingUserId;

            if (!lastGroup || lastGroup.userId !== guideId) {
                lastGroup = new GuideToolMessageGroup(guideId);
                messageGroups.push(lastGroup);
            }

            lastGroup.addChat(new GuideToolMessage(parser.roomName, parser.roomId));
            setOngoingMessageGroups(messageGroups);
        }
    });

    useMessageEvent<GuideSessionEndedMessageEvent>(GuideSessionEndedMessageEvent, (event) => {
        if (isOnDuty) {
            setOngoingUserId(0);
            setOngoingUsername(null);
            setOngoingFigure(null);
            setOngoingIsTyping(false);
            setOngoingMessageGroups([]);
            updateSessionState(GuideSessionState.GUIDE_TOOL_MENU);
        } else {
            updateSessionState(GuideSessionState.USER_FEEDBACK);
        }
    });

    useMessageEvent<GuideSessionErrorMessageEvent>(GuideSessionErrorMessageEvent, (event) => {
        const parser = event.getParser();

        // SOMETHING_WRONG_REQUEST = 0, NO_HELPERS_AVAILABLE = 1, NO_GUARDIANS_AVAILABLE = 2 / 3

        switch (parser['errorCode']) {
            case 0:
                updateSessionState(GuideSessionState.USER_SOMETHING_WRONG);
                break;
            case 1:
                updateSessionState(GuideSessionState.USER_NO_HELPERS);
                break;
            case 2:
            case 3:
                // AIR setStateClosedWithNotification: the bully report went to the moderators instead.
                simpleAlert(
                    guardianText('guide.bully.request.error.not_enough_guardians.message'),
                    null,
                    null,
                    null,
                    guardianText('guide.bully.request.error.not_enough_guardians.title')
                );
                break;
        }
    });

    useMessageEvent<GuideSessionDetachedMessageEvent>(GuideSessionDetachedMessageEvent, (event) => {
        setOngoingUserId(0);
        setOngoingUsername(null);
        setOngoingFigure(null);
        setOngoingIsTyping(false);
        setOngoingMessageGroups([]);

        if (isOnDuty) {
            updateSessionState(GuideSessionState.GUIDE_TOOL_MENU);
        } else {
            updateSessionState(GuideSessionState.USER_THANKS);
        }
    });

    useEffect(() => {
        const linkTracker: ILinkEventTracker = {
            linkReceived: (url: string) => {
                const parts = url.split('/');

                if (parts.length < 2) return;

                switch (parts[1]) {
                    case 'tour':
                        // Official HabboHelp.requestGuide: a tour request (type 0) with the tour text.
                        if (!GetConfigurationValue<boolean>('guides.enabled')) return;

                        SendMessageComposer(new GuideSessionCreateMessageComposer(0, LocalizeText('guide.help.request.tour.description')));
                        return;
                }
            },
            eventUrlPrefix: 'help/'
        };

        AddLinkEventTracker(linkTracker);

        return () => RemoveLinkEventTracker(linkTracker);
    }, []);

    const processAction = useCallback(
        (action: string) => {
            switch (action) {
                case 'close':
                    setIsVisible(false);
                    setIsReporting(false);
                    setUserRequest('');
                    setSessionState(GuideSessionState.GUIDE_TOOL_MENU);
                    return;
                case 'toggle_duty':
                    if (!isHandlingBullyReports && !isHandlingGuideRequests && !isHandlingHelpRequests) {
                        simpleAlert(
                            LocalizeText('guide.help.guide.tool.noqueueselected.message'),
                            null,
                            null,
                            null,
                            LocalizeText('guide.help.guide.tool.noqueueselected.caption'),
                            null
                        );
                        return;
                    }

                    // Toggled from a ref, not inside a state updater: an updater may run twice (StrictMode).
                    const wasOnDuty = isOnDutyRef.current;
                    isOnDutyRef.current = !wasOnDuty;
                    setIsOnDuty(!wasOnDuty);
                    SendMessageComposer(
                        new GuideSessionOnDutyUpdateMessageComposer(
                            !wasOnDuty,
                            wasOnDuty ? false : isHandlingGuideRequests,
                            wasOnDuty ? false : isHandlingHelpRequests,
                            wasOnDuty ? false : isHandlingBullyReports
                        )
                    );

                    return;
                case 'forum_link':
                    const url: string = GetConfigurationValue<string>('group.homepage.url', '').replace(
                        '%groupid%',
                        GetConfigurationValue<string>('guide.help.alpha.groupid', '0')
                    );
                    // A hotel without a group homepage set opened an empty tab; only a web URL is opened.
                    if (isSafeExternalUrl(url)) window.open(url, '_blank', 'noopener,noreferrer');
                    return;
            }
        },
        [isHandlingBullyReports, isHandlingGuideRequests, isHandlingHelpRequests, simpleAlert]
    );

    if (!isVisible) return null;

    if (review.state) {
        const reviewHeader: Record<string, string> = {
            [GuideSessionState.GUARDIAN_CHAT_REVIEW_ACCEPT]: 'guide.bully.request.guide.accept.title',
            [GuideSessionState.GUARDIAN_CHAT_REVIEW_WAIT_FOR_VOTERS]: 'guide.bully.request.guide.vote.title',
            [GuideSessionState.GUARDIAN_CHAT_REVIEW_VOTE]: 'guide.bully.request.guide.vote.title',
            [GuideSessionState.GUARDIAN_CHAT_REVIEW_WAIT_FOR_RESULTS]: 'guide.bully.request.guide.results.title',
            [GuideSessionState.GUARDIAN_CHAT_REVIEW_RESULTS]: 'guide.bully.request.guide.results.final.title'
        };
        // Only the two result windows have a close button (AIR openWindow(..., true)).
        const reviewClosable =
            review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_WAIT_FOR_RESULTS || review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_RESULTS;

        return (
            <NativeTextTree>
                <OctaneCardView className="octane-guide-tool octane-guardian-review" theme="primary-slim">
                    <OctaneCardHeaderView headerText={guardianText(reviewHeader[review.state])} noCloseButton={!reviewClosable} onCloseClick={closeReview} />
                    <OctaneCardContentView className="text-black">
                        {review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_ACCEPT && (
                            <GuardianChatReviewAcceptView
                                countdownId={review.countdownId}
                                countdownSeconds={review.countdownSeconds}
                                onAccept={acceptReview}
                                onSkip={skipReview}
                            />
                        )}
                        {(review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_WAIT_FOR_VOTERS || review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_VOTE) && (
                            <GuardianChatReviewVoteView
                                countdownId={review.countdownId}
                                countdownSeconds={review.countdownSeconds}
                                record={review.record}
                                waiting={review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_WAIT_FOR_VOTERS}
                                onClose={closeReview}
                                onVote={voteReview}
                            />
                        )}
                        {(review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_WAIT_FOR_RESULTS || review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_RESULTS) && (
                            <GuardianChatReviewResultsView
                                final={review.state === GuideSessionState.GUARDIAN_CHAT_REVIEW_RESULTS}
                                ownVote={review.ownVote}
                                statuses={review.statuses}
                                winningVote={review.winningVote}
                                onClose={closeReview}
                            />
                        )}
                    </OctaneCardContentView>
                </OctaneCardView>
            </NativeTextTree>
        );
    }

    // AIR offers "report" in the ongoing windows of both sides and in the user's feedback window.
    const canReport = [GuideSessionState.GUIDE_ONGOING, GuideSessionState.USER_ONGOING, GuideSessionState.USER_FEEDBACK].includes(sessionState);

    return (
        <NativeTextTree>
            <>
                {isReporting && canReport && <GuideToolReportView onClose={() => setIsReporting(false)} />}
                <OctaneCardView className="octane-guide-tool" theme="primary-slim">
                    <OctaneCardHeaderView headerText={headerText} noCloseButton={noCloseButton} onCloseClick={(event) => processAction('close')} />
                    <OctaneCardContentView className="text-black">
                        {sessionState === GuideSessionState.GUIDE_TOOL_MENU && (
                            <GuideToolMenuView
                                canJudgeChatReviews={canJudgeChatReviews}
                                guardiansOnDuty={guardiansOnDuty}
                                guidesOnDuty={guidesOnDuty}
                                helpersOnDuty={helpersOnDuty}
                                isHandlingBullyReports={isHandlingBullyReports}
                                isHandlingGuideRequests={isHandlingGuideRequests}
                                isHandlingHelpRequests={isHandlingHelpRequests}
                                isOnDuty={isOnDuty}
                                processAction={processAction}
                                setIsHandlingBullyReports={setIsHandlingBullyReports}
                                setIsHandlingGuideRequests={setIsHandlingGuideRequests}
                                setIsHandlingHelpRequests={setIsHandlingHelpRequests}
                            />
                        )}
                        {sessionState === GuideSessionState.GUIDE_ACCEPT && (
                            <GuideToolAcceptView helpRequestAverageTime={helpRequestAverageTime} helpRequestDescription={helpRequestDescription} />
                        )}
                        {[GuideSessionState.GUIDE_ONGOING, GuideSessionState.USER_ONGOING].includes(sessionState) && (
                            <GuideToolOngoingView
                                isGuide={isOnDuty}
                                isTyping={ongoingIsTyping}
                                messageGroups={ongoingMessageGroups}
                                userFigure={ongoingFigure}
                                userId={ongoingUserId}
                                userName={ongoingUsername}
                                onReport={() => setIsReporting(true)}
                            />
                        )}
                        {sessionState === GuideSessionState.USER_CREATE && (
                            <GuideToolUserCreateRequestView setUserRequest={setUserRequest} userRequest={userRequest} />
                        )}
                        {sessionState === GuideSessionState.USER_PENDING && (
                            <GuideToolUserPendingView helpRequestAverageTime={helpRequestAverageTime} helpRequestDescription={helpRequestDescription} />
                        )}
                        {sessionState === GuideSessionState.USER_FEEDBACK && (
                            <GuideToolUserFeedbackView userName={ongoingUsername} onReport={() => setIsReporting(true)} />
                        )}
                        {sessionState === GuideSessionState.USER_THANKS && <GuideToolUserThanksView />}
                        {sessionState === GuideSessionState.USER_NO_HELPERS && <GuideToolUserNoHelpersView />}
                        {sessionState === GuideSessionState.USER_SOMETHING_WRONG && <GuideToolUserSomethingWrogView />}
                    </OctaneCardContentView>
                </OctaneCardView>
            </>
        </NativeTextTree>
    );
};
