import { IntlMessageFormat } from 'intl-messageformat';

function strtr(string, replacePairs) {
    const regex = Object.entries(replacePairs).map(([from]) => {
        return from.replace(/([-[\]{}()*+?.\\^$|#,])/g, '\\$1');
    });
    if (regex.length === 0) {
        return string;
    }
    return string.replace(new RegExp(regex.join('|'), 'g'), (matched) => replacePairs[matched].toString());
}

function format(id, parameters, locale) {
    if (null === id || '' === id) {
        return '';
    }
    if (typeof parameters['%count%'] === 'undefined' || Number.isNaN(parameters['%count%'])) {
        return strtr(id, parameters);
    }
    const number = Number(parameters['%count%']);
    let parts = [];
    if (/^\|+$/.test(id)) {
        parts = id.split('|');
    }
    else {
        parts = id.match(/(?:\|\||[^|])+/g) || [];
    }
    const intervalRegex = /^(?<interval>({\s*(-?\d+(\.\d+)?[\s*,\s*\-?\d+(.\d+)?]*)\s*})|(?<left_delimiter>[[\]])\s*(?<left>-Inf|-?\d+(\.\d+)?)\s*,\s*(?<right>\+?Inf|-?\d+(\.\d+)?)\s*(?<right_delimiter>[[\]]))\s*(?<message>.*?)$/s;
    const standardRules = [];
    for (let part of parts) {
        part = part.trim().replace(/\|\|/g, '|');
        const matches = part.match(intervalRegex);
        if (matches) {
            const matchGroups = matches.groups || {};
            if (matches[2]) {
                for (const n of matches[3].split(',')) {
                    if (number === Number(n)) {
                        return strtr(matchGroups.message, parameters);
                    }
                }
            }
            else {
                const leftNumber = '-Inf' === matchGroups.left ? Number.NEGATIVE_INFINITY : Number(matchGroups.left);
                const rightNumber = ['Inf', '+Inf'].includes(matchGroups.right)
                    ? Number.POSITIVE_INFINITY
                    : Number(matchGroups.right);
                if (('[' === matchGroups.left_delimiter ? number >= leftNumber : number > leftNumber) &&
                    (']' === matchGroups.right_delimiter ? number <= rightNumber : number < rightNumber)) {
                    return strtr(matchGroups.message, parameters);
                }
            }
        }
        else {
            const ruleMatch = part.match(/^\w+:\s*(.*?)$/);
            standardRules.push(ruleMatch ? ruleMatch[1] : part);
        }
    }
    const position = getPluralizationRule(number, locale);
    if (typeof standardRules[position] === 'undefined') {
        if (1 === parts.length && typeof standardRules[0] !== 'undefined') {
            return strtr(standardRules[0], parameters);
        }
        throw new Error(`Unable to choose a translation for "${id}" with locale "${locale}" for value "${number}". Double check that this translation has the correct plural options (e.g. "There is one apple|There are %count% apples").`);
    }
    return strtr(standardRules[position], parameters);
}
function getPluralizationRule(number, locale) {
    number = Math.abs(number);
    let _locale = locale;
    if (locale === 'pt_BR' || locale === 'en_US_POSIX') {
        return 0;
    }
    _locale = _locale.length > 3 ? _locale.substring(0, _locale.indexOf('_')) : _locale;
    switch (_locale) {
        case 'af':
        case 'bn':
        case 'bg':
        case 'ca':
        case 'da':
        case 'de':
        case 'el':
        case 'en':
        case 'en_US_POSIX':
        case 'eo':
        case 'es':
        case 'et':
        case 'eu':
        case 'fa':
        case 'fi':
        case 'fo':
        case 'fur':
        case 'fy':
        case 'gl':
        case 'gu':
        case 'ha':
        case 'he':
        case 'hu':
        case 'is':
        case 'it':
        case 'ku':
        case 'lb':
        case 'ml':
        case 'mn':
        case 'mr':
        case 'nah':
        case 'nb':
        case 'ne':
        case 'nl':
        case 'nn':
        case 'no':
        case 'oc':
        case 'om':
        case 'or':
        case 'pa':
        case 'pap':
        case 'ps':
        case 'pt':
        case 'so':
        case 'sq':
        case 'sv':
        case 'sw':
        case 'ta':
        case 'te':
        case 'tk':
        case 'ur':
        case 'zu':
            return 1 === number ? 0 : 1;
        case 'am':
        case 'bh':
        case 'fil':
        case 'fr':
        case 'gun':
        case 'hi':
        case 'hy':
        case 'ln':
        case 'mg':
        case 'nso':
        case 'pt_BR':
        case 'ti':
        case 'wa':
            return number < 2 ? 0 : 1;
        case 'be':
        case 'bs':
        case 'hr':
        case 'ru':
        case 'sh':
        case 'sr':
        case 'uk':
            return 1 === number % 10 && 11 !== number % 100
                ? 0
                : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 10 || number % 100 >= 20)
                    ? 1
                    : 2;
        case 'cs':
        case 'sk':
            return 1 === number ? 0 : number >= 2 && number <= 4 ? 1 : 2;
        case 'ga':
            return 1 === number ? 0 : 2 === number ? 1 : 2;
        case 'lt':
            return 1 === number % 10 && 11 !== number % 100
                ? 0
                : number % 10 >= 2 && (number % 100 < 10 || number % 100 >= 20)
                    ? 1
                    : 2;
        case 'sl':
            return 1 === number % 100 ? 0 : 2 === number % 100 ? 1 : 3 === number % 100 || 4 === number % 100 ? 2 : 3;
        case 'mk':
            return 1 === number % 10 ? 0 : 1;
        case 'mt':
            return 1 === number
                ? 0
                : 0 === number || (number % 100 > 1 && number % 100 < 11)
                    ? 1
                    : number % 100 > 10 && number % 100 < 20
                        ? 2
                        : 3;
        case 'lv':
            return 0 === number ? 0 : 1 === number % 10 && 11 !== number % 100 ? 1 : 2;
        case 'pl':
            return 1 === number
                ? 0
                : number % 10 >= 2 && number % 10 <= 4 && (number % 100 < 12 || number % 100 > 14)
                    ? 1
                    : 2;
        case 'cy':
            return 1 === number ? 0 : 2 === number ? 1 : 8 === number || 11 === number ? 2 : 3;
        case 'ro':
            return 1 === number ? 0 : 0 === number || (number % 100 > 0 && number % 100 < 20) ? 1 : 2;
        case 'ar':
            return 0 === number
                ? 0
                : 1 === number
                    ? 1
                    : 2 === number
                        ? 2
                        : number % 100 >= 3 && number % 100 <= 10
                            ? 3
                            : number % 100 >= 11 && number % 100 <= 99
                                ? 4
                                : 5;
        default:
            return 0;
    }
}

function formatIntl(id, parameters, locale) {
    if (id === '') {
        return '';
    }
    const intlMessage = new IntlMessageFormat(id, [locale.replace('_', '-')], undefined, { ignoreTag: true });
    parameters = { ...parameters };
    Object.entries(parameters).forEach(([key, value]) => {
        if (key.includes('%') || key.includes('{')) {
            delete parameters[key];
            parameters[key.replace(/[%{} ]/g, '').trim()] = value;
        }
    });
    return intlMessage.format(parameters);
}

let _locale = null;
let _localeFallbacks = {};
let _throwWhenNotFound = false;
function setLocale(locale) {
    _locale = locale;
}
function getLocale() {
    return (_locale ||
        document.documentElement.getAttribute('data-symfony-ux-translator-locale') ||
        (document.documentElement.lang ? document.documentElement.lang.replace('-', '_') : null) ||
        'en');
}
function throwWhenNotFound(enabled) {
    _throwWhenNotFound = enabled;
}
function setLocaleFallbacks(localeFallbacks) {
    _localeFallbacks = localeFallbacks;
}
function getLocaleFallbacks() {
    return _localeFallbacks;
}
function trans(message, parameters = {}, domain = 'messages', locale = null) {
    if (typeof domain === 'undefined') {
        domain = 'messages';
    }
    if (typeof locale === 'undefined' || null === locale) {
        locale = getLocale();
    }
    if (typeof message.translations === 'undefined') {
        return message.id;
    }
    const localesFallbacks = getLocaleFallbacks();
    const translationsIntl = message.translations[`${domain}+intl-icu`];
    if (typeof translationsIntl !== 'undefined') {
        while (typeof translationsIntl[locale] === 'undefined') {
            locale = localesFallbacks[locale];
            if (!locale) {
                break;
            }
        }
        if (locale) {
            return formatIntl(translationsIntl[locale], parameters, locale);
        }
    }
    const translations = message.translations[domain];
    if (typeof translations !== 'undefined') {
        while (typeof translations[locale] === 'undefined') {
            locale = localesFallbacks[locale];
            if (!locale) {
                break;
            }
        }
        if (locale) {
            return format(translations[locale], parameters, locale);
        }
    }
    if (_throwWhenNotFound) {
        throw new Error(`No translation message found with id "${message.id}".`);
    }
    return message.id;
}

export { getLocale, getLocaleFallbacks, setLocale, setLocaleFallbacks, throwWhenNotFound, trans };
