import express           from 'express';
import { StatusCodes }   from 'http-status-codes';
import SecurityCheckType from '../types/SecurityCheckType.js';
import logger            from '../shared/logging/WinstonLogger.js';
import AssignmentManager from '../managers/AssignmentManager.js';


class SecurityMiddleware {
    private checkIfConnected(checkIfConnected: boolean, req: express.Request): boolean {
        return !checkIfConnected || (req.session.profile !== null && req.session.profile !== undefined);
    }

    private async checkType(checkType: SecurityCheckType, req: express.Request): Promise<boolean> {
        try {
            switch ( String(checkType) ) {
                case SecurityCheckType.ADMIN.valueOf():
                    return req.session.profile.isAdmin;
                case SecurityCheckType.TEACHING_STAFF.valueOf():
                    return req.session.profile.isTeachingStaff;
                case SecurityCheckType.ASSIGNMENT_STAFF.valueOf():
                    return await AssignmentManager.isUserAllowedToAccessAssignment(req.boundParams.assignment!, req.session.profile);
                case SecurityCheckType.ASSIGNMENT_IS_PUBLISHED.valueOf():
                    return req.boundParams.assignment?.published ?? false;
                case SecurityCheckType.EXERCISE_SECRET.valueOf():
                    return (req.headers.exercisesecret as string | undefined) === req.boundParams.exercise!.secret;
                default:
                    return false;
            }
        } catch ( e ) {
            logger.error('Security check failed !!! => ' + JSON.stringify(e));
            return false;
        }
    }

    // First check if connected then check if at least ONE rule match. It's NOT an AND but it's a OR function.
    check(checkIfConnected: boolean, ...checkTypes: Array<SecurityCheckType>): (req: express.Request, res: express.Response, next: express.NextFunction) => void {
        return (req: express.Request, res: express.Response, next: express.NextFunction) => {
            if ( !this.checkIfConnected(checkIfConnected, req) ) {
                return req.session.sendResponse(res, StatusCodes.UNAUTHORIZED);
            }

            const isAllowed: boolean = checkTypes.length === 0 ? true : checkTypes.find(async checkType => this.checkType(checkType, req)) !== undefined;

            if ( !isAllowed ) {
                return req.session.sendResponse(res, StatusCodes.FORBIDDEN);
            }

            return next();
        };
    }
}


export default new SecurityMiddleware();