using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

namespace ColaFlow.Modules.Mcp.Infrastructure.Middleware;

/// <summary>
/// Middleware that logs all MCP requests and responses
/// Includes performance timing and sensitive data sanitization
/// </summary>
public class McpLoggingMiddleware(
    RequestDelegate next,
    ILogger<McpLoggingMiddleware> logger)
{
    // Patterns for sanitizing sensitive data
    private static readonly Regex ApiKeyHashPattern = new(@"""keyHash"":\s*""[^""]+""", RegexOptions.Compiled);
    private static readonly Regex ApiKeyPattern = new(@"""apiKey"":\s*""[^""]+""", RegexOptions.Compiled);
    private static readonly Regex PasswordPattern = new(@"""password"":\s*""[^""]+""", RegexOptions.Compiled);

    public async Task InvokeAsync(HttpContext context)
    {
        // Only log MCP requests (POST to /mcp endpoint)
        if (!IsMcpRequest(context))
        {
            await next(context);
            return;
        }

        var correlationId = context.Items["CorrelationId"] as string;
        var stopwatch = Stopwatch.StartNew();

        // Enable request buffering so we can read the body multiple times
        context.Request.EnableBuffering();

        // Log request
        await LogRequestAsync(context, correlationId);

        // Capture response
        var originalBodyStream = context.Response.Body;
        using var responseBodyStream = new MemoryStream();
        context.Response.Body = responseBodyStream;

        try
        {
            // Execute the rest of the pipeline
            await next(context);

            stopwatch.Stop();

            // Log response
            await LogResponseAsync(context, correlationId, stopwatch.ElapsedMilliseconds);

            // Copy response to original stream
            responseBodyStream.Seek(0, SeekOrigin.Begin);
            await responseBodyStream.CopyToAsync(originalBodyStream);
        }
        finally
        {
            context.Response.Body = originalBodyStream;
        }
    }

    /// <summary>
    /// Checks if this is an MCP request
    /// </summary>
    private static bool IsMcpRequest(HttpContext context)
    {
        return context.Request.Method == "POST"
            && context.Request.Path.StartsWithSegments("/mcp");
    }

    /// <summary>
    /// Logs the incoming MCP request
    /// </summary>
    private async Task LogRequestAsync(HttpContext context, string? correlationId)
    {
        context.Request.Body.Position = 0;
        var bodyText = await new StreamReader(context.Request.Body, Encoding.UTF8).ReadToEndAsync();
        context.Request.Body.Position = 0; // Reset for next middleware

        var tenantId = context.Items["TenantId"];
        var apiKeyId = context.Items["ApiKeyId"];
        var userId = context.Items["UserId"];

        // Sanitize sensitive data before logging
        var sanitizedBody = SanitizeSensitiveData(bodyText);

        logger.LogDebug(
            "MCP Request | Method: {Method} | Path: {Path} | CorrelationId: {CorrelationId} | " +
            "TenantId: {TenantId} | ApiKeyId: {ApiKeyId} | UserId: {UserId}\nBody: {Body}",
            context.Request.Method,
            context.Request.Path,
            correlationId,
            tenantId,
            apiKeyId,
            userId,
            sanitizedBody);
    }

    /// <summary>
    /// Logs the outgoing MCP response
    /// </summary>
    private async Task LogResponseAsync(
        HttpContext context,
        string? correlationId,
        long elapsedMs)
    {
        context.Response.Body.Position = 0;
        var bodyText = await new StreamReader(context.Response.Body, Encoding.UTF8).ReadToEndAsync();
        context.Response.Body.Position = 0;

        var statusCode = context.Response.StatusCode;
        var logLevel = statusCode >= 400 ? LogLevel.Error : LogLevel.Debug;

        logger.Log(logLevel,
            "MCP Response | StatusCode: {StatusCode} | CorrelationId: {CorrelationId} | " +
            "Duration: {Duration}ms\nBody: {Body}",
            statusCode,
            correlationId,
            elapsedMs,
            bodyText);

        // Also log performance metrics
        if (elapsedMs > 1000) // Log slow requests (> 1 second)
        {
            logger.LogWarning(
                "Slow MCP Request | CorrelationId: {CorrelationId} | Duration: {Duration}ms",
                correlationId,
                elapsedMs);
        }
    }

    /// <summary>
    /// Sanitizes sensitive data from request/response bodies
    /// IMPORTANT: Prevents logging API keys, passwords, etc.
    /// </summary>
    private static string SanitizeSensitiveData(string body)
    {
        if (string.IsNullOrWhiteSpace(body))
            return body;

        // Replace sensitive fields with [REDACTED]
        var sanitized = body;
        sanitized = ApiKeyHashPattern.Replace(sanitized, "\"keyHash\":\"[REDACTED]\"");
        sanitized = ApiKeyPattern.Replace(sanitized, "\"apiKey\":\"[REDACTED]\"");
        sanitized = PasswordPattern.Replace(sanitized, "\"password\":\"[REDACTED]\"");

        return sanitized;
    }
}
