import POGOProtos from 'pogo-protos';

import React from 'react';
import { connect } from 'react-redux';
import { RouteComponentProps } from 'react-router-dom';

import { League } from 'app/models/League';

import { appReducers } from './index';

import * as ActionsPokemonExplorer from './components/PokemonExplorer/actions';
import * as ActionsPokemonSelectList from './components/PokemonSelectList/actions';
import { IPokemonAppDispatch } from './types';

import { IndividualValueKey } from './components/PokemonExplorer/types';

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

import { appendQueryString, getCurrentQueryStringVlaues } from 'app/utils/navigation';

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

type PokemonAppProps = ReturnType<typeof appReducers>;

interface IConnectedPokemonAppProps extends PokemonAppProps, IPokemonAppDispatch {
    history : RouteComponentProps['history'];
    location : RouteComponentProps['location'];
}

class PokemonApp extends React.Component<IConnectedPokemonAppProps> {
    constructor(props : IConnectedPokemonAppProps) {
        super(props);
    }

    public async componentWillMount() {
        const {
            location,
            dispatch
        } = this.props;

        await Promise.all([
            dispatch(ActionsPokemonExplorer.fetchConfig()),
            dispatch(ActionsPokemonSelectList.fetchPokemonList())
        ]);
        dispatch(ActionsPokemonSelectList.setIsLoading(false));

        const [
            pokemonIdValue,
            pokemonFormValue,
            activeLeagueValue,
         ] = getCurrentQueryStringVlaues(location, 'id', 'form', 'league');

        const pokemonId = pokemonIdValue !== null ? parseInt(pokemonIdValue, 10) : null;
        const pokemonForm = pokemonFormValue ? parseInt(pokemonFormValue, 10) : null;
        if (pokemonId !== null && typeof POGOProtos.Enums.PokemonId[pokemonId] !== 'undefined' &&
            pokemonForm !== null && typeof POGOProtos.Enums.Form[pokemonForm] !== 'undefined'
        ) {
            this.handleActivatePokemon(pokemonId, pokemonForm);
        }

        const activeLeague = activeLeagueValue ? parseInt(activeLeagueValue, 10) : null;
        if (activeLeague !== null && typeof League[activeLeague] !== 'undefined') {
            this.handleChangeLeague(activeLeague);
        }
    }

    public render() {
        const {
            activePokemonId,
            activePokemonForm,
            pokemonList,
            pokemonListFiltered,
            filterTerm,
        } = this.props.pokemonSelectListState;
        const {
            league,
            individualValues,
            leaguePokemon,
        } = this.props.pokemonExplorerState;

        return (
            <div className={ styles.wrapper }>
                <Header />
                <div className={ styles.body }>
                    <PokemonSelectList
                        isLoading={ this.props.pokemonSelectListState.isLoading }
                        activePokemonId={ activePokemonId }
                        activePokemonForm={ activePokemonForm }
                        pokemonList={ filterTerm === '' ? pokemonList : pokemonListFiltered }
                        filterTerm={ this.props.pokemonSelectListState.filterTerm }
                        handleActivatePokemon={ this.handleActivatePokemon }
                        handleChangeFilter={ this.handleChangeFilter }
                    />
                    { leaguePokemon !== null &&
                        <PokemonExplorer
                            isLoading={ this.props.pokemonExplorerState.isLoading }
                            activeLeague={ league }
                            leaguePokemon={ leaguePokemon }
                            individualValues={ individualValues }
                            handleChangeIndividualValue={ this.handleChangeIndividualValue }
                            handleMaximizeLevel={ this.handleMaximizeLevel }
                            handleChangeLeague={ this.handleChangeLeagueNavigation }
                        />
                    }
                </div>
                <Footer />
            </div>
        );
    }

    private readonly handleActivatePokemon = (pokemonId : POGOProtos.Enums.PokemonId, form : POGOProtos.Enums.Form) => {
        const { dispatch } = this.props;

        dispatch(ActionsPokemonSelectList.fetchPokemonLeagueStats(pokemonId, form))
        .then((leaguePokemon) => {
            dispatch(ActionsPokemonSelectList.setActivePokemonId(pokemonId, form));
            dispatch(ActionsPokemonExplorer.setIvLevel(null));
            dispatch(ActionsPokemonExplorer.setIvHp(null));
            dispatch(ActionsPokemonExplorer.setIvAtk(null));
            dispatch(ActionsPokemonExplorer.setIvDef(null));
            dispatch(ActionsPokemonExplorer.setLeaguePokemon(leaguePokemon));
        })
        .catch((error) => {
            // tslint:disable-next-line:no-console
            console.error(error);
            dispatch(ActionsPokemonExplorer.setLeaguePokemon(null));
        })
        .then(() => dispatch(ActionsPokemonExplorer.setIsLoading(false)));
    }

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

    private readonly handleChangeIndividualValue = (stat : IndividualValueKey, value : number | null) => {
        const {
            dispatch,
        } = this.props;

        switch (stat) {
            case 'level':
                dispatch(ActionsPokemonExplorer.setIvLevel(value));
                break;
            case 'hp':
                dispatch(ActionsPokemonExplorer.setIvHp(value));
                break;
            case 'atk':
                dispatch(ActionsPokemonExplorer.setIvAtk(value));
                break;
            case 'def':
                dispatch(ActionsPokemonExplorer.setIvDef(value));
                break;
            default:
                break;
        }
    }

    private readonly handleMaximizeLevel = () => {
        this.props.dispatch(ActionsPokemonExplorer.maximizeLevel());
    }

    private readonly handleChangeLeagueNavigation = (league : League) => {
        const {
            history,
            location,
        } = this.props;

        history.push({
            search: appendQueryString(location, { league: league.toString() })
        });

        this.handleChangeLeague(league);
    }

    private readonly handleChangeLeague = (league : League) => {
        this.props.dispatch(ActionsPokemonExplorer.setActiveLeague(league));
    }
}

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

const mapDispatchToProps = (dispatch : IPokemonAppDispatch['dispatch']) : IPokemonAppDispatch => {
    return {
        dispatch
    };
};

const mergeProps = (state : PokemonAppProps, dispatchProps : IPokemonAppDispatch, ownProps : RouteComponentProps) : IConnectedPokemonAppProps => {
    return {
        ...state,
        ...dispatchProps,
        history: ownProps.history,
        location: ownProps.location,
    };
};

export const ConnectedPokemonApp = connect(mapStateToProps, mapDispatchToProps, mergeProps)(PokemonApp);
