import React from 'react';
import {withNamespaces} from 'react-i18next';
import i18n from 'i18next';
import {
    Route,
    Redirect
} from 'react-router-dom';

import merge from 'webpack-merge';

import Backend from 'i18next-chained-backend';
import XHR from 'i18next-xhr-backend';
import LocalStorageBackend from 'i18next-localstorage-backend'
import LanguageDetector from 'i18next-browser-languagedetector';
import {reactI18nextModule} from 'react-i18next';

//import {ApiRequest, GET} from "./ApiRequest";

function loadLocales(url, options, callback, data) {
    try {
        let waitForLocale = import ('../locales/' + url + '.json');
        let waitForBackendLocale = new Promise(resolve => {
            // mocking data for one namespace/language
            if('en/page--shop-product' === url) {
                resolve({
                    defaults: {
                        types: {
                            "event-item": "Event Item"
                        }
                    }
                });
            }
            resolve(false);
        });

        Promise.all([waitForLocale, waitForBackendLocale])
            .then(data => {
                if(data[1]) {
                    // when received something from backend, deepmerge into bundled locales
                    data[0] = merge(data[0], data[1]);
                }
                callback(data[0], {status: '200'});
            });
    } catch(e) {
        callback(null, {status: '404'});
    }
}

class I18nProvider extends React.Component {
    constructor(props) {
        super(props);

        const {config} = this.props;

        // load translation using xhr -> see /public/locales
        // learn more: https://github.com/i18next/i18next-xhr-backend
        i18n
            .use(Backend)
            // detect user language
            // learn more: https://github.com/i18next/i18next-browser-languageDetector
            .use(LanguageDetector)
            // pass the i18n instance to the react-i18next components.
            // Alternative use the I18nextProvider: https://react.i18next.com/components/i18nextprovider
            .use(reactI18nextModule)
            // init i18next
            // for all options read: https://www.i18next.com/overview/configuration-options
            .init({
                allLanguages: config.allLanguages,
                defaultLanguage: config.defaultLanguage,
                fallbackLng: process.env.NODE_ENV === 'production' ? config.defaultLanguage : null,
                debug: config.debug,

                defaultNS: 'common',
                interpolation: {
                    escapeValue: false, // not needed for react as it escapes by default
                },

                // special options for react-i18next
                // learn more: https://react.i18next.com/components/i18next-instance
                react: {
                    wait: true,
                    nsMode: 'default'
                },

                detection: {
                    order: ['path', 'localStorage'],

                    lookupLocalStorage: 'i18nextLng',
                    lookupFromPathIndex: 0,

                    // cache user language on
                    caches: ['localStorage'],
                    excludeCacheFor: ['cimode'], // languages to not persist (cookie, localStorage)
                },

                backend: {
                    backends: [
                        LocalStorageBackend,  // primary
                        XHR                   // fallback
                    ],
                    backendOptions: [{
                        // prefix for stored languages
                        prefix: 'i18next_res_',

                        // expiration
                        expirationTime: config.expirationTime,

                        // language versions
                        versions: {}
                    }, {
                        // path where resources get loaded from, or a function
                        // returning a path:
                        // function(lngs, namespaces) { return customPath; }
                        // the returned path will interpolate lng, ns if provided like giving a static path
                        loadPath: '{{lng}}/{{ns}}',

                        // path to post missing resources
                        //addPath: 'locales/add/{{lng}}/{{ns}}',

                        // your backend server supports multiloading
                        // /locales/resources.json?lng=de+en&ns=ns1+ns2
                        allowMultiLoading: false, // set loadPath: '/locales/resources.json?lng={{lng}}&ns={{ns}}' to adapt to multiLoading

                        // parse data after it has been fetched
                        // in example use https://www.npmjs.com/package/json5
                        parse: (data, ident) => {
                            ident = ident.split('/');
                            const lng = ident[0];
                            const ns = ident[1];

                            // if that namespace got hardcoded extensions from consuming app, overwrite library locales with consumer app locales
                            if(this.props.locales[lng][ns]) {
                                data = {...data};
                                data = merge(data, this.props.locales[lng][ns]);
                            }

                            return data;
                        },

                        // allow cross domain requests
                        crossDomain: false,

                        // allow credentials on cross domain requests
                        withCredentials: false,

                        // define a custom xhr function
                        // can be used to support XDomainRequest in IE 8 and 9
                        //
                        // 'url' will be passed the value of 'loadPath'
                        // 'options' will be this entire options object
                        // 'callback' is a function that takes two parameters, 'data' and 'xhr'.
                        //            'data' should be the key:value translation pairs for the
                        //            requested language and namespace, or null in case of an error.
                        //            'xhr' should be a status object, e.g. { status: 200 }
                        // 'data' will be a key:value object used when saving missing translations
                        ajax: loadLocales,

                        // adds parameters to resource URL. 'example.com' -> 'example.com?v=1.3.5'
                        //queryStringParams: {v: '1.3.5'}
                    }]
                }
            });

        // add allLanguages prop, as i18n doesn't have such a thing on its own / i18n.languages only returns loaded languages
        i18n.allLanguages = config.allLanguages;

        let html = document.querySelector('html');
        html.lang = this.defaultLanguage();

        i18n.on('languageChanged', (lng) => {
            i18nEvents.trigger('languageChanged', [lng]);
            html.lang = lng;
        });
    }

    defaultLanguage = () => {
        let defaultLang = this.props.config.defaultLanguage;
        if(i18n && i18n.languages && i18n.languages[0]) {
            defaultLang = i18n.languages[0];
        }
        return defaultLang;
    };

    render() {
        return (
            <React.Fragment>
                <Route exact path="/" render={() => (
                    // redirect to the initial language on start, fetch it from path or localStorage
                    <Redirect to={'/' + this.defaultLanguage()}/>
                )}/>

                {this.props.children}
            </React.Fragment>
        );
    }
}

// to have one instance everywhere
const store = {
    events: {},
    allowedTScope: {},
    customTrans: {},
    aliasTScope: {}
};

const enableCustomDefault = (name, t_scope) => {
    if(!store.allowedTScope[name]) {
        store.allowedTScope[name] = {};
    }
    store.allowedTScope[name][t_scope] = true;
};

const setCustom = (name, locale, val) => {
    if(!store.customTrans[name]) {
        store.customTrans[name] = {};
    }
    store.customTrans[name][locale] = val;
};

const addAlias = (name, alias, t_scope) => {
    if(!store.aliasTScope[name]) {
        store.aliasTScope[name] = {};
    }
    store.aliasTScope[name][alias] = t_scope;
};

const getAlias = (name, t_scope) => {
    if(store.aliasTScope.hasOwnProperty(name) && store.aliasTScope[name].hasOwnProperty(t_scope)) {
        return store.aliasTScope[name][t_scope];
    }
    return t_scope;
};

const isAllowedTScope = (name, t_scope) => {
    t_scope = getAlias(name, t_scope);
    if(store.allowedTScope.hasOwnProperty(name) && store.allowedTScope[name].hasOwnProperty(t_scope)) {
        return store.allowedTScope[name][t_scope];
    }
    return false;
};

/**
 * The global theme got the themeprovider in BaseApp, the state of it controls the applied theme, to be able to change the theme from where ever, `provide` the state setting function and add `exec` on e.g. a button onClick
 */
const i18nEvents = {
    on: (name, handler) => {
        let rnd = Math.random().toString(36).substring(7);

        if('undefined' === typeof store.events[name]) {
            // none existing, init
            store.events[name] = {};
        } else if(store.events[name].hasOwnProperty(rnd)) {
            // when id already exists, start again to create new
            return i18nEvents.on(name, handler);
        }

        store.events[name][rnd] = handler;

        return rnd;
    },
    trigger: (name, payload = []) => {
        if(store.events[name]) {
            for(let id in store.events[name]) {
                if(store.events[name].hasOwnProperty(id)) {
                    store.events[name][id](payload);
                }
            }
        }
    },
    off: (name, id) => {
        if(store.events[name] && store.events[name].hasOwnProperty(id)) {
            delete store.events[name][id];
        }
    }
};

export {
    withNamespaces,
    enableCustomDefault, setCustom, isAllowedTScope, addAlias, getAlias,
    i18nEvents,
    i18n, I18nProvider
};