import { GOVBRSuggest } from '../../../suggest/1.2.0/GOVBRSuggest.mjs';
import Fuse from '../../../fusejs/7.0.0/fuse.min.mjs';

/**
 * @typedef {Object} GOVBRInputSuggestDados
 * @prop {string} valor
 * @prop {Record<string, string>} camposAdicionais
 * @prop {string} icone
 * @prop {string[]} termos
 */

/**
 * Webcomponent que permite customizar inputs de tipo TEXTO para que permitam o Usuário pesquisar neles.
 */
export class GOVBRInputSuggest extends HTMLElement {
    /**
     * @type {GOVBRSuggest}
     */
    _suggest;
    /**
     * @type {GOVBRInputSuggestDados[]}
     */
    _dados;

    /**
     * @type {boolean}
     */
    aberto;
    /**
     * @type {HTMLFormElement}
     */
    _form;
    /**
     * @type {HTMLInputElement}
     */
    _input;
    /**
     * @type {GOVBRInputSuggestDados[]}
     */
    _valores = [];
    /**
     * @type {string}
     */
    _lastQuery;
    /**
     * @type {number}
     */
    _debouncer;

    /**
     * @type {(event: InputEvent) => void}
     */
    fnInputChange;
    /**
     * @type {(event: FocusEvent) => void}
     */
    fnInputBlur;
    /**
     * @type {(event: FocusEvent) => void}
     */
    fnInputFocus;

    constructor() {
        super();
        this._suggest = new GOVBRSuggest(this.parentElement || null);
        this._suggest.forcePosicaoX = 'inicio-apos';
    }

    connectedCallback() {
        const shadow = this.attachShadow({ mode: 'open' });

        if (this.hasAttribute('data-dados')) {
            this.carregaDados();
        }
        setTimeout(async () => {
            await GOVBRPlataformaJS.importarStyleSheet('', 'custom-inputs/suggest', '', 'GOVBRInputSuggest', shadow);
            await GOVBRPlataformaJS.importarStyleSheet('', 'fontawesome', '6.5.1', 'css/all.min', shadow);

            const slot = document.createElement('slot');
            shadow.append(slot);

            this._input = this.querySelector('input');
            this._input.setAttribute('autocomplete', 'none');

            let form = /** @type {HTMLElement} */ (this);
            while (this._form && (this._form.tagName || '').toUpperCase() != 'FORM') {
                form = form.parentElement;
            }
            this._form = /** @type {HTMLFormElement} */ (form);

            await this.refresh();

            this['fnInputChange'] =
                this['fnInputChange'] ||
                (async () => {
                    this.aberto = true;
                    if (this._lastQuery === this._input.value) {
                        if (document.activeElement === this._input && !this._suggest.isAberto()) {
                            this.abreSuggest();
                        }
                        return;
                    }
                    if (this._debouncer) {
                        clearTimeout(this._debouncer);
                    }
                    if (this.dataMinLength && this._input.value.length < parseInt(this.dataMinLength)) {
                        return this.fechaSuggest();
                    }
                    this._debouncer = setTimeout(() => {
                        (async () => {
                            this._debouncer = null;
                            /**
                             * @type {Record<string, string>}
                             */
                            const filtros = {};
                            (this.dataFiltros || []).forEach((filtro) => {
                                const retFiltro = filtro.split(':');
                                const nomeFiltro = retFiltro.shift();
                                const valFiltro = retFiltro.join(':');
                                if (nomeFiltro == 'campo') {
                                    if (this._form) {
                                        const campo = /** @type {HTMLInputElement} */ (this._form.querySelector('#' + valFiltro));
                                        if (campo?.value) {
                                            filtros[valFiltro] = campo.value;
                                        }
                                    }
                                } else {
                                    filtros[nomeFiltro] = JSON.parse(valFiltro);
                                }
                            });
                            try {
                                if (this.dataDados) {
                                    this._valores = this.filtraDados(this._input.value, filtros);
                                }
                                this.refresh();
                            } catch (ex) {
                                this.fechaSuggest();
                                GOVBR.mensagem(ex, 'alerta');
                            }
                        })();
                    }, 500);
                    this.lastQuery = this._input.value;
                });
            this['fnInputBlur'] =
                this['fnInputBlur'] ||
                (async (event) => {
                    const related = /** @type {HTMLElement} */ (event.relatedTarget);
                    if (document.activeElement === document.body && related && this._suggest.isAlvoInterno(related)) {
                        this._input.focus();
                        return;
                    }
                    this.aberto = false;
                    this.fechaSuggest();
                });
            this['fnInputFocus'] =
                this['fnInputFocus'] ||
                (() => {
                    if (!this._suggest.isAberto()) {
                        this['fnInputChange'](null);
                    }
                });
            this._input.addEventListener('focus', this['fnInputFocus']);
            this._input.addEventListener('input', this['fnInputChange']);
            this._input.addEventListener('blur', this['fnInputBlur']);
        }, 0);
    }

    /**
     * Filtra os dados do suggest no cliente.
     * @param {string} query
     * @param {Record<string, string>} filtros
     * @returns {GOVBRInputSuggestDados[]}
     */
    filtraDados(query, filtros) {
        let itens = this.dataDados || [];
        const filtrosArray = Object.entries(filtros);
        if (filtrosArray.length) {
            itens = itens.filter((item) => {
                return filtrosArray.every(([chave, valor]) => {
                    return item.camposAdicionais?.[chave] == valor;
                });
            });
        }
        const fuseOptions = {
            threshold: 0.1,
            ignoreLocation: true,
            includeMatches: true,
            includeScore: true,
            /**
             * @param {any} obj
             * @param {string|string[]} path
             * @returns {string|string[]}
             */
            getFn: (obj, path) => {
                const value = Fuse.config.getFn(obj, path) || '';
                if (Array.isArray(value)) {
                    return value.map((el) => el.normalize('NFD').replace(/[\u0300-\u036f]/g, ''));
                }
                return value.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
            },
            keys: ['valor', 'termos'],
        };
        const fuse = new Fuse(itens, fuseOptions);
        return fuse
            .search(query.normalize('NFD').replace(/[\u0300-\u036f]/g, ''))
            .map((e) => {
                return itens[e.refIndex];
            })
            .slice(0, 100);
    }

    /**
     * Abre o suggest para o input.
     */
    abreSuggest() {
        const suggest = this._suggest.abreSuggest(this._input);
        suggest.classList.add('govbr-plataforma-lista-input-suggest');
        suggest.tabIndex = 0;
        this.montaListaSuggest(suggest);
    }

    /**
     * Fecha o suggest para o input.
     */
    fechaSuggest() {
        this._suggest.fechaSuggest();
    }

    /**
     * @param {HTMLElement} suggest
     */
    montaListaSuggest(suggest) {
        (this._valores || []).forEach((valor) => {
            valor.camposAdicionais = valor.camposAdicionais || {};
            const item = document.createElement('a');
            item.className = 'lista-suggest-item';
            item.href = '#';
            item.addEventListener('click', (e) => {
                e.preventDefault();
                this._input.focus();
                this._input.value = valor.valor;
                this._input.dispatchEvent(new Event('input'));
                this._input.dispatchEvent(new Event('change'));
                this.aberto = false;
                this.fechaSuggest();
                if (this._form) {
                    Object.entries(valor.camposAdicionais).forEach(([campo, valor]) => {
                        const input = /** @type {HTMLInputElement} */ (this._form.querySelector('#' + campo));
                        if (input) {
                            input.value = valor;
                            input.dispatchEvent(new Event('input'));
                            input.dispatchEvent(new Event('change'));
                        }
                    });
                }
                const event = new CustomEvent('select-item', {
                    detail: {
                        valor: this._input,
                    },
                });
                this.dispatchEvent(event);
            });
            if (valor.icone) {
                const icone = document.createElement('i');
                icone.className = 'icone-valor ' + valor.icone;
                item.append(icone);
            }
            const valorBase = document.createElement('span');
            valorBase.className = 'valor-base';
            valorBase.innerText = valor.valor;
            item.append(valorBase);
            const outrosValores = Object.values(valor.camposAdicionais);
            if (outrosValores.length) {
                const divOutrosValores = document.createElement('div');
                divOutrosValores.className = 'ct-outros-valores';
                outrosValores.forEach((valor) => {
                    const span = document.createElement('span');
                    span.className = 'outros-valores';
                    span.innerText = valor;
                    divOutrosValores.append(span);
                });
                item.append(divOutrosValores);
            }
            suggest.append(item);
        });
    }

    async refresh() {
        if (this.aberto) {
            if (this._valores.length && document.activeElement === this._input) {
                this.abreSuggest();
            } else {
                this.fechaSuggest();
            }
        }
        const existeValor = (this._valores || []).find((e) => {
            return e.valor == this._input.value;
        });
        if (this.dataForceValor) {
            if (existeValor) {
                this._input.setCustomValidity('');
                this.classList.remove('valor-invalido');
            } else {
                this._input.setCustomValidity('O valor informado não existe, selecione um item válido da lista...');
                this.classList.add('valor-invalido');
            }
        } else {
            if (existeValor) {
                this.classList.remove('valor-novo');
            } else {
                this.classList.add('valor-novo');
            }
        }
    }

    get dataForceValor() {
        return this.hasAttribute('data-force-valor');
    }
    /**
     * @return {string[]}
     */
    get dataFiltros() {
        try {
            return JSON.parse(this.getAttribute('data-filtros') || '[]') || [];
        } catch (ex) {
            return [];
        }
    }
    get dataMinLength() {
        return this.getAttribute('data-min-length') || '';
    }

    carregaDados() {
        if (this.hasAttribute('data-dados')) {
            this._dados = JSON.parse(this.getAttribute('data-dados'));
            this.removeAttribute('data-dados');
        }
    }

    get dataDados() {
        this.carregaDados();
        return this._dados;
    }
}

customElements.define('govbr-plataforma-input-suggest', GOVBRInputSuggest);
