import { formatoNumerico, parametros, sumar } from '../../utilidades.js';

export default class Tabla {

  #tabla;

  #metodo = {
    inicializar: { ejecucion: false }
  };

  constructor({
    tabla = tablaRegistros,
    columna = [],
    datos = [],
    configuracion = {
      campo: {
        busqueda: { peticion: false }
      }
    }
  }) {

    this.tabla = tabla;
    this.columna = columna;
    this.datos = datos;
    this.configuracion = configuracion;

    this.#tabla = {
      columna: {
        sumar: [],
        agrupar: {
          fila: { pie: $('<tr/>') }
        }
      },
      configuracion: {
        campo: {
          busqueda: { peticion: false }
        },
        keys: {
          className: 'table-active',
          tabIndex: -1
        },
        stateSave: false,
        info: false,
        columns: [],
        ordering: true,
        order: [],
        pageLength: 500,
        scrollY: '45vh',
        scrollCollapse: true,
        deferRender: true,
        language: {
          sProcessing: 'Procesando...',
          sLengthMenu: 'Mostrar _MENU_ registros',
          sZeroRecords: 'No se encontraron resultados',
          sEmptyTable: 'Ningún dato disponible en esta tabla',
          sInfo: 'Mostrando registros del _START_ al _END_ de un total de _TOTAL_ registros',
          sInfoEmpty: 'Mostrando registros del 0 al 0 de un total de 0 registros',
          sInfoFiltered: '(filtrado de un total de _MAX_ registros)',
          sSearch: '',
          sInfoThousands: ',',
          sLoadingRecords: 'Cargando...',
          oPaginate: {
            sFirst: 'Primero',
            sLast: 'Último',
            sPrevious: '<i class="fa-solid fa-arrow-left"></i>',
            sNext: '<i class="fa-solid fa-arrow-right"></i>'
          },
          oAria: {
            sSortAscending: ': Activar para ordenar la columna de manera ascendente',
            sSortDescending: ': Activar para ordenar la columna de manera descendente'
          },
          buttons: { copy: 'Copiar', colvis: 'Visibilidad' }
        },
        dom: `ft<'row justify-content-between'<'col-auto'B><'col-auto'p>>`,
        buttons: [{
          extend: 'colvis',
          className: 'btn btn-primary buttons-colvis',
          text: 'Columnas <i class="fa-solid fa-eye"></i>',
        }, {
          extend: 'print',
          className: 'btn buttons-print',
          text: 'Imprimir <i class="fa-solid fa-print px-1"></i>',
          footer: true,
          exportOptions: {
            page: 'current',
            columns: ':visible'
          },
          action: async function(evento, instancia, boton, configuracion) {

            let
              ventana = {
                elemento: window.open('', '_blank',
                  `width = 992, height = 600, left = ${(window.screen.width - 992) / 2}, top = ${(window.screen.height - 600) / 2}`
                ),
                tabla: {
                  elemento: $(`
                    <table class="${instancia.table().node().className}">
                      <thead>
                        <tr>
                          <td colspan="100%">
                            <h1 class="fs-6 text-center text-white">${document.title}</h1>
                          </td>
                        </tr>
                        <tr></tr>
                      </thead>
                      <tbody></tbody>
                      <tfoot><tr></tr></tfoot>
                    </table>
                  `),
                  cabecera: {
                    fila: {
                      elemento: $(`
                        <tr>
                          <td class="px-0 pt-0 bg-transparent" colspan="100%">
                            <div class="d-flex justify-content-between align-items-center"></div>
                          </td>
                        </tr>
                      `)
                    },
                    columna: { contenido: '' },
                    parametros: []
                  },
                  filas: { cuerpo: '' },
                  columnas: { cabeza: '', pie: '' }
                }
              }

            if (!ventana)
              return alert('¡HA OCURRIDO UN ERROR AL INTENTAR IMPRIMIR!')
            ;

            ventana.elemento.document.head.innerHTML = document.head.outerHTML;
            ventana.elemento.document.body.classList.add('dt-print-view');

            instancia.contenedor = instancia.table();

            instancia.formulario = $(instancia.contenedor.container().closest('form'));

            instancia.contenedor.header().querySelectorAll('tr:last-child th').forEach(elemento =>
              ventana.tabla.columnas.cabeza += `<th class="${elemento.className}">${elemento.innerHTML}</th>`
            );
            ventana.tabla.elemento.find('thead tr:last').append(ventana.tabla.columnas.cabeza);

            instancia.contenedor.body().querySelectorAll('tr').forEach(elemento =>
              ventana.tabla.filas.cuerpo += elemento.outerHTML
            );
            ventana.tabla.elemento.find('tbody').append(ventana.tabla.filas.cuerpo).find('.table-active').removeClass('table-active');

            instancia.contenedor.footer().querySelectorAll('tr th').forEach(elemento =>
              ventana.tabla.columnas.pie += `<th class="${elemento.className}">${elemento.innerHTML}</th>`
            );
            ventana.tabla.elemento.find('tfoot tr').append(ventana.tabla.columnas.pie);

            $(ventana.elemento.document.body).append(ventana.tabla.elemento);

            instancia.formulario.length &&
            parametros(instancia.formulario[0]).forEach((valor, campo) => {

              if (valor) {

                campo = { nombre: campo, elemento: instancia.formulario.find(`[name=${campo}]`) }
                campo.etiqueta = campo.elemento.closest('.form-group').find('label').text();
                campo.valor = campo.elemento.is('select') ? campo.elemento[0].selectedOptions[0].text : valor;

                if (/fecha|desde|hasta/i.test(campo.nombre))
                  campo.valor = new Date(`${campo.valor} 00:00:00.000`).toLocaleDateString('uk').replaceAll('.', '/')
                ;

                ventana.tabla.cabecera.parametros.push({
                  campo: campo.etiqueta,
                  valor: campo.valor
                })

              }

            });

            await $.ajax({
              url: `${window.aplicacion.enlace}/ejecutar`,
              type: 'GET',
              data: {
                procedimiento: 'p_traer_cabecera_impresion',
                json_filtros: JSON.stringify(ventana.tabla.cabecera.parametros)
              },
              beforeSend: function(peticion, configuracion) {},
              success: (respuesta, estado, peticion) => {

                respuesta.forEach(parametro => {

                  switch (parametro.campo) {

                    case 'logo':

                      parametro.campo = '';

                      parametro.valor = `
                        <img
                          class="w-25 mb-1"
                          src="${window.aplicacion.enlace}/archivos/imagenes/logotipo/${parametro.valor}.png"
                        >
                      `

                    break;

                    case 'ancho':
                      return ventana.tabla.cabecera.columna.contenido = `<p style="width: ${parametro.valor}"></p>`
                    break;

                    case 'filtros':
                      return ventana.tabla.cabecera.fila.elemento.find(`td.bg-transparent`).append(`
                        <p class="m-1 mb-0 text-center" style="font-size: 10px">${parametro.valor}</p>
                      `)
                    break;

                  }

                  if (parametro.campo)
                    parametro.valor = `
                      <p class="d-flex justify-content-between">
                        <span>${parametro.campo}</span>
                        <span>${parametro.valor}</span>
                      </p>
                    `
                  else {

                    if (!parametro.valor) {

                        ventana.tabla.cabecera.fila.elemento.find('td.bg-transparent div.d-flex.justify-content-between').append(`<div class="d-flex flex-column">${ventana.tabla.cabecera.columna.contenido}</div>`);

                        ventana.tabla.cabecera.columna.contenido = '';

                        return

                    }

                    parametro.valor = `<p>${parametro.valor}</p>`

                  }

                  ventana.tabla.cabecera.columna.contenido += parametro.valor

                })

              },
              error: function(peticion, estado) {},
              complete: (peticion, estado) => {}
            });

            ventana.tabla.elemento.find('thead').prepend(ventana.tabla.cabecera.fila.elemento);

            ventana.tabla.cabecera.parametros.filtros &&
            ventana.tabla.elemento.find('thead tr td.bg-transparent').append(`
              <p class="m-1 mb-0 text-center" style="font-size: 10px">${ventana.tabla.cabecera.parametros.filtros}</p>
            `);

            setTimeout(() => {
							aplicacion.empresa.rnc !== '131136303' ? ventana.elemento.print() : null;
						}, 500)

          }
        }, {
          extend: 'excelHtml5',
          className: 'btn buttons-excel',
          filename: () => {

            return prompt('Ingrese el nombre del archivo Excel', `Reportes de ${document.title.split('s de ')[1]}`)

          },
          text: 'EXCEL <i class="fa-regular fa-file-excel px-1"></i>',
          footer: true,
          exportOptions: { page: 'current', columns: ':visible' }
        }],
        initComplete: (configuracion, datos) => {

          let tabla = {
            contenedor: $('.dataTables_wrapper').has(this.tabla),
            campo: {
              busqueda: {
                contenedor: $(this.#tabla.configuracion.campo.busqueda.peticion
                  ? `<form class="form" autocomplete="off">
                      <div class="input-group">

                        <div class="form-group">
                          <label for="">Buscar</label>
                        </div>

                        <button type="submit" class="btn btn-primary">
                          <i class="fa-solid fa-magnifying-glass"></i>
                        </button>

                      </div>
                    </form>
                  `
                  : `<div class="form-group">
                      <label for="">Buscar</label>
                    </div>
                  `
                )
              }
            }
          }

          tabla.campo.busqueda.elemento = tabla.contenedor.find('.dataTables_filter input');

          if (this.#tabla.configuracion.campo.busqueda.peticion)
            tabla.campo.busqueda.elemento.attr({
              name: 'entidad',
              onkeyup: `
                event.keyCode === 13 &&
                !$(this).closest('.dataTables_wrapper').find('table tbody .table-active').length &&
                $(this).closest('form').submit()`
            })
          ;

          $(tabla.campo.busqueda.elemento).insertAfter(tabla.campo.busqueda.contenedor.find('label'));

          tabla.contenedor.find('.dataTables_filter').append(tabla.campo.busqueda.contenedor);

          tabla.contenedor.find('.dataTables_filter > label').remove();

          $(this.tabla)
            .parent()
            .parent()
            .find('.dataTables_scrollHead th:not(.d-none)')
            .first()
            .css('border-top-left-radius', '5px')
          ;

          $(this.tabla)
            .parent()
            .parent()
            .find('.dataTables_scrollHead th:not(.d-none)')
            .last()
            .css('border-top-right-radius', '5px')
          ;

          $(this.tabla)
            .parent()
            .parent()
            .find('.dataTables_scrollFoot th:not(.d-none)')
            .first()
            .css('border-bottom-left-radius', '5px')
          ;

          $(this.tabla)
            .parent()
            .parent()
            .find('.dataTables_scrollFoot th:not(.d-none)')
            .last()
            .css('border-bottom-right-radius', '5px')
          ;

          $('.dt-buttons').removeClass('flex-wrap')

        }
      }
    };

    this.#configurar()

  }

  async #configurar() {

    this.#tabla.elemento = $(this.tabla);

    Object.assign(this.#tabla.configuracion, this.configuracion);

    if (this.columna instanceof Array)
      this.#tabla.configuracion.columns = this.columna
    else if (this.columna instanceof Object) {

      if (this.columna.peticion && Object.keys(this.columna.peticion).length) {

        this.#tabla.columna.peticion = this.columna.peticion;

        this.#tabla.columna.peticion.parametro['0'] = 0

      } else
        this.#tabla.columna.peticion = null

    }

    if (this.datos instanceof Array)
      this.#tabla.configuracion.data = this.datos
    else if (this.datos instanceof Object && Object.keys(this.datos).length) {

      this.datos.peticion.parametro['0'] = 0;

      this.#tabla.configuracion.ajax = {
        url: this.datos.peticion.ruta,
        type: 'GET',
        data: this.datos.peticion.parametro,
        dataSrc: '',
        dataType: 'JSON',
        beforeSend: function(peticion, configuracion) {},
        // success: function(respuesta, estado, peticion) {},
        error: function(peticion, estado) {},
        complete: function(peticion, estado) {}
      }

    }

  }

  async #columnas() {

    if (this.#tabla.columna.peticion)
      await $.get(this.#tabla.columna.peticion.ruta, this.#tabla.columna.peticion.parametro, respuesta => {

        respuesta.forEach((registro, indice) => {

          registro.elemento = document.createElement('th');

          if (registro.titulo)
            registro.elemento.textContent = registro.titulo
          ;

          if (registro.columna)
            registro.elemento.dataset.columnaNombre = registro.columna
          ;

          if (registro.tipo)
            registro.elemento.dataset.columnaTipo = registro.tipo
          ;

          if (Number(registro.columna_primaria))
            registro.elemento.classList.add('id')
          ;

          if (!Number(registro.visible))
            registro.elemento.classList.add('d-none')
          ;

          if (registro.ancho)
            registro.elemento.dataset.columnaAncho = registro.ancho
          ;

          switch (registro.alineacion) {

            case 'izquierda': registro.alineacion = ''; break;

            case 'centro': registro.alineacion = 'text-center'; break;

            case 'derecha': registro.alineacion = 'text-end'; break;

          }

          if (registro.alineacion)
            registro.elemento.classList.add(registro.alineacion)
          ;

          if (Number(registro.negrita))
            registro.elemento.classList.add('fw-bold')
          ;

          if (Number(registro.ordenable))
            registro.elemento.dataset.columnaOrdenable = ''
          ;

          if (Number(registro.buscable))
            registro.elemento.dataset.columnaBuscable = ''
          ;

          if (!isNaN(registro.sumar))
            registro.sumar = Number(registro.sumar)
          ;
          if (Number(registro.sumar))
            registro.elemento.dataset.columnaSumar = ''
          else if (registro.sumar)
            registro.elemento.dataset.columnaSumar = registro.sumar
          ;

          if (Number(registro.agrupar))
            registro.elemento.dataset.columnaAgrupar = ''
          ;

          if (registro.campo_atributos)
            registro.elemento.dataset.columnaCampoAtributos = registro.campo_atributos
          ;

          this.#tabla.elemento.find('thead tr').append(registro.elemento)

        });

        this.#tabla.elemento.find('tbody tr').append(`
          <td class="text-center" colspan="100%">Cargando...</td>
        `);

        this.#tabla.elemento.find('tfoot tr').append('<th colspan="100%">&nbsp;</th>')

      })
    ;

    return this.#tabla.elemento.find('thead th').toArray()

  }

  async inicializar() {

    if (this.#metodo.inicializar.ejecucion)
      return
    ;

    this.#metodo.inicializar.ejecucion = true;

    await this.destruir();

    this.#configurar();

    let columnas = await this.#columnas();

    columnas.forEach((registro, indice) => {

      let columna = { defaultContent: '' }

      if (registro.innerHTML)
        columna.title = registro.innerHTML
      ;

      if (registro.dataset.columnaNombre)
        columna.data = registro.dataset.columnaNombre
      ;

      if (registro.className)
        columna.className = registro.className
      ;

      if (registro.dataset.columnaAncho)
        columna.width = registro.dataset.columnaAncho
      ;

      if (registro.dataset.columnaOrdenable === undefined)
        columna.orderable = false
      ;

      if (registro.dataset.columnaBuscable === undefined)
        columna.orderable = false
      ;

      if (registro.dataset.columnaSumar !== undefined)
        this.#tabla.columna.sumar.push({
          indice: indice,
          nombre: columna.data,
          clases: columna.className,
          filas: registro.dataset.columnaSumar === 'filas'
        })
      ;

      if (registro.dataset.columnaAgrupar !== undefined)
        Object.assign(this.#tabla.columna.agrupar, {
          indice: indice,
          nombre: columna.data
        })
      ;

      if (registro.dataset.columnaCampoAtributos)
        registro.dataset.columnaCampoAtributos = registro.dataset.columnaCampoAtributos.trim().replace(/\s+/g, ' ')
      ;

      switch (registro.dataset.columnaTipo) {

        case 'varchar': registro.dataset.columnaTipo = 'string'; break;

        case 'int': registro.dataset.columnaTipo = 'num'; break;

        case 'numeric': registro.dataset.columnaTipo = 'num-fmt'; break;

        case 'datetime': registro.dataset.columnaTipo = 'date'; break;

      }

      if (registro.dataset.columnaTipo)
        columna.render = (celda, renderizado, fila, propiedades) => {

          if (renderizado === 'display')
            switch (registro.dataset.columnaTipo) {

              case 'num-fmt':
                celda = formatoNumerico(celda, 2)
              break;

              case 'date':
                celda = new Date(celda).toLocaleDateString('uk').replaceAll('.', '/')
              break;

              case 'input':

                celda = `
                  <input
                    ${registro.dataset.columnaCampoAtributos ?? `class="form-control"`}
                    value=${celda}
                  >
                `

              break;

              case 'checkbox':

                celda = `
                  <div class="form-check justify-content-center">
                    <input
                      type="checkbox"
                      ${registro.dataset.columnaCampoAtributos ?? `class="form-check-input"`}
                      value="${celda}"
                      ${Number(celda) ? 'checked' : ''}
                    >
                  </div>
                `

              break;

              default:

                switch (registro.dataset.columnaTipo) {

                  case 'visualizar':
                    celda = `
                      <a href="#" class="text-primary ${registro.dataset.columnaTipo}">
                        <i class="fa-solid fa-eye"></i>
                      </a>
                    `
                  break;

                  case 'modificar':
                    celda = `
                      <a href="#" class="text-success ${registro.dataset.columnaTipo}">
                        <i class="fa-solid fa-pen-to-square"></i>
                      </a>
                    `
                  break;

                  case 'eliminar':
                    celda = `
                      <a href="#" class="text-danger ${registro.dataset.columnaTipo}">
                        <i class="fa-solid fa-trash"></i>
                      </a>
                    `
                  break;

                  case 'imprimir':
                    celda = `
                      <a href="#" class="text-primary ${registro.dataset.columnaTipo}">
                        <i class="fa-solid fa-print"></i>
                      </a>
                    `
                  break;

                  default:
                  break;

                }

              break

            }

          return celda

        }
      ;

      this.#tabla.configuracion.columns.push(columna);

      this.#tabla.columna.agrupar.fila.pie.append(`<td class="${columna.className}"></td>`)

    });

    if (this.#tabla.columna.sumar.length)
      this.#tabla.configuracion.drawCallback = (configuracion) => {

        this.#tabla.columna.sumar.forEach((valor, iteracion) => {

          let columna =
            this.#tabla.elemento.DataTable().column(valor.indice, { page: 'current' })
          ;

          $(columna.footer()).html(
            valor.filas
              ? this.#tabla.elemento.DataTable().rows({ page: 'current' }).count()
              : () => (window.aplicacion?.ruta === 'reportes.nominas' && $('#tipo_reporte').val() === '01' ? 'TOTAL: ' : '') + formatoNumerico(sumar(columna.data()), 2)
          )

        })

      }
    ;

    if (this.#tabla.columna.agrupar.indice || this.#tabla.columna.agrupar.nombre) {

      // this.#tabla.configuracion.orderFixed =
      //   [[this.#tabla.columna.agrupar.indice, 'asc']]
      // ;

      this.#tabla.configuracion.rowGroup = {
        dataSrc: this.#tabla.columna.agrupar.nombre,
        startRender: (filas, grupo) => {

          if (!grupo || grupo === 'No group')
            grupo = '&nbsp;'
          ;

          return $('<tr/>').append(`<td colspan="100%">${grupo}</td>`);

        },
        endRender: (filas, grupo) => {

          this.#tabla.columna.sumar.forEach((valor, iteracion) => {

            let total = 0;

            if (valor.filas)
              this.#tabla.columna.agrupar.fila.pie
                .children()
                .eq(valor.indice)
                .text(filas.count())
            else {

              filas.data().filter((fila, indice, filas) => {

                total += Number(fila[valor.nombre].replaceAll(',', ''))

              });

              this.#tabla.columna.agrupar.fila.pie.children().eq(valor.indice).text(
                (window.aplicacion.ruta === 'reportes.nominas' && $('#tipo_reporte').val() === '01' ? 'SUBTOTAL: ' : '') + formatoNumerico(total, 2)
              )

            }

          });

          if (this.#tabla.columna.sumar.length)
            return this.#tabla.columna.agrupar.fila.pie.clone()

        }
      }

      this.#tabla.configuracion.buttons.findIndex((boton) => {

        if (boton.extend === 'colvis')
          boton.className += ' d-none'

      })

    }

    if (
      !this.#tabla.elemento.find('tfoot th').length ||
      this.#tabla.elemento.find('tfoot th[colspan="100%"]').length
    )
      this.#tabla.elemento.find('tfoot th').remove() &&
        this.#tabla.configuracion.columns.forEach((columna, iteracion) => {

          this.#tabla.elemento.find('tfoot tr').append('<th>&nbsp;</th>')

        })
    ;

    $(this.#tabla.elemento).DataTable(this.#tabla.configuracion);

    this.#metodo.inicializar.ejecucion = false

  }

  async destruir() {

    if ($.fn.DataTable.isDataTable(this.#tabla.elemento))
      await this.#tabla.elemento.removeClass('dataTable').DataTable().destroy()
    ;

    if (this.#tabla.columna.peticion || this.#tabla.configuracion.columns.length) {

      if (this.#tabla.columna.peticion) {

        this.#tabla.configuracion.columns = [];
        this.#tabla.columna.sumar = [];
        this.#tabla.columna.agrupar = {
          fila: { pie: $('<tr/>') }
        }

      }

      await this.#tabla.elemento
        .find('thead tr:not(:first), tfoot tr:not(:first)')
        .remove()
      ;

      await this.#tabla.elemento
        .find('thead tr, tbody, tfoot tr')
        .children()
        .remove()

    }

    delete this.#tabla.configuracion.rowGroup

  }

}
