namespace Indotalent.ConfigBackEnd.Behaviours;

using Indotalent.ConfigBackEnd.Attributes;
using Indotalent.ConfigBackEnd.Exceptions;
using Indotalent.ConfigBackEnd.Interfaces;
using MediatR;
using System.Reflection;

public class AuthorizationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly ICurrentUserService _currentUserService;

    public AuthorizationBehaviour(ICurrentUserService currentUserService)
    {
        _currentUserService = currentUserService;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        var authorizeAttributes = request.GetType().GetCustomAttributes<AuthorizeAttribute>();

        if (authorizeAttributes.Any())
        {
            if (_currentUserService.UserId == null)
            {
                throw new UnauthorizedAccessException();
            }

            var authorizeAttributesWithRoles = authorizeAttributes.Where(a => !string.IsNullOrWhiteSpace(a.Role));

            if (authorizeAttributesWithRoles.Any())
            {
                var authorized = false;
                foreach (var role in authorizeAttributesWithRoles.Select(a => a.Role))
                {
                    var isInRole = await _currentUserService.IsInRoleAsync(role.Trim());
                    if (isInRole)
                    {
                        authorized = true;
                        break;
                    }
                }

                if (!authorized)
                {
                    throw new ForbiddenAccessException();
                }
            }

            var authorizeAttributesWithPermissions = authorizeAttributes.Where(a => !string.IsNullOrWhiteSpace(a.Permission));

            if (authorizeAttributesWithPermissions.Any())
            {
                foreach (var permission in authorizeAttributesWithPermissions.Select(a => a.Permission))
                {
                    var hasPermission = await _currentUserService.HasPermissionAsync(permission.Trim());
                    if (!hasPermission)
                    {
                        throw new ForbiddenAccessException();
                    }
                }
            }
        }

        return await next();
    }
}