import {
    fromLocalizedDigits,
    toLocalizedDigits,
    numberFormat,
} from 'lightning/internationalizationLibrary';
import { isEmptyString } from 'lightning/inputUtils';

// This should be removed once we have a way to get the decimal and grouping separators
import { getLocale } from 'lightning/configProvider';

export function toIsoDecimal(numberAsString) {
    const result = transformLocalizedNumberToIsoDecimal(
        numberAsString,
        getLocaleSeparators()
    );
    if (isNaN(result)) {
        return '';
    }
    return result;
}

export function isValidNumber(numberAsString) {
    return !isNaN(
        transformLocalizedNumberToIsoDecimal(
            numberAsString,
            getLocaleSeparators()
        )
    );
}

export function fromIsoDecimal(numberAsString) {
    const decimalSeparator = getLocale().decimal;

    return toLocalizedDigits(numberAsString.replace('.', decimalSeparator));
}

// TODO: Too many options, simplify
export function increaseNumberByStep({
    value,
    increment,
    step,
    fractionDigits,
}) {
    const startingValue = value === '' || value == null ? '0' : value;

    const stepAsFloat = parseFloat(step);

    let result;
    if (isNaN(stepAsFloat)) {
        result = parseFloat(startingValue) + increment;
    } else {
        // ideally we'd round the value to the closest correct step, so that if say the step is '2', and the
        // current value is '1' it would increment to '2' instead of '3', since the former would be the valid
        // number given the step constraint, however this would significantly complicate the code, keeping
        // it simple for now.
        const increaseBy = increment * stepAsFloat;
        result = parseFloat(startingValue) + increaseBy;
    }
    return result.toFixed(fractionDigits);
}

export function calculateFractionDigitsFromStep(step) {
    let calculatedFractionDigits;

    if (step) {
        const stepAsString = String(step).toLowerCase();
        if (stepAsString !== 'any') {
            // lowering the case because we're checking for exponent format as well
            let fractionDigits = 0;
            if (
                stepAsString.indexOf('.') >= 0 &&
                stepAsString.indexOf('e') < 0
            ) {
                const fractionalPart = stepAsString.split('.')[1];
                // we're parsing to account for cases where the step is
                // '1.0', or '1.000', etc.
                if (parseInt(fractionalPart, 10) > 0) {
                    fractionDigits = fractionalPart.length;
                }
            } else if (stepAsString.indexOf('e-') > 0) {
                // exponent form eg. 1.5e-5
                const splitOnExponent = stepAsString.split('e-');
                const fractionalPart = splitOnExponent[0].split('.')[1];
                const exponentPart = splitOnExponent[1];
                const fractionalPartLength = fractionalPart
                    ? fractionalPart.length
                    : 0;
                fractionDigits =
                    parseInt(exponentPart, 10) + fractionalPartLength;
            }
            calculatedFractionDigits = fractionDigits;
        }
    }
    return calculatedFractionDigits;
}

export function formatNumber(numberAsString, options) {
    if (isEmptyString(numberAsString)) {
        return '';
    }

    let formattedValue = numberAsString;
    let inputValue = numberAsString;

    // set formatter style & default options
    const formatStyle = options.style;
    const formatOptions = { style: formatStyle };

    formatOptions.minimumFractionDigits = options.minimumFractionDigits;
    formatOptions.maximumFractionDigits = options.maximumFractionDigits;

    if (formatStyle === 'percent-fixed') {
        // percent-fixed just uses percent format and divides the value by 100
        // before passing to the library, this is to deal with the
        // fact that percentages in salesforce are 0-100, not 0-1
        formatOptions.style = 'percent';
        const inputValueAsString = inputValue.toString();
        const normalisedNumberInPercent = parseFloat(inputValue) / 100;

        // If the number contains fraction digits and is not in an exponent format
        if (
            inputValueAsString.indexOf('.') > 0 &&
            inputValueAsString.indexOf('e') < 0
        ) {
            // Depending on the input number, division by 100 may lead to rounding errors
            // (e.g 0.785 / 100 is 0.007850000000000001), so we need to round back
            // to the correct precision, that is - existing number of fractional digits
            // plus extra 2 for division by 100.
            inputValue = normalisedNumberInPercent.toFixed(
                inputValueAsString.split('.')[1].length + 2
            );
        } else {
            inputValue = normalisedNumberInPercent;
        }
    }

    try {
        formattedValue = numberFormat(formatOptions).format(inputValue) || '';
    } catch (ignore) {
        // ignore any errors
    }
    return formattedValue;
}

// Exporting only to test
export function transformLocalizedNumberToIsoDecimal(
    numberAsString,
    { decimalSeparator, groupSeparator }
) {
    if (numberAsString == null || numberAsString.length === 0) {
        return '';
    }

    // remove the grouping separator
    let result = numberAsString.split(groupSeparator).join('');
    if (decimalSeparator !== '.') {
        // replace the local decimal separator with a
        result = result.replace(decimalSeparator, '.');
    }
    return fromLocalizedDigits(addLeadingZeroIfNeeded(result));
}

export function normalizeNumber(value) {
    if (value === undefined || value === null || isNaN(value)) {
        return '';
    }
    return String(value);
}

function addLeadingZeroIfNeeded(result) {
    // If the number starts with +. OR  -. OR . ; insert a 0 before the decimal separator.
    // eg. -.2 -> -0.2
    const decimalSeparatorLocation = result.indexOf('.');
    if (decimalSeparatorLocation === 0 || decimalSeparatorLocation === 1) {
        const firstCharacter = result.charAt(0);
        if (
            firstCharacter === '+' ||
            firstCharacter === '-' ||
            firstCharacter === '.'
        ) {
            result =
                result.substring(0, decimalSeparatorLocation) +
                '0' +
                result.substring(decimalSeparatorLocation);
        }
    }
    return result;
}

function getLocaleSeparators() {
    const decimalSeparator = getLocale().decimal;
    const groupSeparator = getLocale().grouping;

    return { decimalSeparator, groupSeparator };
}
