"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const TAG_TYPE = {
    link: HTMLLinkElement,
    script: HTMLScriptElement,
    img: HTMLImageElement,
};
const TYPES = Object.keys(TAG_TYPE);
function findCurrentDomain(url, domainList) {
    let domain = '';
    // eslint-disable-next-line @typescript-eslint/prefer-for-of
    for (let i = 0; i < domainList.length; i++) {
        if (url.indexOf(domainList[i]) !== -1) {
            domain = domainList[i];
            break;
        }
    }
    return domain || url;
}
function findNextDomain(url, domainList) {
    const currentDomain = findCurrentDomain(url, domainList);
    const index = domainList.indexOf(currentDomain);
    return domainList[(index + 1) % domainList.length] || url;
}
function getRequestUrl(element) {
    if (element instanceof HTMLScriptElement ||
        element instanceof HTMLImageElement) {
        return element.src;
    }
    if (element instanceof HTMLLinkElement) {
        return element.href;
    }
    return null;
}
const defaultConfig = {
    max: 3,
    type: TYPES,
    domain: [],
    crossOrigin: false,
};
function validateTargetInfo(config, e) {
    var _a;
    const target = e.target;
    const tagName = (_a = target.tagName) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase();
    const allowTags = config.type;
    const url = getRequestUrl(target);
    if (!tagName ||
        allowTags.indexOf(tagName) === -1 ||
        !TAG_TYPE[tagName] ||
        !(target instanceof TAG_TYPE[tagName]) ||
        !url) {
        return false;
    }
    return { target, tagName, url };
}
// eslint-disable-next-line consistent-return
function createElement(origin, attributes) {
    const crossOrigin = attributes.crossOrigin === true ? 'anonymous' : attributes.crossOrigin;
    const crossOriginAttr = crossOrigin ? `crossorigin="${crossOrigin}"` : '';
    const retryTimesAttr = attributes.times
        ? `data-builder-retry-times="${attributes.times}"`
        : '';
    const isAsyncAttr = attributes.isAsync ? 'data-builder-async' : '';
    if (origin instanceof HTMLScriptElement) {
        const script = document.createElement('script');
        script.src = attributes.url;
        if (crossOrigin) {
            script.crossOrigin = crossOrigin;
        }
        if (attributes.times) {
            script.dataset.builderRetryTimes = String(attributes.times);
        }
        if (attributes.isAsync) {
            script.dataset.builderAsync = '';
        }
        return {
            element: script,
            str: `<script src="${attributes.url}" ${crossOriginAttr} ${retryTimesAttr} ${isAsyncAttr}></script>`,
        };
    }
    if (origin instanceof HTMLLinkElement) {
        const link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href = attributes.url;
        if (crossOrigin) {
            link.crossOrigin = crossOrigin;
        }
        if (attributes.times) {
            link.dataset.builderRetryTimes = String(attributes.times);
        }
        return {
            element: link,
            str: `<link rel="stylesheet" href="${attributes.url}" ${crossOriginAttr} ${retryTimesAttr}></link>`,
        };
    }
}
function reloadElementResource(origin, fresh, options) {
    if (origin instanceof HTMLScriptElement) {
        if (options.isAsync) {
            document.body.appendChild(fresh.element);
        }
        else {
            document.write(fresh.str);
        }
    }
    if (origin instanceof HTMLLinkElement) {
        document.getElementsByTagName('head')[0].appendChild(fresh.element);
    }
    if (origin instanceof HTMLImageElement) {
        origin.src = options.url;
        origin.dataset.builderRetryTimes = String(options.times);
    }
}
function retry(config, e) {
    const targetInfo = validateTargetInfo(config, e);
    if (targetInfo === false) {
        return;
    }
    const { target, tagName, url } = targetInfo;
    // Filter by config.test and config.domain
    let tester = config.test;
    if (tester) {
        if (typeof tester === 'string') {
            const regexp = new RegExp(tester);
            tester = (str) => regexp.test(str);
        }
        if (typeof tester !== 'function' || !tester(url)) {
            return;
        }
    }
    const domain = findCurrentDomain(url, config.domain);
    if (config.domain &&
        config.domain.length > 0 &&
        config.domain.indexOf(domain) === -1) {
        return;
    }
    // If the retry times has exceeded the maximum, fail
    const existRetryTimes = Number(target.dataset.builderRetryTimes) || 0;
    if (existRetryTimes === config.max) {
        if (typeof config.onFail === 'function') {
            const context = {
                times: existRetryTimes,
                domain,
                url,
                tagName,
            };
            config.onFail(context);
        }
        return;
    }
    // Then, we will start to retry
    const nextDomain = findNextDomain(domain, config.domain);
    const isAsync = Boolean(target.dataset.builderAsync) ||
        target.async ||
        target.defer;
    const attributes = {
        url: url.replace(domain, nextDomain),
        times: existRetryTimes + 1,
        crossOrigin: config.crossOrigin,
        isAsync,
    };
    const element = createElement(target, attributes);
    if (config.onRetry && typeof config.onRetry === 'function') {
        const context = {
            times: existRetryTimes,
            domain,
            url,
            tagName,
        };
        config.onRetry(context);
    }
    reloadElementResource(target, element, attributes);
}
function load(config, e) {
    const targetInfo = validateTargetInfo(config, e);
    if (targetInfo === false) {
        return;
    }
    const { target, tagName, url } = targetInfo;
    const domain = findCurrentDomain(url, config.domain);
    const retryTimes = Number(target.dataset.builderRetryTimes) || 0;
    if (retryTimes === 0) {
        return;
    }
    if (typeof config.onSuccess === 'function') {
        const context = {
            times: retryTimes,
            domain,
            url,
            tagName,
        };
        config.onSuccess(context);
    }
}
function resourceMonitor(error, success) {
    if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
        document.addEventListener('error', e => {
            if (e && e.target instanceof Element) {
                error(e);
            }
        }, true);
        document.addEventListener('load', e => {
            if (e && e.target instanceof Element) {
                success(e);
            }
        }, true);
    }
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function init(options) {
    // eslint-disable-next-line prefer-object-spread
    const config = Object.assign({}, defaultConfig, options);
    // Normalize config
    if (!Array.isArray(config.type) || config.type.length === 0) {
        config.type = defaultConfig.type;
    }
    if (!Array.isArray(config.domain) || config.domain.length === 0) {
        config.domain = defaultConfig.domain;
    }
    if (Array.isArray(config.domain)) {
        config.domain = config.domain.filter(Boolean);
    }
    // Bind event in window
    try {
        resourceMonitor((e) => {
            try {
                retry(config, e);
            }
            catch (err) {
                console.error('retry error captured', err);
            }
        }, (e) => {
            try {
                load(config, e);
            }
            catch (err) {
                console.error('load error captured', err);
            }
        });
    }
    catch (err) {
        console.error('monitor error captured', err);
    }
}
