// filepath: /media/masoud/Project/FourSat/CMS/src/CMSMicroservice.WebApi/Common/Behaviours/ExceptionHandlingBehaviour.cs
using System.Text.Json;
using System.Collections.Generic;
using CMSMicroservice.Application.Common.Exceptions;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;

namespace CMSMicroservice.WebApi.Common.Behaviours;

public class ExceptionHandlingBehaviour : Interceptor
{
    private readonly ILogger<ExceptionHandlingBehaviour> _logger;

    public ExceptionHandlingBehaviour(ILogger<ExceptionHandlingBehaviour> logger)
    {
        _logger = logger;
    }

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        try
        {
            return await continuation(request, context);
        }
        catch (ValidationException vex)
        {
            // Flatten validation errors into a trailer so clients can display them
            var metadata = new Metadata();
            string description = vex.Message;
            try
            {
                if (vex.Errors is { Count: > 0 })
                {
                    var payload = JsonSerializer.Serialize(vex.Errors);
                    metadata.Add("validation-errors-text", payload);

                    // Build a human-friendly description out of individual messages
                    var parts = new List<string>();
                    foreach (var kv in vex.Errors)
                    {
                        foreach (var msg in kv.Value)
                        {
                            if (!string.IsNullOrWhiteSpace(kv.Key))
                                parts.Add($"{kv.Key}: {msg}");
                            else
                                parts.Add(msg);
                        }
                    }
                    if (parts.Count > 0)
                    {
                        description = string.Join(" | ", parts);
                    }
                }
            }
            catch
            {
                // ignore serialization issues, still return InvalidArgument
            }

            _logger.LogWarning(vex, "Validation failed for request {Method}", context.Method);
            throw new RpcException(new Status(StatusCode.InvalidArgument, description), metadata);
        }
        catch (NotFoundException nfex)
        {
            _logger.LogInformation(nfex, "Entity not found for request {Method}", context.Method);
            throw new RpcException(new Status(StatusCode.NotFound, nfex.Message));
        }
        catch (UnauthorizedAccessException uaex)
        {
            _logger.LogInformation(uaex, "Unauthorized access for request {Method}", context.Method);
            throw new RpcException(new Status(StatusCode.Unauthenticated, uaex.Message));
        }
        catch (ForbiddenAccessException fax)
        {
            _logger.LogInformation(fax, "Forbidden access for request {Method}", context.Method);
            throw new RpcException(new Status(StatusCode.PermissionDenied, fax.Message));
        }
        catch (DuplicateException dex)
        {
            _logger.LogInformation(dex, "Duplicate resource for request {Method}", context.Method);
            throw new RpcException(new Status(StatusCode.AlreadyExists, dex.Message));
        }
        catch (ArgumentException aex)
        {
            _logger.LogInformation(aex, "Invalid argument for request {Method}", context.Method);
            throw new RpcException(new Status(StatusCode.InvalidArgument, aex.Message));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception for request {Method}", context.Method);
            // Hide internal details from clients
            throw new RpcException(new Status(StatusCode.Internal, "Internal server error"));
        }
    }
}
