/**
 * Función para dar formato numérico.
 * @ Retorna una variable de tipo texto o un arreglo con valores de tipo texto.
 */
export function formatoNumerico(parametroFormatear, cantidadDecimales = 0) {

  const formatear = (valorFormatear) => {

    if (!valorFormatear)
      valorFormatear = 0
    ;

    valorFormatear = Number(
      String(valorFormatear).replaceAll(',', '')
    );

    return valorFormatear.toLocaleString('es-419', {
      minimumFractionDigits: cantidadDecimales,
      maximumFractionDigits: cantidadDecimales
    })

  };

  return Array.isArray(parametroFormatear)
    ? parametroFormatear.map((valorFormatear) => formatear(valorFormatear))
    : formatear(parametroFormatear)

}

/**
 * Función para verificar si el parámetro enviado es una variable o un arreglo de índice único.
 * @ Retorna una variable de tipo booleano
 */
export function variableArregloIndiceUnico(parametroVerificar) {

  const
    arregloIndiceUnico = (
      Array.isArray(parametroVerificar) &&
      parametroVerificar.length === 1
    ),
    variable = (
      !Array.isArray(parametroVerificar) ||
      typeof parametroVerificar === 'string' ||
      typeof parametroVerificar === 'number'
    );

  return (
    arregloIndiceUnico || variable
  )

}

/**
 * Función para singularizar palabras en español.
 * @ Retorna una variable de tipo texto.
 */
export function singularizarPalabra(palabra) {

  // manejar excepciones
  if (palabra === 'autos') {
    return 'auto';
  }
  if (palabra === 'pies') {
    return 'pie';
  }

  // reglas generales
  if (palabra.endsWith('es')) {
    if (palabra.endsWith('ces')) {
      return palabra.slice(0, -3) + 'z';
    }
    if (palabra.endsWith('ses')) {
      return palabra.slice(0, -2);
    }
    if (palabra.endsWith('shes')) {
      return palabra.slice(0, -2);
    }
    if (palabra.endsWith('jes')) {
      return palabra.slice(0, -1);
    }
    if (palabra.endsWith('gues')) {
      return palabra.slice(0, -2);
    }
    if (palabra.endsWith('ques')) {
      return palabra.slice(0, -1);
    }
    if (palabra.endsWith('ues')) {
      return palabra.slice(0, -2) + 'e';
    }
    return palabra.slice(0, -2);
  }
  if (palabra.endsWith('s')) {
    if (palabra.endsWith('rs')) {
      return palabra.slice(0, -1);
    }
    if (palabra.endsWith('ns')) {
      return palabra.slice(0, -1);
    }
    if (palabra.endsWith('ss')) {
      return palabra.slice(0, -1);
    }
    if (palabra.endsWith('us')) {
      return palabra.slice(0, -2) + 'o';
    }
    return palabra.slice(0, -1);
  }
  if (palabra.endsWith('a')) {
    return palabra.slice(0, -1);
  }
  if (palabra.endsWith('z')) {
    return palabra.slice(0, -1);
  }
  if (palabra.endsWith('ión')) {
    return palabra.slice(0, -3);
  }
  if (palabra.endsWith('dad')) {
    return palabra.slice(0, -3);
  }
  if (palabra.endsWith('tud')) {
    return palabra.slice(0, -3);
  }
  if (palabra.endsWith('eza')) {
    return palabra.slice(0, -3);
  }
  if (palabra.endsWith('ie')) {
    return palabra.slice(0, -2) + 'z';
  }
  if (palabra.endsWith('nte')) {
    return palabra.slice(0, -3);
  }

  return palabra;

}

/**
 * Función para pluralizar palabras en español.
 * @ Retorna una variable de tipo texto.
 */
export function pluralizarPalabra(palabra) {

  // ! Declaración de variables
  let
    palabraPluralizada = null,
    ultimaLetra = null,
    penultimaLetra = null,
    antepenultimaLetra = null,
    trasantepenultimaLetra = null,
    letraAntesY = null,
    letraAntesZ = null;

  // ! Asignando valores
  ultimaLetra = palabra.slice(-1),
  penultimaLetra = palabra.slice(-2),
  antepenultimaLetra = palabra.slice(-3),
  trasantepenultimaLetra = palabra.slice(-4);

  // !
  if (
    ultimaLetra === "a" ||
    ultimaLetra === "e" ||
    ultimaLetra === "i" ||
    ultimaLetra === "o" ||
    ultimaLetra === "u"
  ) {

    // + Asignando valor
    palabraPluralizada = palabra + "s"

  }
  // !
  else if (ultimaLetra === "y") {

    // +
    if (
      penultimaLetra === "ay" ||
      penultimaLetra === "ey" ||
      penultimaLetra === "oy" ||
      penultimaLetra === "uy"
    ) {

      // ? Asignando valor
      palabraPluralizada = palabra + "s"

    }
    // +
    else {

      // ? Asignando valor
      letraAntesY = palabra.slice(0, -1);

      // ? Asignando valor
      palabraPluralizada = letraAntesY + "ies"

    }

  }
  // !
  else if (ultimaLetra === "z") {

    // + Asignando valor
    letraAntesZ = palabra.slice(0, -1);

    // + Asignando valor
    palabraPluralizada = letraAntesZ + "ces"

  }
  // !
  else if (
      ultimaLetra === "n" && (
        antepenultimaLetra === "ión" ||
        trasantepenultimaLetra === "ción"
  )) {

    // + Asignando valor
    palabraPluralizada = palabra

  }
  // !
  else {

    // + Asignando valor
    palabraPluralizada = palabra + "es"

  }

  return palabraPluralizada

}

/**
 * Función para capitalizar solamente la primera letra de palabras en un texto.
 * @ Retorna una variable de tipo texto.
 */
export function capitalizarLetra(texto, delimitador) {

  // Divide la cadena en palabras usando el delimitador especificado o el espacio por defecto
  var palabras = texto.split(delimitador || ' ');
  var resultado = [];

  palabras.forEach(palabra => {

    // Capitaliza la primera letra de cada palabra y convierte el resto de las letras en minúsculas
    resultado.push(palabra.charAt(0).toUpperCase() + palabra.slice(1).toLowerCase())

  });

  // Vuelve a unir las palabras usando el delimitador especificado o el espacio por defecto
  return resultado.join(delimitador || ' ');

}

/**
 * Función para pluralizar frases en español.
 * @ Retorna una variable de tipo texto.
 */
export function pluralizarFrase(frase, delimitador = '_') {

  // Separar las palabras de la frase en un array
  const palabras = frase.split(delimitador);

  // Pluralizar cada palabra del array utilizando la función pluralizarPalabra
  const palabrasPluralizadas = palabras.map(palabra => pluralizarPalabra(palabra));

  // Unir las palabras del array en una frase nuevamente
  const frasePluralizada = palabrasPluralizadas.join(delimitador);

  return frasePluralizada;

}

/**
 * Función para extraer valores numéricos de un texto.
 * @ Retorna una variable de tipo texto.
 */
export function extraerNumerosTexto(valorExtraer) {

  return valorExtraer.replace(/[^\d.-]/g, "")

}

/**
 * Función para remover los diacríticos de un texto.
 * @ Retorna una variable de tipo texto.
 */
export function removerDiacriticosTexto(texto) {

  return texto.normalize('NFD').replace(/[\u0300-\u036f]/g, "")

}

/**
 * Función para actualizar los parámetros de la tabla de registros con la librería 'DataTable'
 * @ Retorna un objeto de 'DataTable'.
 */
export function actualizarParametrosTablaRegistros({
  tabla = `$('#tablaRegistros')`,
  parametros = {}
}) {

  $.each(parametros, function (indice, valor) {

    eval(`
      $(${tabla})
        .dataTable()
        .fnSettings()
        .ajax
        .data
        .${indice} = '${valor}'
    `)

  })

}

/**
 * Función para
 * @ Retorna.
 */
export function formatoFecha(parametroFormatear, cantidadDecimales = 0) {

  f

}

/**
 * Función para agregar mascara a un campo con su formato.
 * @ Retorna una instancia de la mascara con la librería 'iMask'
 */
export function mascara(elemento) {

  let mascara = {
    tipo: elemento.dataset.mascara,
    patron: elemento.dataset.mascaraPatron,
    minimo: Number(elemento.dataset.mascaraValorMinimo),
    maximo: Number(elemento.dataset.mascaraValorMaximo)
  };

  if (isNaN(mascara.minimo))
    mascara.minimo = 0
  ;

  if (isNaN(mascara.maximo))
    mascara.maximo = 2147483648
  ;

  switch (mascara.tipo) {

    case 'entero':

      if (!mascara.patron)
        mascara.patron = Number
      ;

      mascara.propiedades = {
        mask: mascara.patron,
        scale: 0,
        signed: false,
        min: mascara.minimo,
        max: mascara.maximo
      }

    break;

    case 'numerico':

      mascara.decimales = Number(elemento.dataset.mascaraDecimales);

      if (!mascara.patron)
        mascara.patron = Number
      ;

      if (isNaN(mascara.decimales))
        mascara.decimales = 2
      ;

      mascara.propiedades = {
        mask: mascara.patron,

        scale: mascara.decimales,
        signed: false,
        thousandsSeparator: ',',
        padFractionalZeros: true,
        normalizeZeros: true,
        radix: '.',
        mapToRadix: ['.'],

        min: mascara.minimo,
        max: mascara.maximo
      }

      if (!elemento.getAttribute('value'))
        elemento.setAttribute('value', '0.00')
      ;

      $(elemento).change(function(evento) {

        if (!this.value)
          mascara.instancia.value = '0.00'

      })

    break;

    case 'telefono':

      if (!mascara.patron)
        mascara.patron = '000-000-0000'
      ;

      mascara.propiedades = { mask: mascara.patron }

    break;

    case 'cedula':

      if (!mascara.patron)
        mascara.patron = '000-0000000-0'
      ;

      mascara.propiedades = { mask: mascara.patron }

    break;

    case 'rnc':

      if (!mascara.patron)
        mascara.patron = '000-00000-0'
      ;

      mascara.propiedades = { mask: mascara.patron }

    break;

    case 'pasaporte':

      if (!mascara.patron)
        mascara.patron = '***************'
      ;

      mascara.propiedades = { mask: mascara.patron }

    break;

    default:

      if (!mascara.patron)
        mascara.patron = '***************'
      ;

      mascara.propiedades = { mask: mascara.patron }

    break;

  }

  mascara.instancia = IMask(elemento, mascara.propiedades);

  return mascara.instancia

}

/**
 * Función para obtener los parametros de una URL, formulario o objeto.
 * @ Retorna una variable de FormData
 */
export function parametros(variable) {

  let parametros = new FormData();

  if (typeof variable === 'string')
    new URL(variable).searchParams.forEach((valor, parametro) => parametros.set(parametro, valor))
  else if (variable instanceof Element) {

    if (variable.form)
      variable = variable.form
    ;

    variable.campos = {
      deshabilitados: variable.querySelectorAll('[name]:disabled')
    }

    variable.campos.deshabilitados.forEach(campo => campo.disabled = false);

    parametros = new FormData(variable);

    variable.campos.deshabilitados.forEach(campo => campo.disabled = true);

    delete variable.campos.deshabilitados

  } else if (variable instanceof FormData)
    parametros = variable
  else if (variable instanceof Object)
    for (let nombre in variable)
      parametros.set(nombre, variable[nombre] instanceof Object ? JSON.stringify(variable[nombre]) : variable[nombre])
  ;

  return parametros

}

/**
 *
 */
export function sumar(parametro) {

  // !
  let suma = parametro;

  // !
  if (parametro instanceof Array || parametro['$'])
    suma = parametro.reduce((suma, valor) => suma += Number(isFinite(valor) ? valor : valor.replaceAll(',', '')), 0)
  else if (parametro instanceof Object)
    for (let indice in parametro)
      suma += Number(isFinite(parametro[indice]) ? parametro[indice] : parametro[indice].replaceAll(',', ''))
  ;

  // !
  return suma

}

/**
 * Función para fusionar objetos de manera profunda.
 * @ Retorna una variable de tipo objeto
 */
export function fusionar(...objetos) {

  return objetos.reduce((fusion, objeto) => {

    let valor = {};

    Object.keys(objeto).forEach(indice => {

      valor.fusion = fusion[indice];
      valor.objeto = objeto[indice];

      if (Array.isArray(valor.fusion) && Array.isArray(valor.objeto))
        fusion[indice] = valor.fusion.concat(...valor.objeto)
      else if (typeof valor.fusion === 'object' && typeof valor.objeto === 'object')
        fusion[indice] = fusionar(valor.fusion, valor.objeto)
      else
        fusion[indice] = valor.objeto

    });

    return fusion

  }, {})

}

/**
 * Función para realizar peticiones.
 * @ Retorna una variable de tipo objeto
 */
export async function peticion({
  ruta,
  metodo = 'GET',
  modo = 'cors',
  cache = 'default',
  credenciales = 'same-origin',
  encabezado,
  redireccionamiento = 'follow',
  politica = 'no-referrer-when-downgrade',
  parametros: cuerpo = {},
  integridad = '',
  mantener = false,
  ventana,
  funcion = {}
}) {

  if (peticion.controlador)
    peticion.controlador.abort()
  ;

  peticion.controlador = new AbortController();
  peticion.senal = peticion.controlador.signal;

  cuerpo = parametros(cuerpo);

  cuerpo.set('0', 0);

  if (!ruta)
    ruta = window.aplicacion.enlace + '/ejecutar'
  else
    new URL(ruta).searchParams.forEach((valor, parametro) =>
      cuerpo.set(parametro, valor)
    )
  ;

  funcion.parametros = Object.fromEntries(cuerpo);

  if (metodo === 'GET') {

    ruta += '?' + new URLSearchParams(funcion.parametros);

    cuerpo = undefined

  }

  if (!ventana)
    ventana = window
  ;

  let
    configuracion = {
      method: metodo, // GET, POST, PUT DELETE
      mode: modo, // no-cors, cors, same-origin
      cache: cache, // default, no-cache, reload, force-cache, only-if-cached
      credentials: credenciales, // include, same-origin, omit
      headers: encabezado,
      redirect: redireccionamiento, // manual, follow, error
      referrerPolicy: politica, // no-referrer, no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
      body: cuerpo,
      integrity: integridad, // Valor de integridad del subrecurso
      keepalive: mantener, // false, true
      signal: peticion.senal, // undefined, AbortSignal
      window: ventana, // null, window
    },
    respuesta
  ;

  try {

    peticion.instancia = await fetch(ruta, configuracion);

    document.dispatchEvent(new CustomEvent('peticion.preenviada', {
      detail: {
        argumentos: arguments[0],
        ruta: ruta,
        parametros: funcion.parametros,
        configuracion: configuracion,
        funcion: funcion,
        peticion: peticion.instancia
      }
    }));

    typeof funcion.preenviada === 'function' && funcion.preenviada(peticion.instancia);

    if (!peticion.instancia.ok)
      throw await peticion.instancia.json()
    ;

    respuesta = await peticion.instancia.json();

    document.dispatchEvent(new CustomEvent('peticion.exitosa', {
      detail: {
        argumentos: arguments[0],
        ruta: ruta,
        parametros: funcion.parametros,
        configuracion: configuracion,
        funcion: funcion,
        respuesta: respuesta
      }
    }));

    typeof funcion.exitosa === 'function' && funcion.exitosa(respuesta);

    return respuesta

  } catch (respuesta) {

    document.dispatchEvent(new CustomEvent('peticion.erronea', {
      detail: {
        argumentos: arguments[0],
        ruta: ruta,
        parametros: funcion.parametros,
        configuracion: configuracion,
        funcion: funcion,
        respuesta: respuesta
      }
    }));

    typeof funcion.erronea === 'function' && funcion.erronea(respuesta);

    throw respuesta.length ? respuesta[0].mensaje : peticion.instancia.statusText

  } finally {

    document.dispatchEvent(new CustomEvent('peticion.completada', {
      detail: {
        argumentos: arguments[0],
        ruta: ruta,
        parametros: funcion.parametros,
        configuracion: configuracion,
        funcion: funcion,
        instancia: peticion.instancia,
        respuesta: respuesta
      }
    }));

    typeof funcion.completada === 'function' && funcion.completada(respuesta);

    peticion.controlador = null

  }

}
