using System.Net;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

namespace Nalu.Tests;

public class McpServerTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _client;

    public McpServerTests(WebApplicationFactory<Program> factory)
    {
        _client = factory.CreateClient();
        _client.DefaultRequestHeaders.Add("Authorization", "Bearer nalu-test-key-001");
    }

    private async Task<JsonNode?> PostMcp(object body)
    {
        var json = JsonSerializer.Serialize(body);
        var response = await _client.PostAsync("/mcp",
            new StringContent(json, Encoding.UTF8, "application/json"));
        response.EnsureSuccessStatusCode();
        var text = await response.Content.ReadAsStringAsync();
        return JsonNode.Parse(text);
    }

    // ── Auth ──────────────────────────────────────────────────────────────────

    [Fact]
    public async Task Mcp_NoApiKey_Returns401()
    {
        using var client = new HttpClient { BaseAddress = _client.BaseAddress };
        var response = await client.PostAsync("/mcp",
            new StringContent("{}", Encoding.UTF8, "application/json"));
        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    // ── initialize ────────────────────────────────────────────────────────────

    [Fact]
    public async Task Mcp_Initialize_ReturnsServerInfo()
    {
        var result = await PostMcp(new
        {
            jsonrpc = "2.0",
            method = "initialize",
            @params = new { protocolVersion = "2024-11-05", capabilities = new { } },
            id = 1
        });

        result.Should().NotBeNull();
        result!["result"]!["serverInfo"]!["name"]!.GetValue<string>().Should().Be("nalu-ai");
        result["result"]!["protocolVersion"]!.GetValue<string>().Should().Be("2024-11-05");
        result["result"]!["capabilities"]!["tools"].Should().NotBeNull();
    }

    // ── tools/list ────────────────────────────────────────────────────────────

    [Fact]
    public async Task Mcp_ToolsList_ContainsAllValidators()
    {
        var result = await PostMcp(new
        {
            jsonrpc = "2.0",
            method = "tools/list",
            @params = new { },
            id = 2
        });

        var tools = result!["result"]!["tools"]!.AsArray();
        tools.Should().NotBeEmpty();

        var toolNames = tools.Select(t => t!["name"]!.GetValue<string>()).ToList();
        toolNames.Should().Contain("nalu_extract_name");
        toolNames.Should().Contain("nalu_extract_cpf");
        toolNames.Should().Contain("nalu_extract_cep");
        toolNames.Should().Contain("nalu_extract_phone");
        toolNames.Should().Contain("nalu_extract_email");
        toolNames.Should().Contain("nalu_extract_yes_no");
        toolNames.Should().Contain("nalu_extract_postal_code");
        // Universal — new
        toolNames.Should().Contain("nalu_extract_birthdate");
        toolNames.Should().Contain("nalu_extract_handoff");
        toolNames.Should().Contain("nalu_extract_cancel_intent");
        // Brasil — new
        toolNames.Should().Contain("nalu_extract_cnpj");
        toolNames.Should().Contain("nalu_extract_plate_br");
        toolNames.Should().Contain("nalu_extract_company_name");
    }

    [Fact]
    public async Task Mcp_ToolsList_EachToolHasInputSchema()
    {
        var result = await PostMcp(new
        {
            jsonrpc = "2.0",
            method = "tools/list",
            @params = new { },
            id = 3
        });

        var tools = result!["result"]!["tools"]!.AsArray();
        foreach (var tool in tools)
        {
            var schema = tool!["inputSchema"];
            schema.Should().NotBeNull($"tool {tool["name"]} should have inputSchema");
            schema!["properties"]!["agent_input"].Should().NotBeNull();
            schema["properties"]!["user_input"].Should().NotBeNull();
            var required = schema["required"]!.AsArray().Select(r => r!.GetValue<string>()).ToList();
            required.Should().Contain("agent_input");
            required.Should().Contain("user_input");
        }
    }

    // ── tools/call ────────────────────────────────────────────────────────────

    [Fact]
    public async Task Mcp_ToolsCall_ExtractName_WithGreeting_ObtainedFalse()
    {
        var result = await PostMcp(new
        {
            jsonrpc = "2.0",
            method = "tools/call",
            @params = new
            {
                name = "nalu_extract_name",
                arguments = new
                {
                    agent_input = "Qual seu nome completo?",
                    user_input = "Bom dia!"
                }
            },
            id = 4
        });

        result!["result"]!["isError"]!.GetValue<bool>().Should().BeFalse();

        var content = result["result"]!["content"]!.AsArray();
        content.Should().HaveCount(1);
        content[0]!["type"]!.GetValue<string>().Should().Be("text");

        var text = content[0]!["text"]!.GetValue<string>();
        var extracted = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(text)!;
        extracted["obtained"].GetBoolean().Should().BeFalse();
    }

    [Fact]
    public async Task Mcp_ToolsCall_UnknownTool_ReturnsError()
    {
        var result = await PostMcp(new
        {
            jsonrpc = "2.0",
            method = "tools/call",
            @params = new { name = "nalu_extract_unknown", arguments = new { } },
            id = 5
        });

        result!["error"].Should().NotBeNull();
        result["error"]!["code"]!.GetValue<int>().Should().Be(-32602);
    }

    // ── New .md file → auto-appears in tools/list ─────────────────────────────

    [Fact]
    public async Task Mcp_ToolsList_AllLoadedValidatorsHaveMcpTool()
    {
        // Any validator without mcp_tool should not appear in tools/list
        var result = await PostMcp(new
        {
            jsonrpc = "2.0",
            method = "tools/list",
            @params = new { },
            id = 6
        });

        var tools = result!["result"]!["tools"]!.AsArray();
        foreach (var tool in tools)
        {
            tool!["name"]!.GetValue<string>().Should().NotBeNullOrWhiteSpace();
            tool!["description"]!.GetValue<string>().Should().NotBeNullOrWhiteSpace();
        }
    }

    // ── notifications ─────────────────────────────────────────────────────────

    [Fact]
    public async Task Mcp_Initialized_Notification_Returns202()
    {
        var json = JsonSerializer.Serialize(new { jsonrpc = "2.0", method = "initialized" });
        var response = await _client.PostAsync("/mcp",
            new StringContent(json, Encoding.UTF8, "application/json"));
        response.StatusCode.Should().Be(HttpStatusCode.Accepted);
    }
}
