using ColaFlow.Modules.Mcp.Infrastructure.Middleware;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using NSubstitute;

namespace ColaFlow.Modules.Mcp.Tests.Infrastructure.Middleware;

/// <summary>
/// Unit tests for McpCorrelationIdMiddleware
/// </summary>
public class McpCorrelationIdMiddlewareTests
{
    [Fact]
    public async Task InvokeAsync_ShouldGenerateCorrelationId_WhenNotProvidedInRequest()
    {
        // Arrange
        var context = new DefaultHttpContext();
        var nextCalled = false;
        RequestDelegate next = (HttpContext ctx) =>
        {
            nextCalled = true;
            return Task.CompletedTask;
        };

        var middleware = new McpCorrelationIdMiddleware(next);

        // Act
        await middleware.InvokeAsync(context);

        // Assert
        nextCalled.Should().BeTrue();
        context.Items.Should().ContainKey("CorrelationId");
        context.Items["CorrelationId"].Should().NotBeNull();
        context.Items["CorrelationId"].Should().BeOfType<string>();

        var correlationId = context.Items["CorrelationId"] as string;
        correlationId.Should().NotBeNullOrWhiteSpace();

        // Should be a valid GUID without hyphens (format "N")
        Guid.TryParse(correlationId, out _).Should().BeTrue();
    }

    [Fact]
    public async Task InvokeAsync_ShouldUseProvidedCorrelationId_WhenPresentInRequestHeader()
    {
        // Arrange
        var expectedCorrelationId = Guid.NewGuid().ToString("N");
        var context = new DefaultHttpContext();
        context.Request.Headers["X-Correlation-Id"] = expectedCorrelationId;

        RequestDelegate next = (HttpContext ctx) => Task.CompletedTask;
        var middleware = new McpCorrelationIdMiddleware(next);

        // Act
        await middleware.InvokeAsync(context);

        // Assert
        context.Items["CorrelationId"].Should().Be(expectedCorrelationId);
    }

    [Fact]
    public async Task InvokeAsync_ShouldAddCorrelationIdToResponseHeader()
    {
        // Arrange
        var context = new DefaultHttpContext();
        context.Response.Body = new MemoryStream(); // Required for response headers

        RequestDelegate next = (HttpContext ctx) =>
        {
            // Trigger response to start (which adds headers)
            ctx.Response.StatusCode = 200;
            return Task.CompletedTask;
        };

        var middleware = new McpCorrelationIdMiddleware(next);

        // Act
        await middleware.InvokeAsync(context);

        // Assert - Note: Headers are added via OnStarting callback, which executes when response starts
        // In this test, we can verify the correlation ID was stored in context items
        context.Items.Should().ContainKey("CorrelationId");
    }

    [Fact]
    public async Task InvokeAsync_ShouldCallNextMiddleware()
    {
        // Arrange
        var context = new DefaultHttpContext();
        var nextCalled = false;

        RequestDelegate next = (HttpContext ctx) =>
        {
            nextCalled = true;
            return Task.CompletedTask;
        };

        var middleware = new McpCorrelationIdMiddleware(next);

        // Act
        await middleware.InvokeAsync(context);

        // Assert
        nextCalled.Should().BeTrue();
    }

    [Fact]
    public async Task InvokeAsync_ShouldGenerateUniqueCorrelationIds()
    {
        // Arrange
        var context1 = new DefaultHttpContext();
        var context2 = new DefaultHttpContext();

        RequestDelegate next = (HttpContext ctx) => Task.CompletedTask;
        var middleware = new McpCorrelationIdMiddleware(next);

        // Act
        await middleware.InvokeAsync(context1);
        await middleware.InvokeAsync(context2);

        // Assert
        var correlationId1 = context1.Items["CorrelationId"] as string;
        var correlationId2 = context2.Items["CorrelationId"] as string;

        correlationId1.Should().NotBeNullOrWhiteSpace();
        correlationId2.Should().NotBeNullOrWhiteSpace();
        correlationId1.Should().NotBe(correlationId2);
    }
}
