import React from 'react';
import Media from 'react-media';
import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom';

import classNames from 'classnames';

import { appReducers } from 'app/index';

import * as ActionsPokemonApp from 'app/actions';
import * as ActionsPokemonExplorer from 'app/components/PokemonExplorer/actions';
import * as ActionsPokemonSelectList from 'app/components/PokemonSelectList/actions';
import { IPokemonAppDispatch, IRouterProps, Navigation } from 'app/types';

import { convertFormParamToPokemonForm, convertIdParamToPokemonId, convertLeagueParamToLeague } from 'app/utils/navigation';

import { Footer } from 'app/components/Footer';
import { Header } from 'app/components/Header';
import { ConnectedPokemonExplorer } from 'app/components/PokemonExplorer/PokemonExplorer';
import { PokemonSelectList } from 'app/components/PokemonSelectList/PokemonSelectList';

import { MAX_MOBILE_WIDTH, MIN_TABLET_WIDTH } from 'common/models/constants';
import * as PVPogoProtos from 'common/models/PVPogoProtos';

import * as styles from 'app/styles/PokemonApp.scss';

type PokemonAppProps = ReturnType<typeof appReducers>;

interface IConnectedPokemonAppProps extends PokemonAppProps, IPokemonAppDispatch, IRouterProps {}

class PokemonApp extends React.Component<IConnectedPokemonAppProps> {

    public async componentWillMount() {
        const {
            dispatch,
            match,
        } = this.props;
        const {
            id,
            form,
            league,
        } = match.params;

        await Promise.all([
            dispatch(ActionsPokemonApp.fetchConfig()),
            // TODO: move this action to PokemonApp actions
            dispatch(ActionsPokemonSelectList.fetchPokemonList())
        ]);
        dispatch(ActionsPokemonSelectList.setIsLoading(false));

        const pokemonId = convertIdParamToPokemonId(id);
        const pokemonForm = convertFormParamToPokemonForm(form);
        if (pokemonId !== null && pokemonForm !== null) {
            this.handleActivatePokemon(pokemonId, pokemonForm);
        }

        const activeLeague = convertLeagueParamToLeague(league);
        if (activeLeague !== null) {
            dispatch(ActionsPokemonExplorer.setActiveLeague(activeLeague));
        }
    }

    public render() {
        const {
            navigation,
            isInterruption,
            attackTypeEffectiveness,
            combatMoves,
        } = this.props.pokemonAppState;
        const {
            pokemonList,
            pokemonListFiltered,
            filterTerm,
        } = this.props.pokemonSelectListState;
        const {
            leaguePokemon,
        } = this.props.pokemonExplorerState;
        const matchParams = this.props.match.params;
        const activePokemonId = convertIdParamToPokemonId(matchParams.id);
        const activePokemonForm = convertFormParamToPokemonForm(matchParams.form);

        const wrapperCss = classNames(
            styles.wrapper,
            {
                [styles.overlaid]: isInterruption,
            }
        );

        const iconCss = classNames(
            'icon',
            'pixel',
            'sprite',
        );

        const pokedexCss = classNames(
            iconCss,
            'pokedex',
            {
                active: navigation === 'pokedex',
            }
        );
        const pokedexButtonCss = classNames(
            styles.navigationButton,
            // {
            //     [styles.activeNavigationButton]: activeNavigation === 'pokedex',
            // }
        );

        const hamburgerButtonCss = classNames(
            styles.hamburgerIcon,
            styles.navigationButton,
            // {
            //     [styles.activeNavigationButton]: activeNavigation === 'menu',
            // }
        );

        // react optimization for rendering on pokemon switching
        // https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
        const uniquePokemonId = leaguePokemon !== null ? `${leaguePokemon.id}~${leaguePokemon.form}` : undefined;

        return (
            <div className={ wrapperCss }>
                <Header>
                    <PokemonSelectList
                        isLoading={ this.props.pokemonSelectListState.isLoading }
                        isListOpen={ navigation === 'pokedex' }
                        activePokemonId={ activePokemonId }
                        activePokemonForm={ activePokemonForm }
                        pokemonList={ filterTerm === '' ? pokemonList : pokemonListFiltered }
                        filterTerm={ this.props.pokemonSelectListState.filterTerm }
                        handleActivatePokemon={ this.handleActivatePokemon }
                        handleChangeFilter={ this.handleChangeFilter }
                    />
                    <Media query={ { minWidth: MIN_TABLET_WIDTH } }>
                        <button className={ pokedexButtonCss } onClick={ this.handlePokedexClick }><i className={ pokedexCss } /></button>
                    </Media>
                    <Media query={ { maxWidth: MAX_MOBILE_WIDTH } }>
                        <button className={ hamburgerButtonCss } onClick={ this.handleMenuClick }>&#9776;</button>
                    </Media>
                </Header>
                <ConnectedPokemonExplorer
                    key={ uniquePokemonId }
                    isMenuOpen={ navigation === 'menu' }
                    isOverlaid={ isInterruption }
                    attackTypeEffectiveness={ attackTypeEffectiveness }
                    combatMoves={ combatMoves }
                    toggleInterruption={ this.handleToggleInterruption }
                />
                <Footer />
            </div>
        );
    }

    private readonly handleInterruption = (isInterruption : boolean, navigation : Navigation) => {
        const { dispatch } = this.props;

        dispatch(ActionsPokemonApp.setNavigation(isInterruption ? navigation : null));
        dispatch(ActionsPokemonApp.setIsInterruption(isInterruption));

        if (isInterruption) {
            dispatch(ActionsPokemonExplorer.setCombatMoveSelectorsOpen({
                quickMove: false,
                chargeMove1: false,
                chargeMove2: false,
            }));
        }
    }

    private readonly handleToggleInterruption = (isInterruption : boolean) => {
        this.props.dispatch(ActionsPokemonApp.setIsInterruption(isInterruption));
        if (!isInterruption) {
            this.props.dispatch(ActionsPokemonApp.setNavigation(null));
        }
    }

    private readonly handlePokedexClick = () => {
        this.handleInterruption(true, 'pokedex');
    }

    private readonly handleMenuClick = () => {
        this.handleInterruption(true, 'menu');
    }

    private readonly handleActivatePokemon = async (pokemonId : PVPogoProtos.PokemonId, form : PVPogoProtos.PokemonForm) => {
        const { dispatch } = this.props;

        dispatch(ActionsPokemonExplorer.setIsLoading(true));
        try {
            const leaguePokemon = await dispatch(ActionsPokemonApp.fetchPokemonLeagueStats(pokemonId, form));
            dispatch(ActionsPokemonExplorer.reset(leaguePokemon));
        } catch (error) {
            // tslint:disable-next-line:no-console
            console.error(error);
            dispatch(ActionsPokemonExplorer.setLeaguePokemon(null));
        }
        dispatch(ActionsPokemonExplorer.setIsLoading(false));
        this.handleInterruption(false, 'pokedex');
    }

    private readonly handleChangeFilter = (filterTerm : string) => {
        this.handleInterruption(true, 'pokedex');
        return this.props.dispatch(ActionsPokemonSelectList.filterPokemonList(filterTerm));
    }
}

const mapStateToProps = (state : PokemonAppProps) : PokemonAppProps => {
    return {
        pokemonAppState: state.pokemonAppState,
        pokemonExplorerState: state.pokemonExplorerState,
        pokemonSelectListState: state.pokemonSelectListState,
    };
};

export const ConnectedPokemonApp = withRouter(connect(mapStateToProps)(PokemonApp));
