/**
 * @enum {string}
 */
const TIPOS_CAMPO = {
    TEXTO: 'texto',
    EDITOR: 'editor',
    NUMERO: 'numero',
    DATA_HORA: 'data-hora',
    LISTA: 'lista',
    BOOLEANO: 'booleano',
};

/**
 * @typedef {Object} Campo
 * @prop {'grupo'|'campo'|'grupo-idx'} Tipo
 * @prop {TIPOS_CAMPO=} Formato
 * @prop {string=} Id
 * @prop {string=} Titulo
 * @prop {string=} Valor
 * @prop {{[key:string]: any}=} Adicionais
 * @prop {number=} ProfundidadeGrupo
 * @prop {number=} IdxGrupo
 */

/**
 * @typedef {Object} Formulario
 * @prop {string} Id
 * @prop {string} Titulo
 * @prop {Campo[]} Valores
 */

/**
 * Módulo para exibição de campos de dados em formato de Leitura apenas.
 */
class GOVBRCamposLeitura {
    /**
     * @type {HTMLElement}
     */
    _alvo;
    /**
     * @type {Formulario[]}
     */
    _forms;

    /**
     * @type {boolean}
     */
    _ocultaVazios;
    /**
     * @type {boolean}
     */
    _ocultaTitulos;

    /**
     * Cria um novo controle de campos para exibição em formato de leitura.
     * @param {string|Element} alvo - Elemento HTML ou seletor para o elemento HTML onde serão criados os campos.
     * @param {Formulario[]} forms - Campos a serem criados.
     * @param {boolean} ocultaVazios - Se for true, campos vazios serão ocultos.
     * @param {boolean} ocultaTitulos - Se for true, títulos dos formulários serão ocultos.
     */
    constructor(alvo, forms, ocultaVazios, ocultaTitulos) {
        // @ts-ignore
        if (window['gx']) {
            try {
                this.iniciaCampos(alvo, forms, ocultaVazios, ocultaTitulos);
            } catch (ex) {
                // Dispara a exceção somente em outro loop ou irá crashar os js do genexus. Disparar a exceção em outro loop garante que esta apareça sem quebrar o genexus.
                setTimeout(() => {
                    throw ex;
                });
            }
        } else {
            this.iniciaCampos(alvo, forms, ocultaVazios, ocultaTitulos);
        }
    }

    /**
     * @param {string|Element} alvo
     * @param {Formulario[]} forms
     * @param {boolean} ocultaVazios
     * @param {boolean} ocultaTitulos
     */
    iniciaCampos(alvo, forms, ocultaVazios, ocultaTitulos) {
        const alvoOrig = alvo;
        if (typeof alvo === 'string') {
            alvo = document.querySelector(alvo);
        }
        if (!(alvo instanceof HTMLElement)) {
            GOVBR.logs.error('Não foi possível utilizar o alvo para exibir os campos. Alvo: ', alvoOrig);
            throw new Error('Não foi possível encontrar o alvo para exibir os campos.');
        }
        GOVBRPlataformaJS.importarStyleSheet('', 'campos-leitura', '', 'GOVBRCamposLeitura', alvo);
        forms.forEach(({ Valores: valores }) => {
            valores.forEach((campo, idx) => {
                if (!Object.prototype.hasOwnProperty.apply(campo, ['Titulo']) || (campo.Tipo !== 'grupo' && !Object.prototype.hasOwnProperty.apply(campo, ['Valor']))) {
                    throw new Error('Campo inválido recebido para o índice ' + idx + ': ' + JSON.stringify(campo));
                }
            });
        });
        this._alvo = alvo;
        this._forms = forms;
        this._ocultaVazios = ocultaVazios;
        this._ocultaTitulos = ocultaTitulos;
        this.montaCamposLeitura();
    }

    /**
     * Monta os campos de leitura.
     */
    montaCamposLeitura() {
        this._alvo.innerHTML = '';
        this._alvo.classList.add('govbr-campos-leitura');
        this._forms.forEach(({ Titulo: titulo, Valores: valores }) => {
            if (!this._ocultaTitulos) {
                const tituloForm = document.createElement('div');
                tituloForm.classList.add('govbr-campos-leitura-titulo');
                tituloForm.innerText = titulo;
                this._alvo.append(tituloForm);
            }
            valores = valores.slice();
            while (valores.length) {
                const campo = valores.shift();
                this.trataCampo(campo, valores);
            }
        });
    }

    /**
     * Trata a exibição do campo.
     * @param {Campo} campo
     * @param {Campo[]} valores
     */
    trataCampo(campo, valores) {
        if (campo.Valor === '' && this._ocultaVazios) {
            return;
        }
        switch (campo.Tipo) {
            case 'grupo': {
                /**
                 * @type {[number, number, number][]}
                 */
                const idxs = [];
                valores.forEach((novoCampo, idxArray) => {
                    if (novoCampo.Tipo === 'campo') {
                        const idx = parseInt(novoCampo.Id.match(new RegExp(campo.Id + '-(\\d+)'))?.[1]);
                        if (!isNaN(idx)) {
                            novoCampo.ProfundidadeGrupo = (novoCampo.ProfundidadeGrupo || 0) + 1;
                            novoCampo.IdxGrupo = idx + 1;
                            if (!idxs[idx]) {
                                idxs.push([idx, idxArray, novoCampo.ProfundidadeGrupo]);
                            }
                        }
                    }
                });
                idxs.forEach(([idx, idxValores, profundidadeGrupo], idxArray) => {
                    valores.splice(idxValores + idxArray, 0, {
                        Tipo: 'grupo-idx',
                        Titulo: campo.Titulo,
                        IdxGrupo: idx + 1,
                        ProfundidadeGrupo: profundidadeGrupo - 1,
                    });
                });
                break;
            }
            case 'grupo-idx': {
                const baseCampo = document.createElement('div');
                baseCampo.classList.add('govbr-campos-leitura-container');
                baseCampo.classList.add('govbr-campos-leitura-grupo');
                const id = new Date().getTime() + '_' + Math.floor(Math.random() * 1000000);
                baseCampo.id = id;
                if (campo.ProfundidadeGrupo) {
                    baseCampo.style.setProperty('--profundidade', '' + campo.ProfundidadeGrupo);
                }
                const label = document.createElement('span');
                label.classList.add('govbr-campos-leitura-label');
                label.innerText = campo.Titulo + ' - Item ' + (campo.IdxGrupo || 1);
                baseCampo.append(label);
                this._alvo.append(baseCampo);
                break;
            }
            default:
            case 'campo': {
                const baseCampo = document.createElement('div');
                baseCampo.classList.add('govbr-campos-leitura-container');
                baseCampo.classList.add('govbr-campos-leitura-' + campo.Formato);
                const id = new Date().getTime() + '_' + Math.floor(Math.random() * 1000000);
                baseCampo.id = id;
                if (campo.ProfundidadeGrupo) {
                    baseCampo.style.setProperty('--profundidade', '' + campo.ProfundidadeGrupo);
                }

                const label = document.createElement('label');
                label.setAttribute('for', 'campo-leitura-' + id);
                label.classList.add('govbr-campos-leitura-label');
                label.innerText = campo.Titulo;
                baseCampo.append(label);

                const valor = document.createElement('span');
                valor.id = 'campo-leitura-' + id;
                valor.setAttribute('tabindex', '0');
                valor.classList.add('govbr-campos-leitura-valor');
                this.carregaValorCampo(campo, valor);
                baseCampo.append(valor);

                this._alvo.append(baseCampo);
                break;
            }
        }
    }

    /**
     * Monta o valor para o campo.
     * @param {Campo} campo
     * @param {HTMLElement} elValor
     */
    carregaValorCampo(campo, elValor) {
        if (campo.Valor === '') {
            elValor.innerText = '(Nada informado)';
            elValor.classList.add('govbr-campo-leitura-valor-vazio');
            return;
        }
        switch (campo.Formato) {
            case TIPOS_CAMPO.EDITOR:
                elValor.innerHTML = campo.Valor;
                break;
            case TIPOS_CAMPO.NUMERO:
                elValor.innerText = (parseFloat(campo.Valor) || 0).toString().replace(/,/g, '').replace(/\./g, ',');
                break;
            case TIPOS_CAMPO.DATA_HORA:
                const isDataApenas = campo.Valor.length === 10;
                const data = isDataApenas ? new Date(campo.Valor + ' 00:00:00') : new Date(campo.Valor);
                if (!data?.getTime?.()) {
                    elValor.innerText = '';
                } else {
                    const horaTexto = isDataApenas
                        ? ''
                        : data.getHours().toString().padStart(2, '0') + ':' + data.getMinutes().toString().padStart(2, '0') + ':' + data.getSeconds().toString().padStart(2, '0');
                    const dataTexto =
                        data.getDate().toString().padStart(2, '0') + '/' + (data.getMonth() + 1).toString().padStart(2, '0') + '/' + data.getFullYear().toString().padStart(4, '0');
                    elValor.innerText = dataTexto + ' ' + horaTexto;
                }
                break;
            case TIPOS_CAMPO.BOOLEANO:
                elValor.innerText = campo.Valor == '1' ? 'Sim' : 'Não';
                break;
            case TIPOS_CAMPO.LISTA:
                if (!campo.Valor) {
                    elValor.innerText = '';
                } else {
                    /**
                     * @type {string[]|string}
                     */
                    let valores = campo.Valor;
                    if (campo.Adicionais.multipla) {
                        try {
                            valores = JSON.parse(campo.Valor);
                        } catch (ex) {
                            GOVBR.logs.error(ex);
                        }
                    }
                    if (!Array.isArray(valores)) {
                        valores = [valores];
                    }
                    /**
                     * @type {{Nome: string, Valor: string}[]}
                     */
                    const lista = JSON.parse(campo.Adicionais.Lista);
                    elValor.innerText = valores
                        .map((valor) => {
                            const itemLista = lista.find((item) => item.Valor === valor);
                            if (itemLista) {
                                return itemLista.Nome;
                            }
                            return '';
                        })
                        .filter((e) => e)
                        .join(', ');
                }
                break;
            default:
                elValor.innerText = campo.Valor;
                break;
        }
    }
}

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