import { NextFunction, Request, Response } from "express";
import { ForbiddenError } from "../api/errors/httpErrors.js";
import { HttpStatusCode } from "../api/apiHelpers/HttpStatusCodes.js";
import { UserRole } from "../api/enums/UserRole.js";

/**
 * Middleware to check if the current user has one of the required roles
 * Must be used after the authentication middleware
 *
 * @param allowedRoles Array of roles that are allowed to access the route
 * @returns Express middleware function
 *
 * @example
 * router.get("/admin-only", requireRole([UserRole.ADMIN]), asyncHandler(async (req, res) => { ... }));
 * router.post("/admin-or-moderator", requireRole([UserRole.ADMIN, UserRole.MODERATOR]), asyncHandler(async (req, res) => { ... }));
 */
export const requireRole = (allowedRoles: UserRole[]) => {
    return (req: Request, res: Response, next: NextFunction) => {
        // Check if user is authenticated
        if (!req.currentUser) {
            return res.status(HttpStatusCode.UNAUTHORIZED).json({ message: "Unauthorized" });
        }

        // Get user's role from the auth payload
        const userRole = req.currentUser.role;

        // Check if user has one of the allowed roles
        if (!userRole || !allowedRoles.includes(userRole)) {
            throw new ForbiddenError(
                `Access denied. Required role: ${allowedRoles.join(" or ")}`
            );
        }

        // User has required role, proceed
        next();
    };
};

/**
 * Convenience middleware for admin-only routes
 *
 * @example
 * router.get("/admin-panel", requireAdmin, asyncHandler(async (req, res) => { ... }));
 */
export const requireAdmin = requireRole([UserRole.ADMIN]);

/**
 * Middleware to check if user is either an admin or the owner of the resource
 * Must be used after the authentication middleware
 *
 * @param getUserIdFromRequest Function to extract the target user ID from the request
 * @returns Express middleware function
 *
 * @example
 * router.put("/:userId", requireAdminOrOwner(req => req.params.userId), asyncHandler(...));
 */
export const requireAdminOrOwner = (
    getUserIdFromRequest: (req: Request) => string | undefined
) => {
    return (req: Request, res: Response, next: NextFunction) => {
        // Check if user is authenticated
        if (!req.currentUser) {
            return res.status(HttpStatusCode.UNAUTHORIZED).json({ message: "Unauthorized" });
        }

        const currentUserId = req.currentUser.id;
        const currentUserRole = req.currentUser.role;
        const targetUserId = getUserIdFromRequest(req);

        // Allow if user is admin
        if (currentUserRole === UserRole.ADMIN) {
            return next();
        }

        // Allow if user is accessing their own resource
        if (targetUserId && currentUserId === targetUserId) {
            return next();
        }

        // Deny access
        throw new ForbiddenError(
            "Access denied. You can only modify your own data."
        );
    };
};

/**
 * Middleware for routes where the user ID is in the request body
 * Common for update operations
 */
export const requireAdminOrSelf = requireAdminOrOwner(req => req.body.id || req.body.userId);