using System.Net;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using ColaFlow.Modules.Mcp.Contracts.JsonRpc;
using ColaFlow.Modules.Mcp.Contracts.Mcp;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;

namespace ColaFlow.IntegrationTests.Mcp;

/// <summary>
/// Integration tests for MCP Protocol endpoint
/// </summary>
public class McpProtocolIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _client;

    public McpProtocolIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task McpEndpoint_WithInitializeRequest_ReturnsSuccess()
    {
        // Arrange
        var request = new JsonRpcRequest
        {
            JsonRpc = "2.0",
            Method = "initialize",
            Params = new
            {
                protocolVersion = "1.0",
                clientInfo = new
                {
                    name = "Test Client",
                    version = "1.0.0"
                }
            },
            Id = 1
        };

        var json = JsonSerializer.Serialize(request, new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        });
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        response.Content.Headers.ContentType?.MediaType.Should().Be("application/json");

        var responseJson = await response.Content.ReadAsStringAsync();
        var rpcResponse = JsonSerializer.Deserialize<JsonRpcResponse>(responseJson);

        rpcResponse.Should().NotBeNull();
        rpcResponse!.JsonRpc.Should().Be("2.0");
        rpcResponse.Id.Should().NotBeNull();
        rpcResponse.Error.Should().BeNull();
        rpcResponse.Result.Should().NotBeNull();
    }

    [Fact]
    public async Task McpEndpoint_InitializeResponse_ContainsServerInfo()
    {
        // Arrange
        var request = new JsonRpcRequest
        {
            JsonRpc = "2.0",
            Method = "initialize",
            Params = new
            {
                protocolVersion = "1.0",
                clientInfo = new { name = "Test", version = "1.0" }
            },
            Id = 1
        };

        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);
        var responseJson = await response.Content.ReadAsStringAsync();
        var rpcResponse = JsonSerializer.Deserialize<JsonElement>(responseJson);

        // Assert
        rpcResponse.GetProperty("result").TryGetProperty("serverInfo", out var serverInfo).Should().BeTrue();
        serverInfo.GetProperty("name").GetString().Should().Be("ColaFlow MCP Server");
        serverInfo.GetProperty("version").GetString().Should().NotBeNullOrEmpty();
    }

    [Fact]
    public async Task McpEndpoint_InitializeResponse_ContainsCapabilities()
    {
        // Arrange
        var request = new JsonRpcRequest
        {
            JsonRpc = "2.0",
            Method = "initialize",
            Params = new
            {
                protocolVersion = "1.0",
                clientInfo = new { name = "Test", version = "1.0" }
            },
            Id = 1
        };

        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);
        var responseJson = await response.Content.ReadAsStringAsync();
        var rpcResponse = JsonSerializer.Deserialize<JsonElement>(responseJson);

        // Assert
        rpcResponse.GetProperty("result").TryGetProperty("capabilities", out var capabilities).Should().BeTrue();
        capabilities.TryGetProperty("resources", out var resources).Should().BeTrue();
        resources.GetProperty("supported").GetBoolean().Should().BeTrue();
        capabilities.TryGetProperty("tools", out var tools).Should().BeTrue();
        tools.GetProperty("supported").GetBoolean().Should().BeTrue();
        capabilities.TryGetProperty("prompts", out var prompts).Should().BeTrue();
        prompts.GetProperty("supported").GetBoolean().Should().BeTrue();
    }

    [Fact]
    public async Task McpEndpoint_WithInvalidJson_ReturnsParseError()
    {
        // Arrange
        var invalidJson = "{ invalid json }";
        var content = new StringContent(invalidJson, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var responseJson = await response.Content.ReadAsStringAsync();
        var rpcResponse = JsonSerializer.Deserialize<JsonRpcResponse>(responseJson);

        rpcResponse.Should().NotBeNull();
        rpcResponse!.Error.Should().NotBeNull();
        rpcResponse.Error!.Code.Should().Be((int)JsonRpcErrorCode.ParseError);
    }

    [Fact]
    public async Task McpEndpoint_WithUnknownMethod_ReturnsMethodNotFound()
    {
        // Arrange
        var request = new JsonRpcRequest
        {
            JsonRpc = "2.0",
            Method = "unknown_method",
            Id = 1
        };

        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);
        var responseJson = await response.Content.ReadAsStringAsync();
        var rpcResponse = JsonSerializer.Deserialize<JsonRpcResponse>(responseJson);

        // Assert
        rpcResponse.Should().NotBeNull();
        rpcResponse!.Error.Should().NotBeNull();
        rpcResponse.Error!.Code.Should().Be((int)JsonRpcErrorCode.MethodNotFound);
        rpcResponse.Error.Message.Should().Contain("unknown_method");
    }

    [Fact]
    public async Task McpEndpoint_WithResourcesList_ReturnsEmptyList()
    {
        // Arrange
        var request = new JsonRpcRequest
        {
            JsonRpc = "2.0",
            Method = "resources/list",
            Id = 1
        };

        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var responseJson = await response.Content.ReadAsStringAsync();
        var rpcResponse = JsonSerializer.Deserialize<JsonElement>(responseJson);

        rpcResponse.TryGetProperty("result", out var result).Should().BeTrue();
        result.TryGetProperty("resources", out var resources).Should().BeTrue();
        resources.GetArrayLength().Should().Be(0);
    }

    [Fact]
    public async Task McpEndpoint_WithToolsList_ReturnsEmptyList()
    {
        // Arrange
        var request = new JsonRpcRequest
        {
            JsonRpc = "2.0",
            Method = "tools/list",
            Id = 1
        };

        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var responseJson = await response.Content.ReadAsStringAsync();
        var rpcResponse = JsonSerializer.Deserialize<JsonElement>(responseJson);

        rpcResponse.TryGetProperty("result", out var result).Should().BeTrue();
        result.TryGetProperty("tools", out var tools).Should().BeTrue();
        tools.GetArrayLength().Should().Be(0);
    }

    [Fact]
    public async Task McpEndpoint_WithNotification_Returns204NoContent()
    {
        // Arrange - Notification has no "id" field
        var request = new
        {
            jsonrpc = "2.0",
            method = "notification_method",
            @params = new { test = "value" }
            // No "id" field = notification
        };

        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/mcp", content);

        // Assert
        // Notifications should return 204 or not return a response
        // For now, check that it doesn't fail
        response.StatusCode.Should().BeOneOf(HttpStatusCode.NoContent, HttpStatusCode.OK);
    }

    [Fact]
    public async Task McpEndpoint_ProtocolOverhead_IsLessThan5Milliseconds()
    {
        // Arrange
        var request = new JsonRpcRequest
        {
            JsonRpc = "2.0",
            Method = "initialize",
            Params = new
            {
                protocolVersion = "1.0",
                clientInfo = new { name = "Test", version = "1.0" }
            },
            Id = 1
        };

        var json = JsonSerializer.Serialize(request);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Warmup
        await _client.PostAsync("/mcp", content);

        // Act
        var stopwatch = System.Diagnostics.Stopwatch.StartNew();
        var response = await _client.PostAsync("/mcp", new StringContent(json, Encoding.UTF8, "application/json"));
        stopwatch.Stop();

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        // Note: This includes network overhead in tests, actual protocol overhead will be much less
        stopwatch.ElapsedMilliseconds.Should().BeLessThan(100); // Generous for integration test
    }
}
