import { Request, Response } from 'express';
import * as jwt from 'jsonwebtoken';
import { Role } from '../../model/role.model';

/**
 * Verifies if a valid auth token is given, before continuing with route. Returns HTTP 401 if token is invalid, continues with [next] otherwise.
 * Also maps [userEmail] to [req], to be used by other Controllers for authorization.
 * @param req Incoming request.
 * @param res Outgoing response.
 * @param next Callback funtion of middleware, usually routing logic.
 * @returns A 401 Response or void (continue with [next] callback).
 */
export function authenticateUser(req: Request, res: Response, next: CallableFunction) : Response|void {
    // Get token from header
    const token = req.header('Authorization');
    if (token == null) return res.status(401).json({ error: 'Authentication failed.' });

    // Verify token
    try {
        jwt.verify(token, process.env.JWTSECRET);
    } catch (error) {
        console.log(error);
        return res.status(401).json({ error: 'Invalid token.' });
    }

    // Save user data w/ request for authorization checks
    const jwtJson = jwt.decode(token, {json: true});
    req.userToken = token;
    req.userEmail = jwtJson?.email;
    req.userRole = jwtJson?.role;
    req.userAuthenticated = true;
    
    next();
 };

 /**
  * Ensures that a user can only access the route, if user has one of the [allowedRoles] assigned.
  * @param allowedRoles List of roles, that are valid to access the given route. If user does have one of these roles, the user may continue.
  * @returns A 401 Response or void (continue with [next] callback).
  */
 export function authorizeUser(allowedRoles: Array<Role>) {
    return (req: Request, res: Response, next: CallableFunction) => {
        // Ensure user is authenticated first
        if (! req.userAuthenticated) return res.status(401).json({ error: 'Authentication failed.' });

        // Ensure user has one of the allowedRoles
        const userRole : Role = req.userRole as Role;
        const hasNecessaryRole = allowedRoles.includes(userRole);
        if (! hasNecessaryRole) return res.status(401).json({ error: 'Authorization failed' });

        next();
    }
 }