import { AuthUser } from "@/contexts/auth/domain";
import { ICommonContext } from "@/contexts/common/infrastructure";
import { generateExpressError } from "@/contexts/common/infrastructure/express";
import { ensureIdIsValid } from "@shared/contexts";
import { NextFunction, Request, Response } from "express";
import httpStatus from "http-status";
import passport from "passport";

// Extender el Request de Express para incluir el usuario autenticado optionalmente
interface AuthenticatedRequest extends Request {
  user?: AuthUser;
}

// Middleware para autenticar usando passport con el local-jwt strategy
const authenticateJwt = passport.authenticate("local-jwt", { session: false });

// Para establecer el contexto de autenticación
const setAuthContext = (req: AuthenticatedRequest, res: Response, user: AuthUser) => {
  const { context } = res.locals || {};
  res.locals.context = {
    ...context,
    user,
  } as ICommonContext;
};

// Comprueba el rol del usuario
const authorizeUser = (condition: (user: AuthUser) => boolean) => {
  return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    const user = req.user as AuthUser;
    if (!user || !condition(user)) {
      generateExpressError(req, res, httpStatus.UNAUTHORIZED);
    }

    setAuthContext(req, res, user);
    next();
  };
};

// Verifica que el usuario esté autenticado
export const checkUser = [authenticateJwt, authorizeUser((user) => user.isUser)];

// Verifica que el usuario sea administrador
export const checkIsAdmin = [authenticateJwt, authorizeUser((user) => user.isAdmin)];

// Middleware para verificar que el usuario sea administrador o el dueño de los datos (self)
export const checkAdminOrSelf = [
  authenticateJwt,
  (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
    const user = req.user as AuthUser;
    const { userId } = req.params;

    // Si el usuario es admin, está autorizado
    if (user.isAdmin) {
      setAuthContext(req, res, user);
      next();
    }

    // Si el usuario es sí mismo
    if (user.isUser && userId) {
      const paramIdOrError = ensureIdIsValid(userId);
      if (paramIdOrError.isSuccess && user.id.equals(paramIdOrError.object)) {
        setAuthContext(req, res, user);
        next();
      }
    }

    generateExpressError(req, res, httpStatus.UNAUTHORIZED);
  },
];
