using CMSMicroservice.Application.Common.Authorization;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.WebApi.Interceptors;

/// <summary>
/// gRPC Interceptor برای بررسی مجوز دسترسی
/// بر اساس [RequiresPermission] attribute روی سرویس‌ها/متدها
/// </summary>
public class PermissionInterceptor : Interceptor
{
    private readonly IPermissionService _permissionService;
    private readonly ILogger<PermissionInterceptor> _logger;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public PermissionInterceptor(
        IPermissionService permissionService,
        ILogger<PermissionInterceptor> logger,
        IHttpContextAccessor httpContextAccessor)
    {
        _permissionService = permissionService;
        _logger = logger;
        _httpContextAccessor = httpContextAccessor;
    }

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request, ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        await EnsureHasPermissionAsync(context);
        return await continuation(request, context);
    }

    public override async Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(
        IAsyncStreamReader<TRequest> requestStream, ServerCallContext context,
        ClientStreamingServerMethod<TRequest, TResponse> continuation)
    {
        await EnsureHasPermissionAsync(context);
        return await continuation(requestStream, context);
    }

    private async Task EnsureHasPermissionAsync(ServerCallContext context)
    {
        var httpContext = context.GetHttpContext() ?? _httpContextAccessor.HttpContext;
        if (httpContext == null) return;

        var endpoint = httpContext.GetEndpoint();
        if (endpoint == null) return;

        var permissionAttributes = endpoint.Metadata.GetOrderedMetadata<RequiresPermissionAttribute>();
        if (permissionAttributes == null || permissionAttributes.Count == 0) return;

        foreach (var attribute in permissionAttributes)
        {
            var hasPermission = await _permissionService.HasPermissionAsync(
                attribute.Permission, httpContext.RequestAborted);

            if (!hasPermission)
            {
                _logger.LogWarning(
                    "Permission denied: {Permission} for method {Method}",
                    attribute.Permission, context.Method);

                throw new RpcException(new Status(
                    StatusCode.PermissionDenied,
                    $"شما مجوز دسترسی به این عملیات را ندارید ({attribute.Permission})"));
            }
        }
    }
}
