using Microsoft.Extensions.Primitives;

namespace ZB.MOM.WW.MxGateway.Server.Diagnostics;

/// <summary>Middleware extensions for structured gateway request logging with correlation context.</summary>
public static class GatewayRequestLoggingMiddlewareExtensions
{
    /// <summary>Header name for the session ID.</summary>
    public const string SessionIdHeaderName = "x-session-id";

    /// <summary>Header name for the worker process ID.</summary>
    public const string WorkerProcessIdHeaderName = "x-worker-process-id";

    /// <summary>Header name for the correlation ID.</summary>
    public const string CorrelationIdHeaderName = "x-correlation-id";

    /// <summary>Header name for the command method name.</summary>
    public const string CommandMethodHeaderName = "x-command-method";

    /// <summary>Adds gateway request logging scope middleware that reads correlation headers and redacts sensitive data.</summary>
    /// <param name="app">Application builder.</param>
    /// <returns>The same <see cref="IApplicationBuilder"/>, for chaining.</returns>
    public static IApplicationBuilder UseGatewayRequestLoggingScope(this IApplicationBuilder app)
    {
        ArgumentNullException.ThrowIfNull(app);

        // Resolved once at registration: the logger is keyed by category, not by request, so the
        // per-request DI resolve and logger-factory lock bought nothing.
        ILogger logger = app.ApplicationServices
            .GetRequiredService<ILoggerFactory>()
            .CreateLogger("MxGateway.Request");

        return app.Use(async (context, next) =>
        {
            // Scope construction is deliberately unconditional: gating it on IsEnabled would drop
            // scope state for providers (and scope consumers) registered after startup.
            using IDisposable? scope = logger.BeginGatewayScope(new GatewayLogScope(
                SessionId: ReadHeader(context, SessionIdHeaderName),
                WorkerProcessId: ReadInt32Header(context, WorkerProcessIdHeaderName),
                CorrelationId: ReadUInt64Header(context, CorrelationIdHeaderName),
                CommandMethod: ReadHeader(context, CommandMethodHeaderName),
                ClientIdentity: ReadHeader(context, "authorization")));

            await next(context);
        });
    }

    private static string? ReadHeader(HttpContext context, string headerName)
    {
        return context.Request.Headers.TryGetValue(headerName, out StringValues values)
            ? values.ToString()
            : null;
    }

    private static int? ReadInt32Header(HttpContext context, string headerName)
    {
        string? value = ReadHeader(context, headerName);

        return int.TryParse(value, out int parsedValue)
            ? parsedValue
            : null;
    }

    private static ulong? ReadUInt64Header(HttpContext context, string headerName)
    {
        string? value = ReadHeader(context, headerName);

        return ulong.TryParse(value, out ulong parsedValue)
            ? parsedValue
            : null;
    }
}
