import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { HAS_ANY_ROLES_KEY, HAS_ROLES_KEY } from './roles.decorator'
import { Role } from '../model/role.enum'

@Injectable()
export class RolesGuard implements CanActivate {
    constructor(private reflector: Reflector) {}

    canActivate(context: ExecutionContext): boolean {
        const classRoles = this.reflector.get<Role[]>(HAS_ROLES_KEY, context.getClass())
        const classAnyRoles = this.reflector.get<Role[]>(HAS_ANY_ROLES_KEY, context.getClass())

        const roles = this.reflector.get<Role[]>(HAS_ROLES_KEY, context.getHandler())
        const anyRoles = this.reflector.get<Role[]>(HAS_ANY_ROLES_KEY, context.getHandler())
        if (!classRoles && !classAnyRoles && !roles && !anyRoles) {
            return true
        }

        let result = true
        const request = context.switchToHttp().getRequest()
        const userRoles = request.user?.roles || []

        if (classRoles) {
            result = result && matchAll(classRoles, userRoles)
        }

        if (classAnyRoles) {
            result = result && matchAny(classAnyRoles, userRoles)
        }

        if (roles) {
            result = result && matchAll(roles, userRoles)
        }

        if (anyRoles) {
            result = result && matchAny(anyRoles, userRoles)
        }
        return result
    }
}

function matchAny(roles: Role[], userRoles: Role[]) {
    return roles.some((role) => userRoles.includes(role))
}

function matchAll(roles: Role[], userRoles: Role[]) {
    return roles.every((role) => userRoles.includes(role))
}
