import { MancalaGame, Pit } from 'mancala.js';
import * as React from 'react';
import { FunctionComponent, useState } from 'react';
import { useNavigate, useParams } from 'react-router';
import { Link } from 'react-router-dom';
import { v4 } from 'uuid';
import { PitAnimator } from '@mancala/core';
import BoardToolbar from '../components/board/BoardToolbar';
import BoardView from '../components/board/BoardView';
import Button from '../components/Button';
import HeaderBar from '../components/headerbar/HeaderBar';
import HeaderbarIcon from '../components/headerbar/HeaderbarIcon';
import HeaderbarTitle from '../components/headerbar/HeaderbarTitle';
import ThemeSwitchMenu from '../components/headerbar/ThemeSwitchMenu';
import InfoPanel from '../components/InfoPanel';
import LoadingComponent from '../components/LoadingComponent';
import PageContainer from '../components/PageContainer';
import Row from '../components/Row';
import UserStatus from '../components/UserStatus';
import { channel_on_game_update, channel_on_game_crashed, channel_on_game_user_leave, channel_on_user_connection_change, channel_leave_game, channel_game_move, channel_listen_game_events, channel_unlisten_game_events } from '@mancala/core';
import { Context } from '../context/context';
import useWindowDimensions from '../hooks/useWindowDimensions';
import { GameMove, LoadingState, Game, GameUsersConnectionInfo } from "@mancala/core";
import { Theme } from '@mancala/core';
import { getColorByBrightness } from "@mancala/core";
import { BoardViewModel } from "@mancala/core";
import Center from '../components/Center';
import notyf from '../util/Notyf';
import swal from 'sweetalert';
import Util from '../util/Util';
import { useTranslation } from 'react-i18next';

const GamePage: FunctionComponent<{
    context: Context,
    userKey?: string,
    theme: Theme,
}> = ({ context, userKey, theme }) => {
    let params = useParams<{ gameId: string }>();

    const [game, setGame] = useState<Game | undefined>(undefined);

    const [userKeyWhoLeave, setUserKeyWhoLeave] = useState<string | undefined>(undefined);

    const [boardViewModel, setBoardViewModel] = useState<BoardViewModel | undefined>(undefined);

    const [boardId, setBoardId] = useState<string>("-1");

    const [pitAnimator, setPitAnimator] = useState<PitAnimator | undefined>(undefined);

    // It is a flag for ongoing action such as send game move.
    // We have to block future actions if there is an ongoing action.
    const [hasOngoingAction, setHasOngoingAction] = useState<boolean>(false);

    const [gameUsersConnectionInfo, setGameUsersConnectionInfo] = useState<GameUsersConnectionInfo | undefined>();

    const { height, width } = useWindowDimensions();

    const navigate = useNavigate();

    const { t } = useTranslation();

    const [gameLoadingState, setLoadingStateGame] = useState<LoadingState<Game>>(LoadingState.Unset());


    const checkIsSpectator = (game: Game) => userKey !== game.mancalaGame.player1Id && userKey !== game.mancalaGame.player2Id;

    const mancalaGame: MancalaGame | undefined = game?.mancalaGame;
    const isSpectator = game ? checkIsSpectator(game) : undefined;
    const isPlayer2 = !isSpectator && userKey === mancalaGame?.player2Id;

    const onGameUpdate = (pitAnimator: PitAnimator, newGame: Game) => {
        setGame(newGame);
        pitAnimator.setUpdatedGame(newGame);
        setHasOngoingAction(false);
        setGameUsersConnectionInfo(newGame.gameUsersConnectionInfo);
    }

    const isUserOnline = (userId: string) => {
        if (!gameUsersConnectionInfo) return false;
        const user1ConnectionInfo = gameUsersConnectionInfo.user1ConnectionInfo;
        const user2ConnectionInfo = gameUsersConnectionInfo.user2ConnectionInfo;
        if (user1ConnectionInfo.userId === userId) return user1ConnectionInfo.isOnline;
        if (user2ConnectionInfo.userId === userId) return user2ConnectionInfo.isOnline;
        return false;
    }

    const onGameUpdateEvent = (pitAnimator: PitAnimator, message: Object) => {
        const newGame: Game = message as Game;
        newGame.mancalaGame = MancalaGame.createFromMancalaGame(newGame.mancalaGame);
        onGameUpdate(pitAnimator, newGame);
    }
    const onGameCrashed = (message: any) => {
        const newCrashMessage = message as string;
        notyf.error(t("InternalErrorOccurred"));
        console.error("on_game_crash");
        console.error(newCrashMessage);
    }
    const onGameUserLeave = (message: any) => {
        const userKeyWhoLeave = message;
        setUserKeyWhoLeave(userKeyWhoLeave);
        setHasOngoingAction(false);
    };
    const onUserConnectionChange = (message: any) => {
        const gameUsersConnectionInfo = message as GameUsersConnectionInfo;
        setGameUsersConnectionInfo(gameUsersConnectionInfo);
    };

    const listenMessages = (game: Game, pitAnimator: PitAnimator): () => void => {
        const _onGameUpdate = (message: object) => onGameUpdateEvent(pitAnimator, message);
        context.rtmt.addMessageListener(channel_on_game_update, _onGameUpdate);
        context.rtmt.addMessageListener(channel_on_game_crashed, onGameCrashed);
        context.rtmt.addMessageListener(channel_on_game_user_leave, onGameUserLeave);
        context.rtmt.addMessageListener(channel_on_user_connection_change, onUserConnectionChange);
        checkIsSpectator(game) && userKey && context.rtmt.sendMessage(channel_listen_game_events, game.id);
        return () => {
            checkIsSpectator(game) && userKey && context.rtmt.sendMessage(channel_unlisten_game_events, game.id);
            context.rtmt.removeMessageListener(channel_on_game_update, _onGameUpdate);
            context.rtmt.removeMessageListener(channel_on_game_crashed, onGameCrashed);
            context.rtmt.removeMessageListener(channel_on_game_user_leave, onGameUserLeave);
            context.rtmt.removeMessageListener(channel_on_user_connection_change, onUserConnectionChange);
        }
    };

    const updateBoardViewModel = (boardViewModel: BoardViewModel) => {
        boardViewModel.id = v4();
        setBoardId(boardViewModel.id);
        setBoardViewModel(boardViewModel);
    };

    const getBoardIndex = (index: number) => {
        if (!game || !mancalaGame) return -1;
        const pitsLenght = mancalaGame.board.pits.length;
        if (userKey === mancalaGame.player2Id) return index + pitsLenght / 2;
        return index;
    };

    const getOpponentId = () => mancalaGame?.player1Id === userKey ? mancalaGame?.player2Id : mancalaGame?.player1Id;

    const checkHasAnOngoingAction = () => hasOngoingAction;

    const onLeaveGameClick = () => {
        if (Util.checkConnectionAndMaybeAlert(context, t)) return;
        swal({
            title: t("AreYouSureToLeaveGame"),
            icon: "warning",
            buttons: [t("Yes"), t("Cancel")],
            dangerMode: true,
        })
            .then((cancel) => {
                if (!cancel) {
                    context.rtmt.sendMessage(channel_leave_game, {});
                }
            });
    };

    const onNewGameClick = () => {
        if (Util.checkConnectionAndMaybeAlert(context, t)) return;
        navigate("/loby")
    };

    const onPitSelect = (index: number, pit: Pit) => {
        if (!game || isSpectator || !userKey) {
            return;
        }
        if(userKeyWhoLeave) {
            notyf.error(t("GameEnded"));
            return;
        }
        if(game.mancalaGame.state === "ended") {
            notyf.error(t("GameEnded"));
            return;
        }
        if (Util.checkConnectionAndMaybeAlert(context, t)) return;
        if (game.mancalaGame.getPlayerIdByIndex(index) !== userKey) {
            notyf.error(t("UCanOnlyPlayYourOwnPits"));
            return;
        }
        const pitIndexForUser = index % (game.mancalaGame.board.totalPitCount() / 2);
        if (!game.mancalaGame.canPlayerMove(userKey, pitIndexForUser)) {
            notyf.error(t("OpponentTurn"));
            return;
        }
        if (checkHasAnOngoingAction()) {
            notyf.error(t("UMustWaitUntilCurrentMoveComplete"));
            return;
        }
        if (!boardViewModel) return;
        //TODO: this check should be in mancala.js
        if (pit.stoneCount === 0) {
            notyf.error(t("UCanNotPlayEmptyPit"));
            return;
        }
        setHasOngoingAction(true);
        boardViewModel.pits[getBoardIndex(pitIndexForUser)].pitColor =
            context.themeManager.theme.pitSelectedColor;
        updateBoardViewModel(boardViewModel);
        const gameMove: GameMove = { index: pitIndexForUser };
        context.rtmt.sendMessage(channel_game_move, gameMove);
    };

    React.useEffect(() => {
        let pitAnimator: PitAnimator | undefined;
        let unlistenMessages: () => void;
        setLoadingStateGame(LoadingState.Loading())
        context.gameStore.get(params.gameId!!).then((game) => {
            if (game) {
                pitAnimator = new PitAnimator(context.themeManager, updateBoardViewModel);
                setPitAnimator(pitAnimator);
                onGameUpdate(pitAnimator, game);
                unlistenMessages = listenMessages(game, pitAnimator);
                setLoadingStateGame(LoadingState.Loaded({ value: game }))
            } else {
                setLoadingStateGame(LoadingState.Error({ errorMessage: t("GameNotFound") }))
            }
        })
        return () => {
            unlistenMessages?.();
            pitAnimator?.dispose();
        };
    }, []);

    const textColorOnAppBar = getColorByBrightness(
        context.themeManager.theme.appBarBgColor,
        context.themeManager.theme.textColor,
        context.themeManager.theme.textLightColor
    );
    const isMobile = width < 600;

    const renderNewGameBtn = isSpectator || (userKeyWhoLeave || !game || (game && game.mancalaGame.state == "ended"));
    const showBoardView = game && boardViewModel && userKey && true;
    const topLocatedUserId = (isSpectator ? mancalaGame?.player2Id : getOpponentId()) || "0";
    const bottomLocatedUserId = (isSpectator ? mancalaGame?.player1Id : userKey) || "1";
    const topLocatedUser = {
        id: topLocatedUserId,
        name: "Anonymous",
        isOnline: isUserOnline(topLocatedUserId),
        isAnonymous: true
    };
    const bottomLocatedUser = {
        id: bottomLocatedUserId,
        name: "Anonymous",
        isOnline: isSpectator ? isUserOnline(bottomLocatedUserId) : context.rtmt.connectionState === "connected",
        isAnonymous: true
    };
    const currentUser = isSpectator ? {
        id: "2",
        name: "Anonymous",
        isOnline: context.rtmt.connectionState === "connected",
        isAnonymous: true
    } : bottomLocatedUser;
    const leftPlayer = userKeyWhoLeave ? (userKeyWhoLeave === topLocatedUser.id ? topLocatedUser : bottomLocatedUser) : undefined;
    return (
        <PageContainer theme={theme!}>
            {renderHeaderBar()}
            {renderMobileBoardToolbar()}
            {buildBoardTopToolbar()}
            {showBoardView && (
                <BoardView
                    game={game}
                    boardId={boardId}
                    boardViewModel={boardViewModel}
                    context={context}
                    onPitSelect={onPitSelect}
                    revert={isPlayer2} />
            )}
            <Center>
                <LoadingComponent context={context} loadingState={gameLoadingState}></LoadingComponent>
            </Center>
        </PageContainer>
    );

    function renderHeaderBar() {
        return <HeaderBar color={theme?.appBarBgColor}>
            <Row>
                <Link style={{ textDecoration: 'none' }} to={"/"}>
                    <HeaderbarIcon />
                </Link>
                <Link style={{ textDecoration: 'none' }} to={"/"}>
                    <HeaderbarTitle title={t("Mancala")} color={textColorOnAppBar} />
                </Link>
            </Row>
            <Row>
                <ThemeSwitchMenu context={context} textColor={textColorOnAppBar} />
                <Button
                    context={context}
                    color={context.themeManager.theme.pitColor}
                    text={renderNewGameBtn ? t("NewGame") : t("Leave")}
                    onClick={renderNewGameBtn ? onNewGameClick : onLeaveGameClick} />
            </Row>
        </HeaderBar>;
    }

    function renderMobileBoardToolbar() {
        return <BoardToolbar style={{ justifyContent: "center" }} visible={showBoardView && isMobile || false}>
            {buildInfoPanel({ visible: isMobile })}
        </BoardToolbar>;
    }

    function buildBoardTopToolbar() {
        return <BoardToolbar style={{ alignItems: "flex-end" }} visible={showBoardView || false}>
            <UserStatus style={{
                marginBottom: "0.5rem", marginLeft: "6%", maxWidth: isMobile ? "40vw" : "30vw",
                width: isMobile ? "40vw" : "30vw"
            }} context={context} layoutMode="left" user={topLocatedUser} visible={showBoardView || false} />
            {buildInfoPanel({ visible: !isMobile })}
            <UserStatus style={{
                marginBottom: "0.5rem", marginRight: "6%", maxWidth: isMobile ? "40vw" : "30vw",
                width: isMobile ? "40vw" : "30vw"
            }} context={context} layoutMode="right" user={bottomLocatedUser} visible={showBoardView || false} />
        </BoardToolbar>;
    }

    function buildInfoPanel(params: { visible: boolean }) {
        return (
            <InfoPanel
                style={{ marginTop: "0.5rem", marginBottom: "0.5rem" }}
                context={context}
                game={game}
                currentUser={currentUser}
                whitePlayer={topLocatedUser}
                blackPlayer={bottomLocatedUser}
                leftPlayer={leftPlayer}
                visible={params.visible}
                isSpectator={isSpectator} />
        );
    }
}

export default GamePage;

