import CustomDataTable from "../app/CustomDataTable.js?v=1.0.0";
import {
  mostrarMensajes,
  mostrarToastify,
  obtenerHTMLBotonesDropdown,
} from "../app/funcionesGenerales.js?v=1.0.0";
import metodosFormularios from "../app/metodosFormularios.js?v=1.0.0";
import Usuario from "../models/usuarioModel.js?v=1.0.0";
import Rol from "../models/rolModel.js?v=1.0.0";
import TipoRol from "../models/tipoRolModel.js?v=1.0.0";

!(function () {
  document
    .getElementById("btn-agregar-usuario")
    .addEventListener("click", () => {
      configUsuario.editar();
    });

  document
    .getElementById("btn-cancelar-usuario")
    .addEventListener("click", () => {
      configUsuario.cancelar();
    });

  document
    .getElementById("btn-username-check")
    .addEventListener("click", () => {
      configUsuario.verificarDisponibilidad();
    });

  document.getElementById("usuario-form").addEventListener("submit", (e) => {
    e.preventDefault();
    configUsuario.guardar();
  });

  document.getElementById("rol-form").addEventListener("submit", (e) => {
    e.preventDefault();
    configRol.guardar();
  });

  document.getElementById("btn-asignar-rol").addEventListener("click", () => {
    configRol.editar();
  });

  document.getElementById("cbo-rol").addEventListener("change", () => {
    configRol.evaluarRol();
  });

  document.addEventListener("click", function (e) {
    const target = e.target;

    if (target.classList.contains("editar-usuario"))
      configUsuario.editar(target.dataset.id);

    if (target.classList.contains("editar-rol"))
      configRol.editar(target.dataset.id);

    if (target.classList.contains("eliminar-usuario"))
      configUsuario.eliminar(target.dataset.id);

    if (target.classList.contains("eliminar-rol"))
      configRol.eliminar(target.dataset.id);

    if (target.classList.contains("crear-contrasenia-temporal"))
      configUsuario.crearContrasenia(target.dataset.id);

    if (target.classList.contains("cambiar-estado"))
      configUsuario.cambiarEstado(target.dataset.id);

    if (target.classList.contains("eliminar-usuario"))
      configUsuario.eliminar(target.dataset.id);
  });
})();

window.onload = async function () {
  try {
    await inicializarTablas();
    await configRol.inicializar();
    await configUsuario.inicializar();

    document.querySelector(".loader") &&
      document.querySelector(".loader").classList.add("d-none");
  } catch (err) {
    mostrarMensajes.error(err);
  }
};

function inicializarTablas() {
  return new Promise((resolve, reject) => {
    try {
      configUsuario.tabla = new CustomDataTable({
        id: "usuarios-tbl",
        alineadoIzquierda: [2, 3],
        noOrdenable: [7],
      });

      configRol.tabla = new CustomDataTable({
        id: "roles-tbl",
        alineadoIzquierda: [1, 2],
        noOrdenable: [3],
      });
      resolve();
    } catch (err) {
      reject(err);
    }
  });
}

const configUsuario = {
  id: 0,
  cnt: 0,
  data: {},
  tabla: null,
  form: "usuario-form",
  campos: {
    foto: document.getElementById("img-foto-usuario"),
    username: document.getElementById("txt-username"),
    nombres: document.getElementById("txt-nombres"),
    apellidos: document.getElementById("txt-apellidos"),
    email: document.getElementById("txt-email"),
    contrasenia: document.getElementById("txt-contrasenia"),
    confirmacionContrasenia: document.getElementById(
      "txt-confirmacion-contrasenia"
    ),
  },
  tabs: {
    gestionUsuarios: document.getElementById("usuarios-tab"),
    edicionUsuario: document.getElementById("editar-usuario-tab"),
  },
  botones: {
    agregarUsuario: document.getElementById("btns-nuevo-usuario"),
    guardar: document.getElementById("btns-guardar-usuario"),
  },
  inicializar: function () {
    return new Promise(async (resolve, reject) => {
      try {
        const { data: usuarios } = await Usuario.obtenerTodos();

        usuarios.forEach((usuario) => {
          this.cnt++;
          this.data[this.cnt] = usuario;

          const fila = this.prepararParaFila({ datos: usuario, id: this.cnt });

          this.tabla.agregarFila({
            datos: fila,
            idFila: `trUsuario${this.cnt}`,
          });
        });

        this.tabla.redibujar();
        resolve();
      } catch (err) {
        reject(err);
      }
    });
  },
  prepararParaFila: function ({ datos, id }) {
    let colorBorde = `border-light`;

    if (datos.sesionActiva) {
      colorBorde = `border-success`;

      const fechaActual = moment().startOf("day");
      const ultimoIngreso = moment(
        datos.ultimoIngreso,
        "DD-MM-YYYY hh:mm a"
      ).startOf("day");

      if (fechaActual.diff(ultimoIngreso, "days") > 0)
        colorBorde = `border-warning`;
    }

    return {
      0: id,
      1: `<div class="tbl-img-container">
        <div class="tbl-img border border-2 ${colorBorde}" style="background-image:url('main/assets/images/users/${datos.foto}')"></div>
      </div>`,
      2: `${datos.nombres} ${datos.apellidos}`,
      3: datos.email,
      4: datos.fechaRegistro,
      5: datos.ultimoIngreso,
      6: datos.bloqueado == 0 ? "Habilitado" : "Bloqueado",
      7: obtenerHTMLBotonesDropdown([
        {
          tipo: "edit",
          selectorClase: `editar-usuario`,
          selectorId: id,
          texto: "Editar",
        },
        {
          tipo: "password",
          selectorClase: `crear-contrasenia-temporal`,
          selectorId: id,
          texto: "Crear contraseña temporal",
        },
        {
          tipo: datos.bloqueado == 0 ? "lock" : "unlock",
          selectorClase: `cambiar-estado`,
          selectorId: id,
          texto: datos.bloqueado == 0 ? "Bloquear" : "Desbloquear",
        },
        {
          tipo: "delete",
          selectorClase: `eliminar-usuario`,
          selectorId: id,
          texto: "Eliminar",
        },
      ]),
    };
  },
  editar: async function (id = 0) {
    try {
      await metodosFormularios.limpiar({ id: this.form });

      this.id = id;

      const contenedorContrasenia =
        document.getElementById("password-container");

      contenedorContrasenia.classList.remove("d-none");
      this.campos.contrasenia.required = this.id == 0;
      this.campos.confirmacionContrasenia.required = this.id == 0;

      const obtenerRoles = async () => {
        if (this.id == 0) {
          return [];
        }

        const { data: roles = [] } = await Usuario.obtenerRoles({
          id: this.data[this.id].id,
        });
        return roles;
      };

      await configRol.configurar({ roles: await obtenerRoles() });

      if (this.id > 0) {
        contenedorContrasenia.classList.add("d-none");

        const usuario = this.data[this.id];

        this.campos.username.value = usuario.username;
        this.campos.nombres.value = usuario.nombres;
        this.campos.apellidos.value = usuario.apellidos;
        this.campos.email.value = usuario.email;
      }

      Object.values(this.tabs).forEach((tab) =>
        tab.classList.toggle("disabled")
      );
      this.tabs.edicionUsuario.click();
      Object.values(this.botones).forEach((btn) =>
        btn.classList.toggle("d-none")
      );
    } catch (err) {
      mostrarMensajes.error(err);
    }
  },
  guardar: async function () {
    if (!metodosFormularios.validar({ id: this.form })) return;

    const roles = configRol.obtenerDatos();

    if (roles.length == 0)
      return mostrarMensajes.warning({
        message: "Debe asignar al menos un rol al usuario",
      });

    let contrasenia = this.campos.contrasenia.value?.trim() || null,
      confirmacionContrasenia =
        this.campos.confirmacionContrasenia.value?.trim() || null;

    if (this.id == 0 && contrasenia != confirmacionContrasenia)
      return mostrarMensajes.warning({
        message:
          "Las contraseña y la confirmación de la contraseña no coinciden",
      });

    try {
      let usuario = new Usuario({
        id: this.data[this.id]?.id || 0,
        username: this.campos.username.value.trim(),
        nombres: this.campos.nombres.value.trim(),
        apellidos: this.campos.apellidos.value.trim(),
        email: this.campos.email.value.trim(),
        fechaRegistro:
          this.data[this.id]?.fechaRegistro ||
          moment().format("DD-MM-YYYY hh:mm a"),
        foto: this.data[this.id]?.foto || "default.png",
        ultimoIngreso: this.data[this.id]?.ultimoIngreso || "Sin registros",
        roles,
      });

      const respuesta = await usuario.guardar({
        contrasenia,
      });

      switch (respuesta.message) {
        case "success":
          usuario.id = respuesta.data.id;

          const fila = this.prepararParaFila({
            datos: usuario,
            id: this.id > 0 ? this.id : this.cnt + 1,
          });

          console.log(fila);

          if (this.id == 0) {
            this.cnt++;
            this.id = this.cnt;

            this.tabla
              .agregarFila({
                datos: fila,
                idFila: `trUsuario${this.id}`,
              })
              .redibujar();
          } else {
            this.tabla
              .actualizarFila({
                datos: fila,
                idFila: `trUsuario${this.id}`,
              })
              .redibujar();
          }

          this.data[this.id] = usuario;

          mostrarToastify({
            message: "Usuario guardado correctamente",
            type: "success",
          });

          if (roles.find((r) => parseInt(r.idRol) == 0))
            configRol.inicializar().catch(mostrarMensajes.error);

          this.cancelar();
          break;
        case "duplicado":
          mostrarMensajes.warning({
            message: "El username ingresado ya se encuentra registrado",
          });
          break;
        default:
          mostrarMensajes.error(respuesta);
          break;
      }
    } catch (err) {
      mostrarMensajes.error(err);
    }
  },
  crearContrasenia: async function (id) {
    const usuario = this.data[id];

    try {
      const confirmacion = await mostrarMensajes.confirm({
        message: `Se creará una contraseña temporal para <strong>${usuario.nombres} ${usuario.apellidos}</strong>, adicionalmente se le enviará un correo. <br />Esta contraseña sólo será válida este día.`,
      });

      if (!confirmacion) return;

      const respuesta = await Usuario.crearContrasenia({
        id: usuario.id,
        username: usuario.username,
        email: usuario.email,
      });

      switch (respuesta.message) {
        case "success":
          mostrarMensajes.info({
            message: `La contraseña temporal es: <strong>${respuesta.contrasenia}</strong>`,
            allowOutsideClick: false,
            showCancelButton: false,
            showCloseButton: false,
          });
          break;
        default:
          mostrarMensajes.error(respuesta);
          break;
      }
    } catch (err) {
      if (!!err) mostrarMensajes.error(err);
    }
  },
  cambiarEstado: async function (id) {
    const usuario = new Usuario(this.data[id]);

    usuario.bloqueado = usuario.bloqueado == 0 ? 1 : 0;

    let strEstado = usuario.bloqueado == 0 ? "desbloqueará" : "bloqueará";

    try {
      const confirmacion = await mostrarMensajes.confirm({
        message: `Se ${strEstado} el usuario <strong>${usuario.nombres} ${usuario.apellidos}</strong>`,
      });

      if (!confirmacion) return;

      const respuesta = await usuario.cambiarEstado();

      switch (respuesta.message) {
        case "success":
          mostrarToastify({
            message: `Datos actualizados correctamente`,
          });

          this.data[id] = usuario;

          this.tabla
            .actualizarFila({
              datos: this.prepararParaFila({
                datos: {
                  ...usuario,
                },
                id,
              }),
              idFila: `trUsuario${id}`,
            })
            .redibujar();
          break;
        default:
          mostrarMensajes.error(respuesta);
          break;
      }
    } catch (err) {
      if (!!err) mostrarMensajes.error(err);
    }
  },
  eliminar: async function (id) {
    const usuario = new Usuario(this.data[id]);

    try {
      const confirmacion = await mostrarMensajes.confirm({
        message: `Se eliminará el usuario <strong>${usuario.nombres} ${usuario.apellidos}</strong>. <br />Esta acción no se puede deshacer.`,
        confirmButtonText: "Sí, eliminar",
        cancelButtonText: "Cancelar",
      });

      if (!confirmacion) return;

      const respuesta = await usuario.eliminar();

      switch (respuesta.message) {
        case "success":
          this.tabla.eliminarFila({ idFila: `trUsuario${id}` }).redibujar();
          delete this.data[id];
          mostrarToastify({
            message: "Usuario eliminado correctamente",
            type: "success",
          });
          break;
        default:
          mostrarMensajes.error(respuesta);
          break;
      }
    } catch (err) {
      if (!!err) mostrarMensajes.error(err);
    }
  },
  cancelar: function () {
    Object.values(this.tabs).forEach((tab) => tab.classList.toggle("disabled"));
    this.tabs.gestionUsuarios.click();
    Object.values(this.botones).forEach((btn) =>
      btn.classList.toggle("d-none")
    );
  },
  verificarDisponibilidad: async function () {
    const username = this.campos.username.value;

    if (!username) {
      mostrarToastify({
        message: "Ingrese un nombre de usuario",
        type: "warning",
      });
      return;
    }

    try {
      const idUsuario = this.data[this.id]?.id || 0;

      const respuesta = await Usuario.verificarDisponibilidad({
        id: idUsuario,
        username,
      });

      switch (respuesta.message) {
        case "success":
          mostrarToastify({
            message: "El username está disponible",
            type: "success",
          });
          break;
        case "duplicado":
          mostrarToastify({
            message: "El username ya está en uso",
            type: "warning",
          });
          break;
        default:
          mostrarMensajes.error(respuesta);
          break;
      }
    } catch (err) {
      mostrarMensajes.error(err);
    }
  },
};

const configRol = {
  id: 0,
  cnt: 0,
  data: {},
  tabla: null,
  campos: {
    rol: document.getElementById("cbo-rol"),
    nuevoRol: document.getElementById("txt-rol"),
    tipoRol: document.getElementById("cbo-tipo-rol"),
  },
  catalogo: [],
  form: "rol-form",
  modal: {
    id: "modal-rol",
    mostrar: () => $(`#${configRol.modal.id}`).modal("show"),
    ocultar: () => $(`#${configRol.modal.id}`).modal("hide"),
  },
  inicializar: function () {
    return new Promise(async (resolve, reject) => {
      try {
        const { data: roles = [] } = await Rol.obtenerTodos();
        const { data: tiposRol = [] } = await TipoRol.obtenerTodos();
        this.catalogo = roles;

        let opciones = tiposRol.map(
          (tipo) => `<option value="${tipo.id}">${tipo.tipo}</option>`
        );

        opciones.length > 0 &&
          opciones.unshift(
            `<option value="" selected disabled>Seleccione</option>`
          );

        this.campos.tipoRol.innerHTML = opciones.join("");

        resolve();
      } catch (err) {
        reject(err);
      }
    });
  },
  configurar: function ({ roles = [] }) {
    return new Promise(async (resolve, reject) => {
      try {
        await this.limpiar();

        roles.forEach((rol) => {
          this.cnt++;
          this.data[this.cnt] = rol;

          const fila = this.prepararParaFila({ datos: rol, id: this.cnt });

          this.tabla.agregarFila({ datos: fila, idFila: `trRol${this.cnt}` });
        });

        this.tabla.redibujar();
        resolve();
      } catch (err) {
        reject(err);
      }
    });
  },
  prepararParaFila: function ({ datos, id }) {
    return {
      0: id,
      1: datos.rol,
      2: datos.tipo,
      3: obtenerHTMLBotonesDropdown([
        {
          tipo: "edit",
          selectorClase: `editar-rol`,
          selectorId: id,
          texto: "Editar",
        },
        {
          tipo: "delete",
          selectorClase: `eliminar-rol`,
          selectorId: id,
          texto: "Eliminar",
        },
      ]),
    };
  },
  evaluarRol: function () {
    if (!this.campos.rol.value) return;

    const rolSeleccionado = this.catalogo.find(
      (cat) =>
        cat.rol.trim() ==
        this.campos.rol.options[this.campos.rol.selectedIndex].text.trim()
    );

    this.campos.tipoRol.disabled = true;

    this.campos.tipoRol.value = rolSeleccionado?.idTipo || "";

    if (!rolSeleccionado && this.campos.rol.value == "0") {
      document
        .getElementById(`${this.campos.rol.id}-container`)
        .classList.add("d-none");
      document
        .getElementById(`${this.campos.nuevoRol.id}-container`)
        .classList.remove("d-none");

      this.campos.nuevoRol.required = true;
      this.campos.tipoRol.disabled = false;
    }
  },
  editar: async function (id = 0) {
    try {
      await metodosFormularios.limpiar({ id: this.form });
      this.campos.tipoRol.disabled = true;
      this.campos.nuevoRol.required = false;

      const contenedorRol = document.getElementById(
          `${this.campos.rol.id}-container`
        ),
        contenedorNuevoRol = document.getElementById(
          `${this.campos.nuevoRol.id}-container`
        );

      this.id = id;

      let rolesAsignados = Object.values(this.data).map((guardado) =>
          guardado.rol.trim()
        ),
        opciones = this.catalogo
          .filter((cat) => {
            const temp = this.data[this.id];
            const rol = temp?.rol || "";
            return (
              !rolesAsignados.includes(cat.rol.trim()) ||
              cat.rol.trim() == rol.trim()
            );
          })
          .map((cat) => `<option value="${cat.id}">${cat.rol}</option>`);

      opciones.unshift(`<option value="0">Agregar rol</option>`);

      opciones.length > 0 &&
        opciones.unshift(
          `<option value="" selected disabled>Seleccione</option>`
        );

      this.campos.rol.innerHTML = opciones.join("");

      contenedorRol.classList.remove("d-none");
      contenedorNuevoRol.classList.add("d-none");

      if (this.id > 0) {
        const guardado = this.data[this.id];

        [...this.campos.rol.options].forEach((opt) => {
          if (opt.text.trim() == guardado.rol.trim()) {
            opt.selected = true;
          }
        });

        this.campos.tipoRol.value = guardado.idTipo;
        contenedorNuevoRol.classList.add("d-none");
        contenedorRol.classList.remove("d-none");
      }

      $(`#${this.campos.rol.id}`).selectpicker("destroy").selectpicker();
      this.modal.mostrar();
    } catch (err) {
      mostrarMensajes.error(err);
    }
  },
  guardar: function () {
    if (!metodosFormularios.validar({ id: this.form })) return;

    const rolUsuario = {
      id: this.id > 0 ? this.data[this.id].id : 0,
      idRol: this.campos.rol.value,
      rol:
        this.campos.rol.value > 0
          ? this.campos.rol.options[this.campos.rol.selectedIndex].text
          : this.campos.nuevoRol.value,
      idTipo: this.campos.tipoRol.value,
      tipo: this.campos.tipoRol.options[this.campos.tipoRol.selectedIndex].text,
    };

    const rolCatalogo = this.catalogo.find(
      (cat) => cat.rol.trim() == rolUsuario.rol.trim()
    );

    if (!!rolCatalogo) rolUsuario.idRol = rolCatalogo.id;

    const fila = this.prepararParaFila({
      datos: rolUsuario,
      id: this.id > 0 ? this.id : this.cnt + 1,
    });

    if (this.id == 0) {
      this.cnt++;
      this.id = this.cnt;

      this.tabla
        .agregarFila({ datos: fila, idFila: `trRol${this.cnt}` })
        .redibujar();
    } else {
      this.tabla
        .actualizarFila({ datos: fila, idFila: `trRol${this.id}` })
        .redibujar();
    }

    this.data[this.id] = rolUsuario;

    if (!rolCatalogo) {
      this.catalogo.push({
        id: 0,
        rol: rolUsuario.rol.trim(),
        idTipo: rolUsuario.idTipo,
        tipo: rolUsuario.tipo.trim(),
      });
    }

    mostrarToastify({
      message: "Rol guardado correctamente",
      type: "success",
    });

    this.modal.ocultar();
  },
  eliminar: async function (id = 0) {
    const guardado = this.data[id];

    try {
      const confirmacion = await mostrarMensajes.confirm({
        message: `Se eliminará el rol <b>${guardado.rol}</b> para este usuario.`,
      });

      if (!confirmacion) return;

      this.tabla.eliminarFila({ idFila: `trRol${id}` }).redibujar();
      delete this.data[id];

      mostrarToastify({
        message: "Rol eliminado correctamente",
        type: "success",
      });
    } catch (err) {
      if (!!err) mostrarMensajes.error(err);
    }
  },
  limpiar: async function () {
    try {
      this.id = 0;
      this.cnt = 0;
      this.data = {};
      this.tabla.limpiar().redibujar();
    } catch (err) {
      throw err;
    }
  },
  obtenerDatos: function () {
    return [...Object.values(this.data)];
  },
};
