/**
 * @license
 * Copyright 2023 Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { InputMask, createMask, MaskedRange } from 'imask';
import { isNumeric } from '@tylertech/forge-core';
export const DEFAULT_DATE_MASK = '0`0{/}`0`0{/}`0`0`0`0';
export class DateInputMask {
    constructor(_element, _options = {}) {
        this._element = _element;
        this._options = _options;
        this._maskOptions = this._createOptions();
        this._mask = new InputMask(this._element, this._maskOptions);
        if (this._options.onChange) {
            this._acceptListener = () => this._onAccept();
            this._mask.on('accept', this._acceptListener);
        }
    }
    destroy() {
        if (this._acceptListener) {
            this._mask.off('accept', this._acceptListener);
        }
        this._mask.destroy();
    }
    resolve(value) {
        const masked = createMask(this._maskOptions);
        return masked.resolve(value);
    }
    _onAccept() {
        if (typeof this._options.onChange === 'function') {
            this._options.onChange(this._mask.value);
        }
    }
    get _isDefaultMask() {
        return this._options.pattern === DEFAULT_DATE_MASK;
    }
    _createOptions() {
        const options = {
            mask: this._options.pattern || DEFAULT_DATE_MASK,
            lazy: this._options.showMaskFormat === undefined ? false : !this._options.showMaskFormat,
            overwrite: true,
            prepare: (value, masked, flags) => this._prepare(value, masked, flags, this._mask),
            blocks: {
                MM: {
                    mask: MaskedRange,
                    autofix: true,
                    from: 1,
                    to: 12,
                    maxLength: 2
                },
                DD: {
                    mask: MaskedRange,
                    autofix: true,
                    from: 1,
                    to: 31,
                    maxLength: 2
                },
                YYYY: {
                    mask: MaskedRange,
                    autofix: true,
                    from: 0,
                    to: 9999,
                    maxLength: 4
                }
            }
        };
        return options;
    }
    _prepare(value, masked, flags, maskInstance) {
        if (typeof this._options.prepareCallback === 'function') {
            return this._options.prepareCallback.call(null, value, masked, flags, this._mask);
        }
        return this._isDefaultMask ? this._prepareDefault(value, masked, flags, maskInstance) : value;
    }
    _prepareDefault(value, masked, flags, maskInstance) {
        if (!flags.input || !value.length || !maskInstance) {
            return value;
        }
        // eslint-disable-next-line @typescript-eslint/dot-notation
        const isAllSelected = maskInstance['_selection'] && maskInstance['_selection'].end === 10;
        const currentValue = isAllSelected ? '' : maskInstance.value;
        if (!isNumeric(value)) {
            // Before we ignore this character let's do some checks for common scenarios where the user enters a slash to help with coercion
            if (value === '/') {
                const isFinalMonthChar = maskInstance.cursorPos === 2 && currentValue.length === 1;
                if (isFinalMonthChar) {
                    // The user attempted to press the slash key after entering a single month character so let's pad the value
                    this._setMaskedValueAdjusted(currentValue.padStart(2, '0'), 3);
                    return '/';
                }
                const isFinalDayChar = maskInstance.cursorPos === 5 && currentValue.length === 4;
                if (isFinalDayChar) {
                    // The user attempted to press the slash key after entering a single day character so let's pad the value
                    const newValue = `${currentValue.substring(0, 3)}${currentValue[3].padStart(2, '0').padStart(2, '0')}`;
                    this._setMaskedValueAdjusted(newValue, 3);
                    return '/';
                }
            }
            return value;
        }
        // We know that the value is numeric so let's coerce it to a number type for comparison below
        const numValue = +value;
        // Attempt to pad a leading zero to the month segment
        const isFirstMonthChar = maskInstance.cursorPos === 1 && numValue > 1;
        if (isFirstMonthChar) {
            // Replace just the month segment with the padded value and update cursor position
            const newValue = `${String(numValue).padStart(2, '0')}${currentValue.slice(2)}`;
            this._setMaskedValueAdjusted(newValue, 3);
            return '/';
        }
        // Attempt to pad a leading zero to the day segment
        const isFirstDayChar = (maskInstance.cursorPos === 3 || maskInstance.cursorPos === 4) && numValue > 3;
        if (isFirstDayChar) {
            // Replace just the day segment with the padded value and update cursor position
            const newValue = `${currentValue.substring(0, 3)}${String(numValue).padStart(2, '0')}${currentValue.slice(5)}`;
            this._setMaskedValueAdjusted(newValue, 3);
            return '/';
        }
        // Attempt to automatically add a slash after the month or day segment is complete and/or move cursor to after slash
        if (!this._options.showMaskFormat) {
            if (maskInstance.cursorPos === 2 || maskInstance.cursorPos === 5) {
                let newValue;
                if (maskInstance.cursorPos === 2) {
                    newValue = `${currentValue.substring(0, 1)}${numValue}/${currentValue.slice(3)}`;
                }
                else {
                    newValue = `${currentValue.substring(0, 4)}${numValue}/${currentValue.slice(6)}`;
                }
                this._setMaskedValueAdjusted(newValue, 2);
                return '';
            }
        }
        return value;
    }
    _setMaskedValueAdjusted(value, position) {
        this._mask.unmaskedValue = value;
        window.requestAnimationFrame(() => this._mask.updateCursor(this._mask.cursorPos + position));
    }
    updateMask() {
        this._mask.updateValue();
    }
    get maskedValue() {
        return this._mask.value;
    }
    set maskedValue(value) {
        this._mask.value = value;
    }
    get unmaskedValue() {
        return this._mask.unmaskedValue;
    }
    set unmaskedValue(value) {
        this._mask.unmaskedValue = value;
    }
}
