import { Request, Response } from "express";
import { tryCatch } from "../common/helper";
import Module from "../models/module";
import Role from "../models/role";
import Admin from "../models/admin";

/**
 * Retorna todos los módulos del sistema.
 *
 * Consulta la colección `Module` proyectando únicamente `name` y `code`.
 * Utiliza `tryCatch` para manejo de errores.
 *
 * @export
 * @async
 * @param {Request} req
 * @param {Response} res
 * @returns {unknown}
 */
export const getModules = async (req: Request, res: Response) => {
  const { data, error } = await tryCatch(Module.find({}, "name code").lean());
  if (error) {
    return res
      .status(500)
      .send({ success: false, message: "Error getting modules" });
  }
  return res.status(200).send({ success: true, data });
};

/**
 * Retorna todos los roles con sus módulos asociados.
 *
 * Consulta la colección `Role` proyectando `name`, `code` y `description`,
 * con `populate` de los módulos asociados (`code`, `name`).
 *
 * @export
 * @async
 * @param {Request} req
 * @param {Response} res
 * @returns {unknown}
 */
export const getRoles = async (req: Request, res: Response) => {
  const { data, error } = await tryCatch(
    Role.find({}, "name code description")
      .populate({
        path: "modules",
        select: "code name",
      })
      .lean(),
  );
  if (error) {
    return res
      .status(500)
      .send({ success: false, message: "Error getting roles" });
  }
  return res.status(200).send({ success: true, data });
};

/**
 * Crea un nuevo rol en el sistema.
 *
 * Requiere `name`, `description` y `modules` en el body. Retorna 400
 * si falta alguno de los campos obligatorios.
 *
 * @export
 * @async
 * @param {Request} req - Body: `name`, `description`, `modules` (arreglo de ObjectId)
 * @param {Response} res
 * @returns {unknown}
 */
export const createRole = async (req: Request, res: Response) => {
  const { name, description, modules } = req.body;
  if (!name || !description || !modules) {
    return res
      .status(400)
      .json({
        success: false,
        message: "Name, description and modules are required",
        code: "field_missing",
      });
  }

  const role = await Role.create({ name, description, modules });
  res.status(201).json({ success: true, message: "Role created successfully" });
};

/**
 * Obtiene un rol por su id.
 *
 * Busca el rol en la colección `Role` por `_id`. Retorna 404 si no existe.
 *
 * @export
 * @async
 * @param {Request} req - Params: `id`
 * @param {Response} res
 * @returns {unknown}
 */
export const getRole = async (req: Request, res: Response) => {
  const { id } = req.params;
  const role = await Role.findById(id);
  if (!role) {
    return res.status(404).json({ success: false, message: "Role not found" });
  }
  return res.status(200).json({ success: true, data: role });
};

/**
 * Edita un rol existente.
 *
 * Actualiza `name`, `description` y `modules` del rol identificado por `id`.
 * Retorna 400 si faltan campos obligatorios y 404 si el rol no existe.
 *
 * @export
 * @async
 * @param {Request} req - Params: `id`; Body: `name`, `description`, `modules`
 * @param {Response} res
 * @returns {unknown}
 */
export const editRole = async (req: Request, res: Response) => {
  const { id } = req.params;
  const { name, description, modules } = req.body;
  if (!name || !description || !modules) {
    return res
      .status(400)
      .json({
        success: false,
        message: " Name, description and modules are required",
        code: "field_missing",
      });
  }

  const role = await Role.findById(id);
  if (!role) {
    return res
      .status(404)
      .json({ success: false, message: "Rol no encontrado" });
  }
  role.name = name;
  role.description = description;
  role.modules = modules;
  await role.save();
  res.status(200).json({ success: true, message: "Rol editado con éxito" });
};

/**
 * Elimina un rol del sistema.
 *
 * Verifica que el rol exista y que no tenga administradores asociados antes
 * de eliminarlo. Retorna 404 si no existe y 400 si tiene usuarios vinculados.
 *
 * @export
 * @async
 * @param {Request} req - Params: `id`
 * @param {Response} res
 * @returns {unknown}
 */
export const deleteRole = async (req: Request, res: Response) => {
  const { id } = req.params;
  const role = await Role.findById(id);
  if (!role) {
    return res
      .status(404)
      .json({ success: false, message: "Rol no encontrado" });
  }

  const usersExist = await Admin.exists({ roles: { $in: [id] } });
  if (usersExist) {
    return res
      .status(400)
      .json({
        success: false,
        message: "Rol no puede eliminarse porque tiene usuarios asociados",
      });
  }

  await Role.findByIdAndDelete(id);
  res.status(200).json({ success: true, message: "Rol eliminado con éxito" });
};
