import { BaseInput } from './base-input';
import { observable, customElement, bindable, bindingMode } from 'aurelia-framework';
import { AsYouType, parsePhoneNumber, CountryCode } from 'libphonenumber-js';
import { UIInternal, CountryDialCodes, ICountryDialCode } from '../../exports';

@customElement("ui-input-phone")
export class UIInputPhone extends BaseInput {

    @bindable({ defaultBindingMode: bindingMode.twoWay })
    public value: string;
    public valueChanged(value: string) {
        this.validateValue(value);
    };

    @bindable
    public defaultCountry: CountryCode = "BE";

    @bindable()
    public autofocus: boolean = false;


    @observable
    private currentCountry: CountryCode;
    private currentCountryChanged(newValue, oldValue) {
        this.setDialCode(newValue, oldValue);
        let ayt = new AsYouType({ defaultCountry: this.currentCountry, defaultCallingCode: this.currentDialCode });
        ayt.input(this.currentDialCode);
        this.setPlaceholder(ayt);
    };

    @observable
    private isValid: boolean;
    private isValidChanged(value: boolean, oldValue: boolean) {
        if (value == true) {
            this.fadeValidIcon();
        }
    };

    private isValidTimer: any;
    private isValidFadeTimer: any;
    private currentPlaceholder: string = "";
    private currentDialCode: string;
    private codeFlagsLib: CountryDialCodes;
    private countriesList: Array<ICountryDialCode>;
    private uiInputRef: HTMLElement;

    constructor(element: Element) {
        super(element);
        this.codeFlagsLib = new CountryDialCodes();
    }

    public attached() {
        // Init the countries flags dial code list
        this.initCountriesList();

        if (this.defaultCountry && this.value == null || this.value.toString().trim().length == 0) {
            this.currentCountry = this.defaultCountry;
        }

        this.validateValue(this.value, false);

        UIInternal.subscribe(UIInternal.EVT_CHANGE_LANGUAGE, () => {
            this.initCountriesList();
        });
    }

    private initCountriesList(): void {
        this.countriesList = this.codeFlagsLib.getCountryListMap().sortBy("isFavorite", false);
        this.prepareHtmlForSelectOptions();
    }

    /**
     * On-focus input, if the value is null or empty, set the current dial code.
     *
     * @private
     * @memberof UIInputPhone
     */
    private setCurrentDialCodeAsValue() {
        if ((this.value == null || this.value.trim().length == 0) && this.currentDialCode != null) {
            this.value = this.currentDialCode + " ";
        }
    }

    /**
     * Change the dialCode ('+32', '+33', ...) of the current phone number value if the given newValue match a new dialCode.
     *
     * @private
     * @param {string} newValue
     * @param {string} oldValue
     * @return {*}  {void}
     * @memberof UIInputPhone
     */
    private setDialCode(newValue: string, oldValue: string): void {
        if (this.countriesList == null) return;
        let ctry = this.countriesList.find(co => co.code == newValue);
        this.currentDialCode = ctry?.dialCode;
        if (this.value?.trim().length > 0) {
            let oldCtry = this.countriesList.find(co => co.code == oldValue);
            if (oldCtry != null && ctry != null && this.value.replace(" ", "").includes(oldCtry.dialCode)) {
                this.value = this.value.replace(" ", "").replace(oldCtry.dialCode, ctry.dialCode) + " ";
            } else if (!this.value.includes(ctry.dialCode)) {
                this.value = ctry.dialCode + this.value;
            }
        }
    }


    /**
     * Prepare the html content for the select:options of the view.
     *
     * @private
     * @memberof UIInputPhone
     */
    private prepareHtmlForSelectOptions(): void {
        this.countriesList?.filter(co => co).forEach(co => {
            co.htmlForSelectOption =
                `<div class="ui-input-phone__option-country">
                    <span class="ui-input-phone__option-country__flag"><span class="ui-flag ${co.code}"></span></span>
                    <span class="ui-input-phone__option-country__dial-code">${co.dialCode}</span>    
                    <span class="ui-input-phone__option-country__country">${co.country}</span>
                    ${ co.isFavorite ? "<span class='ui-input-phone__option-country__favorite digi-star'></span>" : "" }
                    <span class="ui-input-phone__option-country__code">${co.code}</span>
                </div>`;
        });
    }


    /**
     * Validate the given phone number, and set this.value if it is valid.
     *
     * @private
     * @param {string} value
     * @memberof UIInputPhone
     */
    private validateValue(value: string, checkValidity = true): void {
        this.errors = [];

        if (value == null || value.toString().trim().length == 0) {
            this.isValid = null;
            return;
        }

        if (this.isValidFadeTimer) clearTimeout(this.isValidFadeTimer);

        let ayt = new AsYouType();
        ayt.input(value);
        this.formatPhoneValue(ayt, checkValidity);
        this.checkNewCountry(ayt);
        // this.setMask(ayt);
    }


    /**
     * Format the phone number value corresponding to the country international format.
     *
     * @private
     * @param {AsYouType} ayt
     * @memberof UIInputPhone
     */
    private formatPhoneValue(ayt: AsYouType, checkValidity = true): void {
        try {
            let formattedValue: string = (<any>ayt).formattedOutput;

            if (checkValidity) this.setIsValid(formattedValue);

            if (this.value !== formattedValue && formattedValue?.length > 3) {

                // Check if the focus of the input is inside or at the end of value, and reset it after formatting the value, see beyond.
                let focusSelStart: number, focusSelEnd: number;
                let focusSelDir: "forward" | "backward" | "none";
                let inputRef: HTMLInputElement;
                if (this.uiInputRef) {
                    inputRef = this.uiInputRef.querySelector('input');
                    if (inputRef?.selectionStart < this.value?.length) {
                        focusSelStart = inputRef.selectionStart;
                        focusSelEnd = inputRef.selectionEnd;
                        focusSelDir = inputRef.selectionDirection;
                    }
                }

                UIInternal.queueTask(() => {
                    this.value = formattedValue;

                    // Reset the focus selector of input if needed, because the formatting push the cursor at the end of the input.
                    if (focusSelStart != null && inputRef?.setSelectionRange != null) {
                        UIInternal.queueTask(() => {
                            inputRef.setSelectionRange(focusSelStart, focusSelEnd, focusSelDir);
                        });
                    }
                });
            }
        } catch (error) {
            console.error(error);
        }
    }


    /**
     * Check if the given value is a valid phone number international formatted, and set this.isValid.
     *
     * @private
     * @param {string} value
     * @memberof UIInputPhone
     */
    private setIsValid(value: string): void {
        if (value == null || value.trim().length == 0) {
            this.isValid = null;
            return;
        }
        let pn = parsePhoneNumber(value);
        this.isValid = null;
        if (this.isValidTimer) clearTimeout(this.isValidTimer);
        this.isValid = pn.isValid();
    }


    /**
     * Replace the currentCountry if the phone value match a new country.
     *
     * @memberof UIInputPhone
     */
    private checkNewCountry(ayt: AsYouType): void {
        let ctry = ayt.getCountry();
        if (ctry != null && this.currentCountry != ctry) {
            this.currentCountry = ctry;
        }
    }


    /**
     * Set and format the mask for ui-inputmask.
     *
     * @private
     * @param {AsYouType} ayt
     * @memberof UIInputPhone
     */
    // private setMask(ayt: AsYouType) {
    //     let mask: string = (<any>ayt).formatter?.template;
    //     if (mask != null) {
    //         let callingCode = `+${(<any>ayt).state?.callingCode}`;
    //         mask = mask.slice(callingCode.length);
    //         mask = mask
    //             .replace(/\x/g, '#')
    //             // .replace(/\ /g, '-')
    //         ;
    //         mask = callingCode + mask;
    //         this.mask = mask;
    //     }
    // }


    /**
     * Set the currentPlaceholder corresponding to the country international format of the phone number value.
     * 
     * @private
     * @param {AsYouType} ayt
     * @memberof UIInputPhone
     */
    private setPlaceholder(ayt: AsYouType): void {
        let template: string = (<any>ayt).formatter?.template;
        if (template != null && this.currentDialCode != null) {
            this.currentPlaceholder = this.currentDialCode + template.substring(this.currentDialCode.length);
        } else {
            this.currentPlaceholder = null;
        }
    }


    /**
     * Fade out the icon-valid of the view after delay in ms. Default is 3000ms.
     * Set this.isValid to null.
     *
     * @private
     * @memberof UIInputPhone
     */
    private fadeValidIcon(delay = 3000): void {
        if (this.isValidFadeTimer) clearTimeout(this.isValidFadeTimer);
        this.isValidFadeTimer = setTimeout(() => {
            this.isValid = null;
        }, delay);
    }
}
