using Google.Protobuf;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Text.RegularExpressions;
using CMSMicroservice.Application.Common.Interfaces;

namespace CMSMicroservice.WebApi.Common.Behaviours;

public partial class PerformanceBehaviour : Interceptor
{
    private readonly Stopwatch _timer;
    private readonly ILogger _logger;
    private readonly ICurrentUserService _currentUserService;

    [GeneratedRegex(@"""(File|ImageFile|image_file|file)"":\s*\{[^}]*\}", RegexOptions.Singleline)]
    private static partial Regex BinaryFieldPattern();

    public PerformanceBehaviour(ILogger<PerformanceBehaviour> logger, ICurrentUserService currentUserService)
    {
        _timer = new Stopwatch();
        _logger = logger;
        _currentUserService = currentUserService;
    }

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        _timer.Start();

        var response = await continuation(request, context);

        _timer.Stop();

        var elapsedMilliseconds = _timer.ElapsedMilliseconds;

        if (elapsedMilliseconds > 500)
        {
            var requestName = typeof(TRequest).Name;
            var userId = _currentUserService.UserId ?? string.Empty;
            var safeLog = SanitizeForLog(request);

            _logger.LogWarning("gRPC Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds) {UserId} {Request}",
                requestName, elapsedMilliseconds, userId, safeLog);
        }
        return response;
    }

    private static string SanitizeForLog<T>(T request)
    {
        if (request is IMessage protoMessage)
        {
            var json = JsonFormatter.Default.Format(protoMessage);
            json = BinaryFieldPattern().Replace(json, "\"$1\": \"[BINARY DATA]\"");
            if (json.Length > 2000)
                return json[..2000] + "... [TRUNCATED]";
            return json;
        }
        return request?.ToString() ?? "";
    }
}

