import { Request, Response } from "express";
import UsersGroup from "../models/UsersGroup";
import Joi from "joi";
import mongoose from "mongoose";

/** Validador Joi para un ObjectId de MongoDB (string hexadecimal de 24 caracteres). */
const objectId = () => Joi.string().length(24).hex();

/**
 * Schema de validación Joi para crear o actualizar un grupo de usuarios.
 * - `title`: requerido, sin espacios al inicio/fin.
 * - `description`: opcional, acepta null o string vacío.
 * - `users`: array de ObjectIds válidos; por defecto array vacío.
 */
const groupSchema = Joi.object({
  title: Joi.string().trim().required(),
  description: Joi.string().allow(null, "").optional(),
  users: Joi.array().items(objectId()).default([]),
});

/**
 * Crea un nuevo grupo de usuarios.
 *
 * Valida el body con `groupSchema`. Los IDs de usuario duplicados se eliminan
 * usando `Set` antes de persistir el grupo.
 *
 * @export
 * @async
 * @param {Request} req - Body: `title`, `description?`, `users?` (array de ObjectIds)
 * @param {Response} res
 * @returns {unknown}
 */
export async function createGroup(req: Request, res: Response) {
  const { error, value } = groupSchema.validate(req.body, {
    abortEarly: false,
  });
  if (error)
    return res.status(400).json({ success: false, message: error.message });

  const uniqueUsers = Array.from(new Set(value.users));
  const group = await UsersGroup.create({ ...value, users: uniqueUsers });
  return res
    .status(201)
    .json({ success: true, message: "Group created", data: group });
}

/**
 * Retorna la lista paginada de grupos de usuarios con soporte de búsqueda por título.
 *
 * Usa `$facet` en la aggregation para obtener en una sola consulta:
 * - `data`: grupos de la página actual, ordenados por `createdAt` descendente.
 * - `pageInfo`: total de registros para calcular la paginación en el cliente.
 *
 * @export
 * @async
 * @param {Request} req - Query: `page?` (default: 1), `size?` (default: 10), `search?` (filtro por título, insensible a mayúsculas)
 * @param {Response} res
 * @returns {unknown}
 */
export async function listGroups(req: Request, res: Response) {
  const page = Math.max(parseInt(String(req.query.page || 1), 10), 1);
  const size = Math.max(parseInt(String(req.query.size || 10), 10), 1);
  const search = String(req.query.search || "").trim();

  const filter: any = {};
  if (search) filter.title = { $regex: search, $options: "i" };

  const agg = await UsersGroup.aggregate([
    { $match: filter },
    {
      $facet: {
        data: [
          { $sort: { createdAt: -1, _id: 1 } },
          { $skip: (page - 1) * size },
          { $limit: size },
        ],
        pageInfo: [{ $count: "totalRecords" }],
      },
    },
  ]);

  return res
    .status(200)
    .json({ success: true, message: "Groups retrieved", data: agg[0] });
}

/**
 * Retorna un grupo de usuarios por su ID.
 *
 * Valida que el `id` sea un ObjectId válido de MongoDB antes de consultar.
 *
 * @export
 * @async
 * @param {Request} req - Params: `id` (ObjectId del grupo)
 * @param {Response} res
 * @returns {unknown}
 */
export async function getGroup(req: Request, res: Response) {
  const { id } = req.params;
  if (!mongoose.isValidObjectId(id))
    return res.status(400).json({ success: false, message: "Invalid id" });
  const group = await UsersGroup.findById(id);
  if (!group)
    return res.status(404).json({ success: false, message: "Group not found" });
  return res
    .status(200)
    .json({ success: true, message: "Group retrieved", data: group });
}

/**
 * Actualiza un grupo de usuarios existente.
 *
 * Valida el `id` como ObjectId y el body con `groupSchema`. Los IDs de usuario
 * duplicados se eliminan con `Set`. Retorna el documento actualizado (`new: true`).
 *
 * @export
 * @async
 * @param {Request} req - Params: `id`; Body: `title`, `description?`, `users?`
 * @param {Response} res
 * @returns {unknown}
 */
export async function updateGroup(req: Request, res: Response) {
  const { id } = req.params;
  if (!mongoose.isValidObjectId(id))
    return res.status(400).json({ success: false, message: "Invalid id" });

  const { error, value } = groupSchema.validate(req.body, {
    abortEarly: false,
  });
  if (error)
    return res.status(400).json({ success: false, message: error.message });

  const uniqueUsers = Array.from(new Set(value.users));
  const group = await UsersGroup.findByIdAndUpdate(
    id,
    { ...value, users: uniqueUsers },
    { new: true },
  );
  if (!group)
    return res.status(404).json({ success: false, message: "Group not found" });
  return res
    .status(200)
    .json({ success: true, message: "Group updated", data: group });
}

/**
 * Elimina un grupo de usuarios por su ID (hard delete).
 *
 * Valida que el `id` sea un ObjectId válido antes de intentar la eliminación.
 * Si el grupo no existe, responde con 404.
 *
 * @export
 * @async
 * @param {Request} req - Params: `id` (ObjectId del grupo)
 * @param {Response} res
 * @returns {unknown}
 */
export async function deleteGroup(req: Request, res: Response) {
  const { id } = req.params;
  if (!mongoose.isValidObjectId(id))
    return res.status(400).json({ success: false, message: "Invalid id" });
  const resp = await UsersGroup.findByIdAndDelete(id);
  if (!resp)
    return res.status(404).json({ success: false, message: "Group not found" });
  return res.status(200).json({ success: true, message: "Group deleted" });
}
