let counter = 0;
let scriptMap = typeof window !== 'undefined' && window._scriptMap || new Map();
const window = require('./windowOrGlobal');

export const ScriptCache = (function(global) {
    global._scriptMap = global._scriptMap || scriptMap;
    return function ScriptCache(scripts) {
        const Cache = {};

        Cache._onLoad = function(key) {
            return (cb) => {
                let registered = true;

                function unregister() {
                    registered = false;
                }

                let stored = scriptMap.get(key);

                if (stored) {
                    stored.promise.then(() => {
                        if (registered) {
                          stored.error ? cb(stored.error) : cb(null, stored)
                        }

                        return stored;
                    }).catch(error => cb(error));
                } else {
                    // TODO:
                }

                return unregister;
            }
        };

        Cache._scriptTag = (key, src) => {
            if (!scriptMap.has(key)) {
                // Server side rendering environments don't always have access to the `document` global.
                // In these cases, we're not going to be able to return a script tag, so just return null.
                if (typeof document === 'undefined') return null;

                let tag = document.createElement('script');
                let promise = new Promise((resolve, reject) => {
                    let body = document.getElementsByTagName('body')[0];

                    tag.type = 'text/javascript';
                    tag.async = false; // Load in order

                    const cbName = `loaderCB${counter++}${Date.now()}`;
                    let cb;

                    let handleResult = (state) => {
                        return (evt) => {
                            let stored = scriptMap.get(key);
                            if (state === 'loaded') {
                                stored.resolved = true;
                                resolve(src);
                                return;
                            } else if (state === 'error') {
                                stored.errored = true;
                                reject(evt)
                                return;
                            }
                            stored.loaded = true;

                            cleanup();
                        }
                    };

                    const cleanup = () => {
                        if (global[cbName] && typeof global[cbName] === 'function') {
                            global[cbName] = null;
                            delete global[cbName]
                        }
                    };

                    tag.onload = handleResult('loaded');
                    tag.onerror = handleResult('error');
                    tag.onreadystatechange = () => {
                        handleResult(tag.readyState)
                    };

                    // Pick off callback, if there is one
                    if (src.match(/callback=CALLBACK_NAME/)) {
                        src = src.replace(/(callback=)[^\&]+/, `$1${cbName}`)
                        cb = window[cbName] = handleResult('google-load');
                    } else {
                        tag.addEventListener('load', tag.onload)
                    }
                    tag.addEventListener('error', tag.onerror);

                    tag.src = src;
                    body.appendChild(tag);

                    return tag;
                });
                let initialState = {
                    loaded: false,
                    error: false,
                    promise,
                    tag
                };
                scriptMap.set(key, initialState);
            }
            return scriptMap.get(key).tag;
        };

        // let scriptTags = document.querySelectorAll('script')
        //
        // NodeList.prototype.filter = Array.prototype.filter;
        // NodeList.prototype.map = Array.prototype.map;
        // const initialScripts = scriptTags
        //   .filter(s => !!s.src)
        //   .map(s => s.src.split('?')[0])
        //   .reduce((memo, script) => {
        //     memo[script] = script;
        //     return memo;
        //   }, {});

        Object.keys(scripts).forEach(function(key) {
            const script = scripts[key];

            const tag = window._scriptMap.has(key) ?
                        window._scriptMap.get(key).tag :
                        Cache._scriptTag(key, script);

            Cache[key] = {
                tag: tag,
                onLoad: Cache._onLoad(key),
            }
        });

        return Cache;
    }
})(window);

export default ScriptCache;
