import { GOVBRSuggest } from '../../suggest/1.2.0/GOVBRSuggest.mjs';
import {
    GOVBRFormsUCComponente,
    GOVBRFormsUCComponenteContainer,
    GOVBRFormsUCComponenteFactory,
    GOVBRFormsUCComponenteInput,
    GOVBRFormsUCComponenteInputLista,
    GOVBRFormsUCComponenteTabsContainer,
} from './GOVBRFormsUCComponente.mjs';
import { GOVBRFormsUCConfiguradorEventos } from './GOVBRFormsUCConfiguradorEventos.mjs';

/**
 * Classe para o UC de formulários dinâmicos
 */
class GOVBRFormsUC {
    /**
     * @type {import("./GOVBRFormsUserControl.mjs").GOVBRFormsUserControl}
     */
    userControl;
    /**
     * @type {HTMLElement}
     */
    host;
    /**
     * @type {HTMLElement}
     */
    container;
    /**
     * @type {HTMLElement}
     */
    elPropriedades;

    /**
     * Utilizado para controle de múltiplos formulários.
     * @type {import('./GOVBRFormsUserControl.mjs').GOVBRFormsFormulario[]}
     */
    formularios = [];
    /**
     * @type {import('./GOVBRFormsUserControl.mjs').GOVBRFormsFormulario}
     */
    formulario;
    /**
     * @type {GOVBRFormsUC[]}
     */
    ucForms = [];
    /**
     * @type {GOVBRFormsUCComponente[]}
     */
    componentes;
    /**
     * @type {Record<string, GOVBRFormsUCComponente>}
     */
    componentesId;
    /**
     * @type {Record<string, import('./GOVBRFormsUserControl.mjs').GOVBRFormsValor>}}
     */
    valores;
    /**
     * @type {Record<string, string>}
     */
    _propriedades;

    /**
     * @type {(valor: string) => void}
     */
    fnAtualizaValor;

    /**
     * @type {HTMLElement}
     */
    componenteHover;

    /**
     * @type {number}
     */
    timeoutRepaint;
    /**
     * @type {number}
     */
    timeoutValidado;

    /**
     * @type {HTMLElement}
     */
    componenteArrastando;
    /**
     * @type {[number, number]}
     */
    posicaoInicialDrag;
    /**
     * @type {string}
     */
    componenteSelecionado;
    /**
     * @type {string}
     */
    ultimoComponenteSelecionado;
    /**
     * @type {HTMLElement}
     */
    containerAlvo;

    /**
     * @type {GOVBRSuggest}
     */
    suggest;

    /**
     * @type {boolean}
     */
    previsualizando;
    /**
     * @type {boolean}
     */
    ignoraTrocaValores;
    /**
     * @type {boolean}
     */
    possuiValoresIniciais;

    /**
     * @type {'interno'|'externo'}
     */
    localExibicao;

    /**
     * @param {import('./GOVBRFormsUserControl.mjs').GOVBRFormsUserControl} userControl
     * @param {HTMLElement} host
     * @param {boolean} preencher
     * @param {import('./GOVBRFormsUserControl.mjs').GOVBRFormsFormulario} formulario
     * @param {'interno'|'externo'} localExibicao
     */
    constructor(userControl, host, preencher, formulario, localExibicao) {
        this.userControl = userControl;
        this.host = host;
        this.previsualizando = !!preencher;
        this.localExibicao = localExibicao;
        this.host.style.setProperty('height', this.userControl.ALTURA_COMPONENTE || '100%');
        this.host.style.setProperty('width', this.userControl.LARGURA_COMPONENTE || '100%');
        this.suggest = new GOVBRSuggest(this.host);
        this.container = document.createElement('div');
        this.container.classList.add('govbr-forms-base-container');
        if (preencher) {
            this.host.classList.add('govbr-forms-visualizacao');
        } else {
            this.host.classList.add('govbr-forms-edicao');
        }
        this.container.tabIndex = 0;
        this.host.append(this.container);
        const sep = document.createElement('hr');
        this.host.append(sep);
        this.elPropriedades = document.createElement('article');
        this.elPropriedades.classList.add('govbr-forms-propriedades');
        this.host.append(this.elPropriedades);

        this.criaEventos();
        this.carregaEstilos();
        GOVBR.logs.debug('Forms - Carregado');
        GOVBR.logs.debug(this);

        /**
         * @type {import('./GOVBRFormsUserControl.mjs').GOVBRFormsFormulario[]}
         */
        let formularios;
        if (Array.isArray(this.userControl.FORMULARIOS)) {
            formularios = this.userControl.FORMULARIOS;
        } else {
            if (this.userControl.FORMULARIOS?.Id) {
                formularios = [this.userControl.FORMULARIOS];
            } else {
                formularios = [];
            }
        }
        if (!formularios[0]) {
            formularios[0] = {};
        }
        this.userControl.FORMULARIOS = formularios;
        this.formularios = formularios;
        if (!preencher || formulario || this.formularios.length === 1) {
            if (formulario) {
                this.formulario = formulario;
            } else {
                this.formulario = this.formularios[0];
            }
        }
        if (!this.formulario) {
            this.montaMultiploFormularios();
            if (!this.userControl.VALORES_JSON) {
                this.userControl.VALORES_JSON = '[]';
            }
        } else {
            if (!formulario && !this.userControl.OCULTA_TITULO_FORMULARIO) {
                const tituloForm = document.createElement('div');
                tituloForm.classList.add('govbr-forms-container-titulo');
                tituloForm.innerText = this.formulario.Titulo || '';
                this.host.prepend(tituloForm);
            }
            if (!this.formulario.Componentes) {
                this.formulario.Componentes = [];
            }
            // Trata os valores vindos da retaguarda, o objetivo é caso estejam sendo utilizado os identificadores
            // via "dataIdentificador" ele ainda consiga preencher os valores.
            /**
             * @param {string} id
             * @param {import('./GOVBRFormsUserControl.mjs').GOVBRFormsComponente[]} atual
             * @returns {import('./GOVBRFormsUserControl.mjs').GOVBRFormsComponente}
             */
            const findComponente = (id, atual = null) => {
                let ret = null;
                atual = atual || this.formulario.Componentes;
                atual.some((componente) => {
                    if (componente.Filhos?.length) {
                        const filho = findComponente(id, componente.Filhos);
                        if (filho) {
                            ret = filho;
                            return true;
                        }
                    }
                    if (componente.Id === id) {
                        ret = componente;
                        return true;
                    }
                    if (componente.Propriedades) {
                        const prop = componente.Propriedades.find((e) => e.Nome === 'identificador');
                        if (prop && prop.Valor === id) {
                            ret = componente;
                            return true;
                        }
                    }
                    return false;
                });
                return ret;
            };
            if (Array.isArray(this.userControl.VALORES) && this.userControl.VALORES.length) {
                this.formulario.Valores = this.userControl.VALORES.map((valor) => {
                    const identificadores = (valor.Id || '').split('_');
                    valor.Id = identificadores
                        .map((identificador, idx) => {
                            let idxOrig = '';
                            if (idx < identificadores.length - 1) {
                                const partesIdentificador = identificador.split('-');
                                idxOrig = '-' + partesIdentificador.pop();
                                identificador = partesIdentificador.join('-');
                            }
                            const componente = findComponente(identificador);
                            if (!componente?.Id) {
                                GOVBRPlataformaJS.mensagem('Não encontrou o componente de identificador ' + valor.Id, 'erro');
                            }
                            return (componente?.Id || valor.Id) + idxOrig;
                        })
                        .join('_');
                    return valor;
                });
                this.possuiValoresIniciais = true;
            } else if (Array.isArray(this.formulario.Valores) && this.formulario.Valores.length) {
                this.formulario.Valores = this.formulario.Valores.map((valor) => {
                    const identificadores = (valor.Id || '').split('_');
                    valor.Id = identificadores
                        .map((identificador, idx) => {
                            let idxOrig = '';
                            if (idx < identificadores.length - 1) {
                                const partesIdentificador = identificador.split('-');
                                idxOrig = '-' + partesIdentificador.pop();
                                identificador = partesIdentificador.join('-');
                            }
                            const componente = findComponente(identificador);
                            if (!componente?.Id) {
                                GOVBRPlataformaJS.mensagem('Não encontrou o componente de identificador ' + valor.Id, 'erro');
                            }
                            return (componente?.Id || valor.Id) + idxOrig;
                        })
                        .join('_');
                    return valor;
                });
                this.possuiValoresIniciais = true;
            } else {
                this.formulario.Valores = [];
            }
            this.valores = Object.fromEntries(
                this.formulario.Valores.map((valor) => {
                    return [valor.Id, valor];
                })
            );
            this.componentes = this.formulario.Componentes.map((dadosComponente) => GOVBRFormsUCComponenteFactory.getInstanciaComponente(this, dadosComponente));
            this.componenteSelecionado = null;
            this.recriaAreaFormulario(true).then(() => {
                if (preencher) {
                    this.container.querySelectorAll('input, textarea, select').forEach((e) => {
                        e.dispatchEvent(new Event('change'));
                    });
                    this.disparaEventos('abrir', this.formulario.Eventos || []);
                }
            });
        }
    }

    /**
     * Monta a visualização de múltiplos formulários.
     */
    montaMultiploFormularios() {
        const isAba = this.userControl.FORMATO_MULTIFORMULARIOS === 'TABS';
        const containerConteudo = document.createElement('div');
        containerConteudo.classList.add('govbr-forms-container-multiform-conteudo');
        containerConteudo.classList.add('govbr-forms-container-multiform-conteudo-' + (this.userControl.FORMATO_MULTIFORMULARIOS || 'PANELS').toLowerCase());
        /**
         * @type {HTMLElement}
         */
        let containerTabs;
        if (isAba) {
            containerTabs = document.createElement('nav');
            containerTabs.classList.add('govbr-forms-container-multiform-tabs');
            containerConteudo.append(containerTabs);
        }
        this.ucForms = [];
        /**
         * @type {[HTMLElement, HTMLElement]}
         */
        let atual = null;
        this.formularios.forEach((formulario, idx) => {
            /**
             * @type {HTMLElement}
             */
            let tituloForm;
            if (isAba) {
                tituloForm = document.createElement('span');
                tituloForm.classList.add('govbr-forms-container-multiform-aba');
                tituloForm.innerText = formulario.Titulo || '';
                containerTabs.append(tituloForm);
            } else if (!this.userControl.OCULTA_TITULO_FORMULARIO) {
                tituloForm = document.createElement('div');
                tituloForm.classList.add('govbr-forms-container-multiform-conteudo-titulo');
                tituloForm.innerText = formulario.Titulo || '';
                containerConteudo.append(tituloForm);
            }
            const conteudoForm = document.createElement('div');
            conteudoForm.classList.add('govbr-forms-container-multiform-conteudo-item');
            containerConteudo.append(conteudoForm);
            if (isAba) {
                if (idx === 0) {
                    tituloForm.classList.add('govbr-forms-container-multiform-aba-ativa');
                    conteudoForm.classList.add('govbr-forms-container-multiform-conteudo-ativo');
                    atual = [tituloForm, conteudoForm];
                }
                tituloForm.addEventListener('click', () => {
                    if (tituloForm !== atual[0]) {
                        atual[0].classList.remove('govbr-forms-container-multiform-aba-ativa');
                        atual[1].classList.remove('govbr-forms-container-multiform-conteudo-ativo');
                        atual = [tituloForm, conteudoForm];
                        atual[0].classList.add('govbr-forms-container-multiform-aba-ativa');
                        atual[1].classList.add('govbr-forms-container-multiform-conteudo-ativo');
                    }
                });
            } else {
                conteudoForm.classList.add('govbr-forms-container-multiform-conteudo-ativo');
            }
            const UCForm = new GOVBRFormsUC(this.userControl, conteudoForm, true, formulario, this.localExibicao);
            this.ucForms.push(UCForm);
        });
        this.container.append(containerConteudo);
    }

    /**
     * Carrega os estilos necessários para o componente.
     */
    carregaEstilos() {
        GOVBRPlataformaJS.importarStyleSheet(this.userControl.URL_CDN, 'forms', this.userControl.VERSAO_JS_COMPONENTE, 'GOVBRFormsUC', this.host);
        GOVBRPlataformaJS.importarStyleSheet(this.userControl.URL_CDN, 'forms', this.userControl.VERSAO_JS_COMPONENTE, 'GOVBREstiloFormsComponentesUC', this.host);
        GOVBRPlataformaJS.importarStyleSheet(this.userControl.URL_CDN, 'forms', this.userControl.VERSAO_JS_COMPONENTE, 'GOVBREstiloFormsConfiguradorUC', this.host);
        GOVBRPlataformaJS.importarStyleSheet(this.userControl.URL_CDN, 'forms', this.userControl.VERSAO_JS_COMPONENTE, 'GOVBREstiloFormsPropriedadesUC', this.host);
        GOVBRPlataformaJS.importarStyleSheet(this.userControl.URL_CDN, 'ckeditor', '5-43.3.1', 'ckeditor5', this.host);
    }

    /**
     * Cria os eventos do container.
     */
    criaEventos() {
        this.host.addEventListener('keydown', (event) => {
            event.stopPropagation();
            if (event.key == 'Escape') {
                this.componenteSelecionado = null;
                this.recriaAreaFormulario();
            } else if (event.key == 'Delete' && document.activeElement === this.container) {
                const componenteObj = this.container.querySelector('[data-identificador="' + this.componenteSelecionado + '"]');
                // @ts-ignore
                const componente = componenteObj?.['__govbr_instance'];
                if (componente) {
                    this.removeComponente(componente);
                }
            }
        });
        this.container.addEventListener('pointerdown', (event) => {
            if (this.previsualizando) {
                return;
            }
            this.container.focus();
            if (this.suggest.suggestAberto?.contains?.(/** @type {Node} */ (event.target))) {
                return;
            }
            if (this.onDragStartComponenteEditor(event)) {
                event.preventDefault();
            }
        });
        this.container.addEventListener('pointermove', (event) => {
            if (this.previsualizando) {
                return;
            }
            if (this.onDragMoveComponenteEditor(event)) {
                event.preventDefault();
            }
        });
        this.container.addEventListener('pointerup', (event) => {
            if (this.previsualizando) {
                return;
            }
            if (this.onDropComponenteEditor(true, event)) {
                event.preventDefault();
            }
        });
    }

    /**
     * Re-renderiza o componente, recriando a área de formulários. Deve ser chamado quando um componente for alterado.
     */
    recriaAreaFormulario(inicio = false) {
        return new Promise((resolve) => {
            if (this.timeoutRepaint) {
                return resolve();
            }
            this.timeoutRepaint = setTimeout(() => {
                if (this.previsualizando && !inicio) {
                    this.salvaValores(false);
                }
                this.ignoraTrocaValores = true;
                this.container.innerHTML = '';
                if (this.propriedades['colunas']) {
                    this.container.style.setProperty('--numero-colunas', this.propriedades['colunas']);
                }
                /**
                 * @type {Record<string, GOVBRFormsUCComponente>}
                 */
                this.componentesId = {};
                /**
                 * @param {GOVBRFormsUCComponente} componente
                 */
                const montaComponenteId = (componente) => {
                    this.componentesId[componente.identificador] = componente;
                    if (componente.isContainer) {
                        /** @type {GOVBRFormsUCComponenteContainer} */ (componente).getFilhosLinear().forEach(montaComponenteId);
                    }
                };
                this.componentes.forEach(montaComponenteId);
                this.componentes.forEach((componente) => {
                    this.montaComponente(this.container, componente);
                });
                const adicionarNovo = this.criaBotaoAdicionarNovo();
                if (adicionarNovo) {
                    this.container.append(adicionarNovo);
                }
                this.formulario.Componentes = this.componentes.map((e) => e.toJSON());
                if (this.previsualizando) {
                    this.elPropriedades.innerHTML = '';
                } else {
                    this.recriaPropriedades();
                }
                this.timeoutRepaint = null;
                this.ignoraTrocaValores = false;
                resolve();
            }, 0);
        });
    }

    /**
     * Salva os valores dos componentes para o UserControl.
     * @param {boolean} submit
     * @param {boolean} subform
     */
    salvaValores(submit, subform = false) {
        // Está em modo de edição.
        if (!this.previsualizando) {
            this.formulario.Valores = [];
            this.userControl.VALORES_JSON = '[]';
            this.valores = {};
            return;
        }
        // Evita loop infinito devido ao "change" dos campos disparar o salvaValores.
        if (this.ignoraTrocaValores) {
            return;
        }
        // Se tem múltiplos formulários salva estes todos.
        if (this.ucForms.length) {
            this.userControl.PREENCHIMENTO_VALIDO = true;
            this.userControl.MENSAGEM_VALIDACAO = '';
            // Itera os formulários e salva os valores.
            /**
             * @type {import('./GOVBRFormsUserControl.mjs').GOVBRFormsFormulario[]}
             */
            const valores = [];
            this.ucForms.every((UCForm) => {
                // Dispara o salvaValores de cada filho para salvar estes e depois busca estes valores.
                UCForm.salvaValores(submit, true);
                valores.push({
                    Titulo: UCForm.formulario.Titulo,
                    Id: UCForm.formulario.Id,
                    Valores: UCForm.formulario.Valores.slice(),
                });
                return this.userControl.PREENCHIMENTO_VALIDO;
            });
            this.fnAtualizaValor?.(JSON.stringify(valores));
            GOVBR.logs.debug('Valores do Formulário: ', valores);
            // Se é confirmação, salva no JSON para enviar para o servidor.
            if (submit) {
                this.userControl.VALORES_JSON = JSON.stringify(valores);
            }
            return;
        }

        this.ignoraTrocaValores = true;
        const valoresOriginais = Object.fromEntries(Object.entries(this.valores).map(([identificador, valor]) => [identificador, valor]));
        this.valores = {};
        if (submit) {
            this.host.classList.add('govbr-forms-validado');
            if (this.timeoutValidado) {
                clearTimeout(this.timeoutValidado);
            }
            this.timeoutValidado = setTimeout(() => {
                this.host.classList.remove('govbr-forms-validado');
                this.timeoutValidado = null;
            }, 5000);
        }

        // Se tiver um pai, este vai atualizar o UC e portanto não precisa mecher nos valores que tem no UC.
        if (!subform) {
            this.userControl.PREENCHIMENTO_VALIDO = true;
            this.userControl.MENSAGEM_VALIDACAO = '';
        }
        /**
         * @param {GOVBRFormsUCComponenteInput|GOVBRFormsUCComponenteContainer} componente
         */
        const carregaValorComponente = (componente) => {
            if (componente.isContainer) {
                const container = /** @type {GOVBRFormsUCComponenteContainer} */ (componente);
                this.valores[container.identificador] = {
                    Tipo: 'grupo',
                    Id: container.identificador,
                    Titulo: container.dataTitulo || 'Container',
                };
                container.getFilhosLinear().forEach(carregaValorComponente);
            } else {
                const input = /** @type {GOVBRFormsUCComponenteInput} */ (componente);
                if (input.dataLeitura) {
                    if (Object.hasOwnProperty.apply(valoresOriginais, [input.identificador])) {
                        this.valores[input.identificador] = valoresOriginais[input.identificador];
                    }
                } else {
                    const valor = input.valor;
                    if (valor !== null && valor !== undefined) {
                        let adicionais = {};
                        if (input.dataFormato === 'lista') {
                            adicionais = {
                                multipla: /** @type {GOVBRFormsUCComponenteInputLista} */ (input).dataMultiSelecao,
                                lista: /** @type {GOVBRFormsUCComponenteInputLista} */ (input).dataLista,
                            };
                        }
                        this.valores[input.identificador] = {
                            Tipo: 'campo',
                            Id: input.identificador,
                            Valor: valor,
                            Titulo: input.dataTitulo || 'Campo',
                            Formato: input.dataFormato,
                            Adicionais: adicionais,
                        };
                    }
                    if (this.userControl.PREENCHIMENTO_VALIDO && !input.valida(!!submit)) {
                        this.userControl.PREENCHIMENTO_VALIDO = false;
                        if (!this.userControl.MENSAGEM_VALIDACAO) {
                            this.userControl.MENSAGEM_VALIDACAO = input.mensagemInvalida;
                        }
                    }
                }
            }
        };
        // Itera os componentes carregando os valores deste para o valores e depois atualiza o formulário com o valor correto.
        this.componentes.forEach(carregaValorComponente);
        this.formulario.Valores = Object.entries(this.valores).map(([_identificador, valor]) => valor);

        // Se tiver um pai, este vai atualizar o UC e portanto não precisa mecher nos valores que tem no UC.
        // Senão se for confirmação, salva os valores em JSON para serem acessados no GeneXus.
        if (!subform) {
            /**
             * @type {import('./GOVBRFormsUserControl.mjs').GOVBRFormsFormulario[]}
             */
            const valores = [
                {
                    Titulo: this.formulario.Titulo,
                    Id: this.formulario.Id,
                    Valores: this.formulario.Valores.slice(),
                },
            ];
            this.fnAtualizaValor?.(JSON.stringify(valores));
            GOVBR.logs.debug('Valores do Formulário: ', valores);
            if (submit) {
                this.userControl.VALORES_JSON = JSON.stringify(valores);
            }
        }
        this.ignoraTrocaValores = false;
    }

    /**
     * Cria o botão para adicionar um novo componente.
     * @param {string} titulo
     * @param {GOVBRFormsUCComponenteContainer} pai
     * @param {() => any} acao
     * @returns {HTMLElement}
     */
    criaBotaoAdicionarNovo(titulo = '', pai = null, acao = null) {
        if (this.previsualizando) {
            return null;
        }
        titulo = titulo || 'Adicionar um Componente';
        acao =
            acao ||
            (() => {
                adicionarNovo.classList.add('govbr-form-adicionar-novo-visivel');
                const suggest = this.abreSuggest(adicionarNovo, () => {
                    adicionarNovo.classList.remove('govbr-form-adicionar-novo-visivel');
                });
                const lista = document.createElement('ul');
                lista.classList.add('govbr-forms-lista-adicionar-novo');
                const componentes = GOVBRFormsUCComponenteFactory.montaListaComponentes();
                componentes.forEach((item) => {
                    const tipo = item.getAttribute('data-id');
                    if (tipo) {
                        const props = JSON.parse(item.getAttribute('data-props'));
                        item.addEventListener('click', () => {
                            this.fechaSuggest();
                            const componente = GOVBRFormsUCComponenteFactory.getInstanciaComponente(this, {
                                Id: this.getIdentificadorNovoComponente(tipo),
                                Tipo: tipo,
                                Propriedades: props || [],
                            });
                            if (pai) {
                                /** @type {GOVBRFormsUCComponente[]} */ (pai.filhos).push(componente);
                                componente.pai = pai;
                            } else {
                                this.componentes.push(componente);
                            }
                            this.selecionaComponente(componente);
                            this.recriaAreaFormulario();
                        });
                    }
                    lista.append(item);
                });
                suggest.append(lista);
            });

        const adicionarNovo = document.createElement('div');
        adicionarNovo.classList.add('govbr-form-adicionar-novo');
        adicionarNovo.title = titulo;
        adicionarNovo.addEventListener('click', acao);
        const iconeNovo = document.createElement('i');
        iconeNovo.className = 'fa-solid fa-plus';
        adicionarNovo.append(iconeNovo);
        return adicionarNovo;
    }

    /**
     * Retorna qual o identificar para um novo componente do tipo.
     * @param {string} tipo
     * @returns {string}
     */
    getIdentificadorNovoComponente(tipo) {
        let numId = 1;
        /**
         * @param {GOVBRFormsUCComponente} componente
         */
        const verificaComponente = (componente) => {
            if (componente.tipo === tipo) {
                const idComp = componente.identificador.split('-');
                const numIdComp = parseInt(idComp[idComp.length - 1]);
                if (numIdComp >= numId) {
                    numId = numIdComp + 1;
                }
            }
            if (componente.isContainer) {
                /** @type {GOVBRFormsUCComponenteContainer} */ (componente).getFilhosLinear().forEach(verificaComponente);
            }
        };
        this.componentes.forEach(verificaComponente);
        return tipo + '-' + numId;
    }

    /**
     * Limpa o componente selecionado.
     */
    limpaSelecao() {
        this.componenteSelecionado = null;
        this.recriaAreaFormulario();
    }

    /**
     * Seleciona um componente.
     * @param {GOVBRFormsUCComponente|string} alvo
     */
    selecionaComponente(alvo) {
        if (alvo instanceof GOVBRFormsUCComponente) {
            alvo = alvo.identificador;
        }
        this.componenteSelecionado = alvo;
        this.recriaAreaFormulario();
    }

    /**
     * Monta um componente no DOM.
     * @param {HTMLElement} pai
     * @param {GOVBRFormsUCComponente} componente
     */
    montaComponente(pai, componente) {
        if (this.previsualizando && componente.dataLocalExibicao && componente.dataLocalExibicao !== this.localExibicao) {
            return;
        }
        const elemento = this.previsualizando ? componente.montaVisualizacao() : componente.montaVisualizacaoEditor();
        pai.append(elemento);
        if (this.componenteSelecionado && componente.identificador === this.componenteSelecionado) {
            elemento.classList.add('govbr-forms-componente-selecionado');
        }
        if (Object.hasOwnProperty.apply(this.valores, [componente.identificador]) && this.valores[componente.identificador] !== undefined) {
            componente.valor = this.valores[componente.identificador].Valor;
        } else if (/** @type {GOVBRFormsUCComponenteInput} */ (componente).dataValorDefault) {
            componente.valor = /** @type {GOVBRFormsUCComponenteInput} */ (componente).dataValorDefault;
        }
    }

    /**
     * Recria a área de propriedades se necessário.
     */
    recriaPropriedades() {
        if (this.ultimoComponenteSelecionado === undefined || this.componenteSelecionado !== this.ultimoComponenteSelecionado) {
            this.elPropriedades.innerHTML = '';
            const titulo = document.createElement('h3');
            titulo.classList.add('govbr-forms-propriedades-titulo');
            this.elPropriedades.append(titulo);
            this.atualizaTituloPropriedade(titulo);
            const propriedades = document.createElement('div');
            propriedades.classList.add('govbr-forms-propriedades-itens');
            this.elPropriedades.append(propriedades);
            this.montaPropriedadesComponente(propriedades);
            const acoes = document.createElement('div');
            acoes.classList.add('govbr-forms-propriedades-acoes');
            this.elPropriedades.append(acoes);
            this.montaAcoesComponente(acoes);
            this.ultimoComponenteSelecionado = this.componenteSelecionado;
        } else {
            /**
             * @type {HTMLElement}
             */
            const titulo = this.elPropriedades.querySelector('.govbr-forms-propriedades-titulo');
            this.atualizaTituloPropriedade(titulo);
        }
    }

    /**
     * Atualiza o título da área de propriedades
     * @param {HTMLElement} titulo
     */
    atualizaTituloPropriedade(titulo) {
        const componente = this.container.querySelector('[data-identificador="' + this.componenteSelecionado + '"]');
        if (componente?.getAttribute('data-titulo')) {
            titulo.innerText = 'Propriedades - ' + componente.getAttribute('data-titulo');
        } else {
            titulo.innerText = 'Propriedades - Formulário';
        }
    }

    /**
     * Monta as propriedades para o componente.
     * @param {HTMLElement} alvo
     */
    montaPropriedadesComponente(alvo) {
        const componenteHTML = this.container.querySelector('[data-identificador="' + this.componenteSelecionado + '"]');
        // @ts-ignore
        const componente = componenteHTML?.['__govbr_instance'];
        if (componente) {
            this.montaCamposPropriedades(alvo, componente.montaCamposPropriedades(), componente);
        } else {
            this.montaCamposPropriedades(alvo, this.montaPropriedadesFormulario(), this.propriedades);
        }
    }

    /**
     * Monta as propridades para o Formulário.
     */
    montaPropriedadesFormulario() {
        const campoColunas = document.createElement('input');
        campoColunas.type = 'number';
        campoColunas.step = '1';
        campoColunas.id = 'colunas';
        campoColunas.min = '1';
        campoColunas.classList.add('govbr-forms-propriedades-campo-input');
        campoColunas.setAttribute(
            'data-hint',
            'Número de colunas disponíveis para o componente internos, se for maior que 1, os componentes internos do container serão dispostos conforme o fluxo normal, nesta situação, caso um dos componentes internos deva ocupar mais de uma coluna, utilize a propriedade Colunas Ocupadas no respectivo componente..'
        );
        return {
            'Colunas Disponíveis': campoColunas,
        };
    }

    /**
     * Monta os campos para a tela de propriedade.
     * @param {HTMLElement} alvo
     * @param {Record<string, HTMLElement|Record<string, any>>} campos
     * @param {any} propriedades
     */
    montaCamposPropriedades(alvo, campos, propriedades) {
        const todosCampos = Object.entries(campos);
        todosCampos
            .sort(([_, a], [__, b]) => {
                const idxA = parseInt(a?.getAttribute?.('data-order')) || 9999;
                const idxB = parseInt(b?.getAttribute?.('data-order')) || 9999;
                return idxA - idxB;
            })
            .forEach(([label, campo]) => {
                if (campo instanceof HTMLElement) {
                    this.montaCampoPropriedadeInput(alvo, propriedades, label, campo);
                } else {
                    const fieldset = document.createElement('fieldset');
                    const titulo = document.createElement('legend');
                    titulo.innerText = label;
                    fieldset.append(titulo);
                    alvo.append(fieldset);
                    this.montaCamposPropriedades(fieldset, campo, propriedades);
                }
            });
    }

    /**
     * Monta o campo HTML
     * @param {HTMLElement} alvo
     * @param {any} propriedades
     * @param {HTMLElement} campo
     * @param {string} label
     * @returns
     */
    montaCampoPropriedadeInput(alvo, propriedades, label, campo) {
        let input = /** @type {HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement} */ (campo);
        if (!(input instanceof HTMLInputElement || campo instanceof HTMLTextAreaElement || campo instanceof HTMLSelectElement)) {
            input = campo.querySelector('input[id],select[id],textarea[id]');
        }
        if (!input || !input.id) {
            return;
        }
        if (input instanceof HTMLInputElement || input instanceof HTMLTextAreaElement) {
            if (input instanceof HTMLInputElement && input.type === 'number') {
                input.type = 'text';
                input.setAttribute('data-type', 'number');
            }
            input.addEventListener('input', () => {
                if (!input.readOnly && input instanceof HTMLInputElement && input.getAttribute('data-type') == 'number') {
                    let val = input.value.replace(/\./g, '').replace(/,/g, '.');
                    /**
                     * @param {string} val
                     * @returns {string}
                     */
                    const fnParseItem = (val) => {
                        if (input.step && parseFloat(input.step) < 1) {
                            const valNum = parseFloat(val);
                            if (!isNaN(valNum)) {
                                const decimais = input.step.split('.')[1].length;
                                val = valNum.toFixed(decimais);
                            }
                            return val;
                        }
                        return '' + parseInt(val);
                    };
                    if (input.min && fnParseItem(val) < fnParseItem(input.min)) {
                        val = input.min;
                    } else if (input.max && fnParseItem(val) > fnParseItem(input.max)) {
                        val = input.max;
                    }
                    let start = input.selectionStart;
                    let end = input.selectionEnd;
                    val = (fnParseItem(val) || '') + '';
                    val = val.replace(/,/g, '').replace(/\./g, ',');
                    input.value = val;
                    input.selectionStart = start;
                    input.selectionEnd = end;
                }
            });
        }
        if (!input.getAttribute('data-ignora-auto-preenche')) {
            let prop = input.id;
            if (input instanceof HTMLInputElement && input.type == 'checkbox') {
                input.checked = !!propriedades[prop];
            } else {
                input.value = typeof propriedades[prop] === 'number' || typeof propriedades[prop] === 'string' ? '' + propriedades[prop] : '';
            }
            input.dispatchEvent(new Event('change'));
            const fnChange = () => {
                if (input.id === 'dataIdentificador' && /[^a-zA-Z0-9\-]/.test(input.value || '')) {
                    return;
                }
                if (((input instanceof HTMLInputElement || input instanceof HTMLTextAreaElement) && !input.readOnly) || (input instanceof HTMLSelectElement && !input.disabled)) {
                    if (input instanceof HTMLInputElement && input.type == 'checkbox') {
                        propriedades[prop] = input.checked ? '1' : '';
                    } else if (input.getAttribute('data-type') == 'number') {
                        propriedades[prop] = parseFloat((input.value || '').replace(/\./g, '').replace(/,/g, '.'));
                        if (isNaN(propriedades[prop])) {
                            propriedades[prop] = '';
                        }
                    } else {
                        propriedades[prop] = input.value;
                    }
                    this.recriaAreaFormulario();
                }
            };
            input.addEventListener('input', fnChange);
            input.addEventListener('change', fnChange);
        }
        const container = document.createElement('span');
        container.classList.add('govbr-forms-propriedades-campo-container');
        if (campo.classList.contains('govbr-forms-propriedades-campo-duas-colunas')) {
            container.classList.add('govbr-forms-propriedades-campo-duas-colunas');
        }
        if (campo.classList.contains('govbr-forms-propriedades-campo-tres-colunas')) {
            container.classList.add('govbr-forms-propriedades-campo-tres-colunas');
        }
        const labelHTML = document.createElement('label');
        labelHTML.classList.add('govbr-forms-propriedades-campo-label');
        labelHTML.setAttribute('for', input.id);
        labelHTML.innerText = label;
        container.append(labelHTML);
        const hint = input.getAttribute('data-hint');
        if (hint) {
            const containerCampo = document.createElement('div');
            containerCampo.classList.add('govbr-forms-propriedade-campo-container-campo');
            containerCampo.append(campo);
            const botaoHint = document.createElement('button');
            botaoHint.type = 'button';
            botaoHint.classList.add('govbr-forms-propriedades-campo-hint');
            botaoHint.classList.add('fa-solid');
            botaoHint.classList.add('fa-info-circle');
            botaoHint.addEventListener('click', (event) => {
                event.preventDefault();
                const suggest = this.abreSuggest(botaoHint);
                suggest.classList.add('govbr-plataforma-suggest-hint');
                suggest.innerText = hint;
            });
            containerCampo.append(botaoHint);
            container.append(containerCampo);
        } else {
            container.append(campo);
        }
        alvo.append(container);
    }

    /**
     * Monta as ações para o componente.
     * @param {HTMLElement} alvo
     */
    montaAcoesComponente(alvo) {
        const componenteHTML = this.container.querySelector('[data-identificador="' + this.componenteSelecionado + '"]');
        // @ts-ignore
        const componente = componenteHTML?.['__govbr_instance'];
        if (componente) {
            componente.montaAcoesAdicionais(alvo);

            const botaoRemover = document.createElement('button');
            botaoRemover.className = 'govbr-forms-propriedades-acoes-botao govbr-forms-propriedades-acoes-remover';
            botaoRemover.type = 'button';
            botaoRemover.innerText = 'Remover Elemento';
            botaoRemover.title = 'Remover Elemento';
            botaoRemover.addEventListener('click', () => {
                this.removeComponente(componente);
            });
            const iconeRemover = document.createElement('i');
            iconeRemover.className = 'fa-solid fa-trash-can';
            botaoRemover.prepend(iconeRemover);
            alvo.append(botaoRemover);

            const botaoLimpaSelecao = document.createElement('button');
            botaoLimpaSelecao.className = 'govbr-forms-propriedades-acoes-botao govbr-forms-propriedades-acoes-limpa-selecao';
            botaoLimpaSelecao.type = 'button';
            botaoLimpaSelecao.innerText = 'Deselecionar';
            botaoLimpaSelecao.title = 'Deselecionar';
            botaoLimpaSelecao.addEventListener('click', () => {
                this.limpaSelecao();
            });
            const icone = document.createElement('i');
            icone.classList.add('fa-regular');
            icone.classList.add('fa-square');
            botaoLimpaSelecao.prepend(icone);
            alvo.append(botaoLimpaSelecao);
        } else {
            const botaoValidacoes = document.createElement('button');
            botaoValidacoes.className = 'govbr-forms-propriedades-acoes-botao govbr-forms-propriedades-acoes-validacoes';
            botaoValidacoes.type = 'button';
            botaoValidacoes.innerText = 'Validações';
            botaoValidacoes.title = 'Validações';
            if (!this.formulario.Validacoes) {
                this.formulario.Validacoes = [];
            }
            botaoValidacoes.addEventListener('click', () => {
                GOVBRFormsUCConfiguradorEventos.abreJanelaConfiguracaoValidacoes(
                    this,
                    'Formulário',
                    this.formulario.Validacoes,
                    (resultado) => (this.formulario.Validacoes = resultado)
                );
            });
            const iconeValidacoes = document.createElement('i');
            iconeValidacoes.className = 'fa-solid fa-check-double';
            botaoValidacoes.prepend(iconeValidacoes);
            alvo.append(botaoValidacoes);

            const botaoEventos = document.createElement('button');
            botaoEventos.className = 'govbr-forms-propriedades-acoes-botao govbr-forms-propriedades-acoes-eventos';
            botaoEventos.type = 'button';
            botaoEventos.innerText = 'Eventos';
            botaoEventos.title = 'Eventos';
            if (!this.formulario.Eventos) {
                this.formulario.Eventos = [];
            }
            botaoEventos.addEventListener('click', () => {
                GOVBRFormsUCConfiguradorEventos.abreJanelaConfiguracaoEventos(
                    this,
                    'Formulário',
                    this.formulario.Eventos,
                    [
                        ['abrir', 'Ao Abrir', 'Evento disparado ao abrir a tela do formulário.'],
                    ],
                    (resultado) => (this.formulario.Eventos = resultado)
                );
            });
            const iconeEventos = document.createElement('i');
            iconeEventos.className = 'fa-solid fa-forward';
            botaoEventos.prepend(iconeEventos);
            alvo.append(botaoEventos);

            const preview = document.createElement('button');
            preview.className = 'govbr-forms-propriedades-acoes-botao govbr-forms-propriedades-acoes-preview';
            preview.type = 'button';
            preview.innerText = 'Pré-Visualizar';
            preview.title = 'Pré-Visualizar';
            preview.addEventListener('click', () => {
                const dialog = this.abreDialog(
                    'Pré-visualização',
                    () => {},
                    () => {
                        forms.salvaValores(true);
                        return this.userControl.PREENCHIMENTO_VALIDO;
                    }
                );
                const base = document.createElement('div');
                base.classList.add('govbr-forms-base');
                dialog.append(base);
                const forms = new GOVBRFormsUC(this.userControl, base, true, null, this.localExibicao);
            });
            const iconePreview = document.createElement('i');
            iconePreview.className = this.previsualizando ? 'fa-solid fa-arrow-left' : 'fa-solid fa-search';
            preview.prepend(iconePreview);
            alvo.append(preview);

            if (this.userControl.PERMITE_EXPORTAR) {
                const exportar = document.createElement('button');
                exportar.className = 'govbr-forms-propriedades-acoes-botao govbr-forms-propriedades-acoes-exportar';
                exportar.type = 'button';
                exportar.innerText = 'Exportar';
                exportar.title = 'Exportar';
                exportar.addEventListener('click', () => {
                    this.formulario.Valores = null;
                    const a = document.createElement('a');
                    a.download = 'Formulario.json';
                    a.href = 'data:json+utf8,' + JSON.stringify(this.formulario);
                    document.body.append(a);
                    a.click();
                    a.remove();
                });
                const iconeExportar = document.createElement('i');
                iconeExportar.className = 'fa-solid fa-download';
                exportar.prepend(iconeExportar);
                alvo.append(exportar);

                const input = document.createElement('input');
                input.style.display = 'none';
                input.type = 'file';
                input.addEventListener('change', async (event) => {
                    const tg = /** @type {HTMLInputElement} */ (event.target);
                    if (!tg.files.length) {
                        return;
                    }
                    try {
                        const text = await tg.files[0].text();
                        /**
                         * @type {import('./GOVBRFormsUserControl.mjs').GOVBRFormsFormulario}
                         */
                        const formularios = JSON.parse(text);
                        const componentes = formularios.Componentes.map((dadosComponente) => GOVBRFormsUCComponenteFactory.getInstanciaComponente(this, dadosComponente));
                        this.formulario = formularios;
                        this.componentes = componentes;
                        this.userControl.FORMULARIOS = [this.formulario];
                        this.recriaAreaFormulario();
                    } catch (error) {
                        throw new Error('Falha ao carregar o arquivo.');
                    }
                    tg.value = '';
                });
                alvo.append(input);

                const importar = document.createElement('button');
                importar.className = 'govbr-forms-propriedades-acoes-botao govbr-forms-propriedades-acoes-importar';
                importar.type = 'button';
                importar.innerText = 'Importar';
                importar.title = 'Importar';
                importar.addEventListener('click', () => {
                    input.showPicker();
                });
                const iconeImportar = document.createElement('i');
                iconeImportar.className = 'fa-solid fa-upload';
                importar.prepend(iconeImportar);
                alvo.append(importar);
            }
        }
    }

    /**
     * Remove um componente
     * @param {GOVBRFormsUCComponente} componente
     */
    removeComponente(componente) {
        let filhos = componente.pai?.filhos;
        if (!filhos) {
            filhos = this.componentes;
        }
        const idx = filhos.findIndex((e) => e === componente);
        filhos.splice(idx, 1);
        if (componente.pai?.tipo == 'tabs-container') {
            /** @type {GOVBRFormsUCComponenteTabsContainer} */ (componente.pai).tabAtiva = /** @type {GOVBRFormsUCComponenteTabsContainer} */ (componente.pai).tabAtiva - 1;
        }
        componente.pai = null;
        this.componenteSelecionado = null;
        this.recriaAreaFormulario();
    }

    /**
     * Tratamentos para iniciar arrastar um componente.
     * @param {PointerEvent} event
     */
    onDragStartComponenteEditor(event) {
        if (this.componenteArrastando) {
            this.onDropComponenteEditor(false);
        }
        let tg = /** @type {HTMLElement} */ (event.target);
        if (tg.classList.contains('govbr-forms-componente-tabs-titulo')) {
            this.componenteSelecionado = tg.parentElement.getAttribute('data-identificador');
        } else if (tg.classList.contains('govbr-forms-componente')) {
            this.container.classList.add('govbr-forms-arrastando-componente');
            this.componenteArrastando = tg;
            this.componenteArrastando.classList.add('govbr-forms-componente-drag');
            this.posicaoInicialDrag = [
                event.pageX + this.container.scrollLeft,
                event.pageY + this.container.scrollTop,
            ];
            return true;
        } else if (this.componenteSelecionado && !tg.classList.contains('govbr-form-adicionar-novo')) {
            this.componenteSelecionado = null;
            this.recriaAreaFormulario();
        }
        return false;
    }

    /**
     * Tratamento para mover o componento.
     * @param {PointerEvent} event
     */
    onDragMoveComponenteEditor(event) {
        if (this.componenteArrastando) {
            if (event.pressure === 0) {
                this.onDropComponenteEditor(false);
                return true;
            }
            const posicaoAtual = [
                event.pageX + this.container.scrollLeft,
                event.pageY + this.container.scrollTop,
            ];
            const delta = [
                posicaoAtual[0] - this.posicaoInicialDrag[0],
                posicaoAtual[1] - this.posicaoInicialDrag[1],
            ];
            this.componenteArrastando.style.setProperty('--translate-x', delta[0] + 'px');
            this.componenteArrastando.style.setProperty('--translate-y', delta[1] + 'px');

            if (this.containerAlvo !== event.target) {
                if (this.containerAlvo) {
                    this.containerAlvo.classList.remove('govbr-forms-container-alvo');
                }
                this.containerAlvo = /** @type {HTMLElement} */ (event.target);
                if (
                    (this.containerAlvo.classList.contains('govbr-forms-componente-container') &&
                        !this.containerAlvo.classList.contains('govbr-forms-componente-tabs-container')) ||
                    this.containerAlvo.classList.contains('govbr-forms-base-container') ||
                    this.containerAlvo.classList.contains('govbr-forms-componente-tabs-titulo')
                ) {
                    this.containerAlvo.classList.add('govbr-forms-container-alvo');
                }
            }
            return true;
        } else {
            let componenteAlvo = /** @type {HTMLElement} */ (event.target);
            while (componenteAlvo && !componenteAlvo.classList.contains('govbr-forms-componente-container') && !componenteAlvo.classList.contains('govbr-forms-base-container')) {
                componenteAlvo = componenteAlvo.parentElement;
            }
            if (this.componenteHover != componenteAlvo) {
                if (this.componenteHover) {
                    this.componenteHover.classList.remove('govbr-componente-alvo');
                }
                this.componenteHover = componenteAlvo;
                if (this.componenteHover) {
                    this.componenteHover.classList.add('govbr-componente-alvo');
                }
            }
        }
        return false;
    }

    /**
     * Evento ao soltar o componente no editor.
     * @param {boolean} aplica
     * @param {PointerEvent} event
     */
    onDropComponenteEditor(aplica, event = null) {
        if (this.componenteArrastando) {
            this.componenteArrastando.classList.remove('govbr-forms-componente-drag');
            this.componenteSelecionado = this.componenteArrastando?.getAttribute?.('data-identificador');
            if (this.containerAlvo) {
                if (aplica && this.containerAlvo.classList.contains('govbr-forms-container-alvo')) {
                    this.moveComponenteDrop(event);
                }
                this.containerAlvo.classList.remove('govbr-forms-container-alvo');
                this.containerAlvo = null;
            }
            this.componenteArrastando = null;
            this.posicaoInicialDrag = null;
            this.container.classList.remove('govbr-forms-arrastando-componente');
            this.recriaAreaFormulario();
            return true;
        }
        return false;
    }

    /**
     * Move o componente após este ser arrastado e solto.
     * @param {PointerEvent} event
     */
    moveComponenteDrop(event) {
        /**
         * @type {GOVBRFormsUCComponente}
         */
        //@ts-ignore
        const componenteArrastando = this.componenteArrastando.__govbr_instance;
        /**
         * @type {GOVBRFormsUCComponenteContainer}
         */
        //@ts-ignore
        const componenteAlvo = this.containerAlvo.__govbr_instance;
        let filhos = componenteArrastando.pai?.filhos;
        if (!filhos) {
            filhos = this.componentes;
        }
        const idx = filhos.findIndex((e) => e === componenteArrastando);
        filhos.splice(idx, 1);

        let idxAlvo = 0;
        Array.from(this.containerAlvo.children).every((filho) => {
            //@ts-ignore
            const instance = filho.__govbr_instance;
            if (filho === this.componenteArrastando || !instance) {
                return true;
            }
            const rects = filho.getBoundingClientRect();
            if (event.clientY > rects.y) {
                if (event.clientY < rects.y + rects.height && event.clientX < rects.x) {
                    return false;
                }
                idxAlvo++;
                return true;
            }
            return false;
        });

        if (componenteAlvo) {
            componenteAlvo.filhos.splice(idxAlvo, 0, componenteArrastando);
            componenteArrastando.pai = componenteAlvo;
        } else {
            this.componentes.splice(idxAlvo, 0, componenteArrastando);
            componenteArrastando.pai = null;
        }
    }

    /**
     * Abre o Suggest no Alvo
     * @param {HTMLElement} alvo
     * @param {() => any} fnClose
     */
    abreSuggest(alvo, fnClose = () => {}) {
        return this.suggest.abreSuggest(alvo, fnClose);
    }

    /**
     * Posiciona o Suggest de forma relativa ao alvo;
     */
    reposicionaSuggest() {
        return this.suggest.reposicionaSuggest();
    }

    /**
     * Fechar o Suggest no Alvo
     */
    fechaSuggest() {
        return this.suggest.fechaSuggest();
    }

    /**
     * Abre um dialog para o Usuário confirmar/cancelar
     * @param {string} titulo
     * @param {() => void} onConfirma
     * @param {() => void} onValida
     */
    abreDialog(titulo, onConfirma = null, onValida = null) {
        const dialog = document.createElement('dialog');
        dialog.classList.add('govbr-forms-dialog');
        dialog.addEventListener('close', () => {
            dialog.remove();
        });
        const dialogCt = document.createElement('div');
        dialogCt.classList.add('govbr-forms-dialog-base');
        dialog.append(dialogCt);
        const conteudo = document.createElement('article');
        conteudo.classList.add('govbr-form-dialog-conteudo');
        const cabecalho = document.createElement('h2');
        cabecalho.classList.add('govbr-form-dialog-cabecalho');
        cabecalho.innerText = titulo;
        conteudo.append(cabecalho);
        dialogCt.append(conteudo);

        const rodape = document.createElement('nav');
        rodape.classList.add('govbr-form-dialog-rodape');
        if (onConfirma) {
            const botaoConfirma = document.createElement('button');
            botaoConfirma.type = 'button';
            botaoConfirma.classList.add('govbr-form-dialog-botao');
            botaoConfirma.classList.add('govbr-form-dialog-botao-confirma');
            botaoConfirma.innerText = 'Confirmar';
            botaoConfirma.addEventListener('click', () => {
                if (!onValida || onValida()) {
                    onConfirma();
                    dialog.close();
                }
            });
            rodape.append(botaoConfirma);
        }
        const botaoCancela = document.createElement('button');
        botaoCancela.type = 'button';
        botaoCancela.classList.add('govbr-form-dialog-botao');
        botaoCancela.classList.add('govbr-form-dialog-botao-fechar');
        botaoCancela.innerText = 'Fechar';
        botaoCancela.addEventListener('click', () => {
            dialog.close();
        });
        rodape.append(botaoCancela);
        dialogCt.append(rodape);

        document.body.append(dialog);
        dialog.show();
        return conteudo;
    }

    /**
     * Dispara os eventos e realiza suas operações.
     * @param {string} tipo
     * @param {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCEvento[]} eventos
     * @param {string[]} contexto
     */
    disparaEventos(tipo, eventos, contexto = []) {
        if (this.ignoraTrocaValores) {
            return;
        }
        let executou = false;
        eventos.forEach((evento) => {
            if (evento.Tipo == tipo) {
                evento.Condicionantes.some((condicionante) => {
                    if (this.validaCondicional(condicionante.Condicional, contexto)) {
                        this.executaEvento(condicionante.Resultado, contexto);
                        executou = true;
                        return true;
                    }
                });
            }
        });
        if (executou) {
            this.recriaAreaFormulario();
        }
    }

    /**
     * Executa a operação do evento.
     * @param {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCEventoRetorno} resultados
     * @param {string[]} contexto
     */
    executaEvento(resultados, contexto) {
        resultados.forEach((operacaoResultado) => {
            /**
             * @type {GOVBRFormsUCComponente|GOVBRFormsUCComponenteInput}
             */
            let componente;
            if (operacaoResultado.Alvo.Tipo !== 'global') {
                componente = this.buscaComponenteContextoIdentificador(contexto, operacaoResultado.Alvo.Id);
                if (!componente) {
                    return;
                }
            }

            const valor = this.avaliaValor(operacaoResultado.ValorOperacao);
            switch (operacaoResultado.Operacao) {
                case 'mensagem':
                    window.dispatchEvent(
                        new CustomEvent('__govbr_msg', {
                            detail: {
                                tipo: '',
                                mensagem: valor,
                            },
                        })
                    );
                    break;
                case 'visivel':
                    componente.dataVisivel = valor === '1';
                    break;
                case 'ativa':
                    /** @type {GOVBRFormsUCComponenteTabsContainer} */ (componente.pai).tabAtiva = componente.pai.getFilhosLinear().findIndex((filho) => filho === componente);
                    break;
                case 'obrigatorio':
                    /** @type {GOVBRFormsUCComponenteInput} */ (componente).dataObrigatorio = valor === '1';
                    break;
                case 'leitura':
                    /** @type {GOVBRFormsUCComponenteInput} */ (componente).dataLeitura = valor === '1';
                    break;
            }
        });
    }

    /**
     * Valida se o retorno de uma condicional é válida.
     * @param {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCCondicionanteCondicionalAgregada|import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCCondicionanteCondicional} condicional
     * @param {string[]} contexto
     * @returns {boolean}
     */
    validaCondicional(condicional, contexto) {
        if (condicional?.Agregada) {
            return this.validaCondicionalAgregada(condicional, contexto);
        }
        if (!condicional?.Operador) {
            return true;
        }
        const valoresCondicaoOriginal = /** @type {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCCondicionanteCondicional} */ (condicional).ValoresCondicao;
        const valores = /** @type {[string, string, string]} */ (valoresCondicaoOriginal.map((valor) => this.avaliaValor(valor, contexto)));
        return this.aplicaValidacaoOperador(/** @type {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCCondicionanteCondicional} */ (condicional).Operador, ...valores);
    }

    /**
     * Avalia o valor para um alvo.
     * @param {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCAlvo} valor
     * @param {string[]} contexto
     * @returns {string}
     */
    avaliaValor(valor, contexto = []) {
        if (valor.Tipo !== 'componente') {
            return valor.Valor;
        }
        const componente = this.buscaComponenteContextoIdentificador(contexto, valor.Id);
        if (!componente || !(componente instanceof GOVBRFormsUCComponenteInput)) {
            GOVBR.logs.error('Componente não encontrado: ' + valor.Id);
            return '';
        }
        if (componente.dataFormato === 'data-hora') {
            return '' + (new Date(componente.valor).getTime() || 0);
        }
        return componente.valor || '';
    }

    /**
     * Aplica a validação do operador sobre o elemento.
     * @param {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCCondicionanteCondicionalOperadores} operador
     * @param {string} valorAlvo
     * @param {string} valor1
     * @param {string} valor2
     */
    aplicaValidacaoOperador(operador, valorAlvo, valor1, valor2) {
        /**
         * Normaliza o conteúdo textual, para realizar comparações ignorando acentos
         * @param {string} valor
         * @returns {string}
         */
        const normaliza = (valor) => {
            return valor
                .toLowerCase()
                .normalize('NFD')
                .replace(/[\u0300-\u036f]/g, '');
        };
        switch (operador) {
            case 'igual':
                return valorAlvo === valor1;
            case 'diferente':
                return valorAlvo !== valor1;
            case 'maior-que':
                return parseInt(valorAlvo) > parseInt(valor1);
            case 'menor-que':
                return parseInt(valorAlvo) < parseInt(valor1);
            case 'maior-igual':
                return parseInt(valorAlvo) >= parseInt(valor1);
            case 'menor-igual':
                return parseInt(valorAlvo) <= parseInt(valor1);
            case 'entre':
                return parseInt(valor1) < parseInt(valorAlvo) && parseInt(valorAlvo) < parseInt(valor2);
            case 'contem':
                return normaliza(valorAlvo).includes(normaliza(valor1));
            case 'nao-contem':
                return !normaliza(valorAlvo).includes(normaliza(valor1));
            case 'inicia-com':
                return normaliza(valorAlvo).startsWith(normaliza(valor1));
            case 'termina-com':
                return normaliza(valorAlvo).endsWith(normaliza(valor1));
            case 'expressao-regular':
                return new RegExp(valor1).test(valorAlvo);
            case 'nulo':
                return valorAlvo === '';
            case 'nao-nulo':
                return valorAlvo !== '';
            default:
                break;
        }
        GOVBR.logs.error('Operador desconhecido: ' + operador);
        return false;
    }

    /**
     * Valida se o retorno de uma condicional é válida.
     * @param {import('./GOVBRFormsUCConfiguradorEventos.mjs').GOVBRFormsUCCondicionanteCondicionalAgregada} condicional
     * @param {string[]} contexto
     * @returns {boolean}
     */
    validaCondicionalAgregada(condicional, contexto) {
        const retornoCondicional1 = this.validaCondicional(condicional.Condicionais[0], contexto);
        // Apenas uma condicional, retorna esta.
        if (!condicional.Operador) {
            return retornoCondicional1;
        }
        // Operador "e"
        if (condicional.Operador === 'e') {
            // Se a primeira é false, não precisa validar a segunda.
            if (!retornoCondicional1) {
                return false;
            }
        } else {
            // Operador "ou"
            // Se a primeira é true, não precisa validar a segunda.
            if (retornoCondicional1) {
                return true;
            }
        }
        // Senão o retorno é o resultado da segunda.
        return this.validaCondicional(condicional.Condicionais[1], contexto);
    }

    /**
     * Busca o componente dentro do contexto para o identificador fornecido.
     * @param {string[]} contexto
     * @param {string} identificador
     * @returns {GOVBRFormsUCComponente|GOVBRFormsUCComponenteInput}
     */
    buscaComponenteContextoIdentificador(contexto, identificador) {
        const contextoFormatado = contexto?.length ? (contexto || []).join('_') + '_' : '';
        let componente = this.componentesId[contextoFormatado + identificador];
        if (!componente) {
            if (contexto.length) {
                componente = this.buscaComponenteContextoIdentificador(contexto.slice(0, contexto.length - 1), identificador);
            } else {
                GOVBR.logs.error('Componente não encontrado: ' + identificador);
            }
        }
        return componente;
    }

    /**
     * Define uma função chamada quando o valor é alterado/validado para que atualize o valor.
     * @param {(valor:string) => void} fn
     */
    setFnAtualizaValor(fn) {
        this.fnAtualizaValor = fn;
    }

    /**
     * Retorna as propriedades do formulário.
     * @returns {Record<string, string>}
     */
    get propriedades() {
        if (!this.formulario) {
            return {};
        }
        if (!this.formulario.Propriedades) {
            this.formulario.Propriedades = [];
        }
        if (!this._propriedades) {
            this._propriedades = this.formulario.Propriedades.reduce(
                /** @param {Record<string, string>} accum */ (accum, { Nome: nome, Valor: valor }) => {
                    accum[nome] = valor;
                    return accum;
                },
                {}
            );
        }
        return this._propriedades;
    }
}

// Caso esteja no browser, disponibiliza a classe no objeto de window.
if (typeof window !== 'undefined') {
    // @ts-ignore
    window['GOVBRFormsUC'] = GOVBRFormsUC;
}
export { GOVBRFormsUC };
