import csv from 'csvtojson';
import POGOProtos from 'pogo-protos';
import xhr2 from 'xhr2';

import { AjaxRequest } from 'api/AjaxRequest';

import { DEFAULT_POKEMON_NAME, IPokemonSpecies } from 'app/models/Pokemon';

interface IPokeApiSpeciesNameJSON {
    pokemon_species_id : string;
    local_language_id : '9' | string;
    name : string;
    genus : string;
}
interface IPokeApiSpeciesJSON {
    id : string;
    identifier : string;
    order : string;
}

interface ILivePokeApiLanguage {
    name : 'en' | string;
}
interface ILivePokeApiSpeciesJSON {
    id : number;
    names : Array<{ name : string; language : ILivePokeApiLanguage; }>;
    order : number;
    flavor_text_entries : Array<{ description : string; language : ILivePokeApiLanguage; }>;
    genera : Array<{ genus : string; language : ILivePokeApiLanguage; }>;
}
interface ILivePokeApiSpeciesListJSON {
    count : number;
    results : Array<{ name : string }>;
}

interface IApiService {
    getLivePokemonSpeciesKeys() : Promise<Array<string>>;
    getPokemonSpeciesInfo(pokemonId : POGOProtos.Enums.PokemonId, defaultDex : number, defaultName : string) : Promise<IPokemonSpecies>;
    getLivePokemonSpeciesInfo(dex : number) : Promise<IPokemonSpecies>;
}

export class ApiService implements IApiService {
    private AjaxRequest : AjaxRequest;

    constructor() {
        this.AjaxRequest = new AjaxRequest(xhr2);
    }

    public async getPokemonSpeciesInfo(pokemonId : POGOProtos.Enums.PokemonId, defaultDex : number, defaultName : string) {
        const formattedName = this.formatPokemonIdToPokeApiNameKey(pokemonId);

        const speciesArray : Array<IPokeApiSpeciesJSON> = await csv().fromFile('externals/PokeApi/data/v2/csv/pokemon_species.csv');

        let dex = defaultDex;
        let order = defaultDex;
        for (const entry of speciesArray) {
            if (entry.identifier === formattedName) {
                dex = parseInt(entry.id, 10);
                order = parseInt(entry.order, 10);
                break;
            }
        }

        const namesArray : Array<IPokeApiSpeciesNameJSON> = await csv().fromFile('externals/PokeApi/data/v2/csv/pokemon_species_names.csv');

        let name = defaultName;
        let genus = '';
        for (const entry of namesArray) {
            if (parseInt(entry.pokemon_species_id, 10) === dex && entry.local_language_id === '9') {
                name = entry.name;
                genus = entry.genus;
                break;
            }
        }

        const serializedResponse : IPokemonSpecies = {
            name,
            dex,
            order,
            genus,
        };
        return serializedResponse;
    }

    public async getLivePokemonSpeciesKeys() {
        const queryParameters = {
            offset: 0,
            limit: 9999, // 807 is current max from PokeApi
        };

        // https://pokeapi.co/docs/v2.html#pokemon-species
        const response : ILivePokeApiSpeciesListJSON = await this.AjaxRequest.ajaxGet(`https://pokeapi.co/api/v2/pokemon-species/`, queryParameters);

        return response.results.map((listItem) => {
            return listItem.name;
        });
    }

    public async getLivePokemonSpeciesInfo(pokemonId : POGOProtos.Enums.PokemonId) {
        const formattedName = this.formatPokemonIdToPokeApiNameKey(pokemonId);

        const queryParameters = {
        };

        // https://pokeapi.co/docs/v2.html#pokemon-species
        const response : ILivePokeApiSpeciesJSON = await this.AjaxRequest.ajaxGet(`https://pokeapi.co/api/v2/pokemon-species/${formattedName}/`, queryParameters);

        let name = DEFAULT_POKEMON_NAME;
        response.names.some((entry) => {
            if (entry.language.name === 'en') {
                name = entry.name;
                return true;
            }
            return false;
        });

        let genus = '';
        response.genera.some((entry) => {
            if (entry.language.name === 'en') {
                genus = entry.genus;
                return true;
            }
            return false;
        });

        const serializedResponse : IPokemonSpecies = {
            name,
            dex: response.id,
            order: response.order,
            genus,
        };
        return serializedResponse;
    }

    private formatPokemonIdToPokeApiNameKey(pokemonId : POGOProtos.Enums.PokemonId) {
        let key : string;
        if (pokemonId === POGOProtos.Enums.PokemonId.NIDORAN_MALE) {
            key = 'nidoran-m';
        } else if (pokemonId === POGOProtos.Enums.PokemonId.NIDORAN_FEMALE) {
            key = 'nidoran-f';
        } else {
            key = POGOProtos.Enums.PokemonId[pokemonId];
        }
        return key.toLowerCase().replace(/_/, '-');
    }
}
