using Microsoft.Extensions.Logging.Abstractions;
using ModelContextProtocol.Server;
using Nexus.Api.Controllers;
using Nexus.Api.Data;
using Nexus.Api.Models;
using Nexus.Api.Services;
using Xunit;

namespace Nexus.Api.Tests;

public sealed class NexusMcpToolsTests
{
    [Fact]
    public void NexusMcpTools_RegistersExpectedToolNames()
    {
        var toolNames = typeof(NexusMcpTools)
            .GetMethods()
            .Select(method => method.GetCustomAttributes(typeof(McpServerToolAttribute), inherit: false)
                .OfType<McpServerToolAttribute>()
                .FirstOrDefault())
            .Where(attribute => attribute is not null)
            .Select(attribute => attribute!.Name ?? string.Empty)
            .Order()
            .ToArray();

        Assert.Equal(
        [
            "nexus_agent_overview",
            "nexus_append_activity",
            "nexus_create_child_task",
            "nexus_create_task",
            "nexus_get_activity",
            "nexus_get_agent_proposal",
            "nexus_get_board",
            "nexus_get_children",
            "nexus_get_task",
            "nexus_handoff",
            "nexus_propose_agent",
            "nexus_update_status"
        ], toolNames);
    }

    [Fact]
    public void NexusMcpTaskState_OnlyContainsCanonicalStates()
    {
        Assert.Equal(
        [
            nameof(NexusMcpTaskState.Backlog),
            nameof(NexusMcpTaskState.InProgress),
            nameof(NexusMcpTaskState.Blocked),
            nameof(NexusMcpTaskState.Done),
            nameof(NexusMcpTaskState.Review)
        ], Enum.GetNames<NexusMcpTaskState>());

        Assert.DoesNotContain("Delegated", Enum.GetNames<NexusMcpTaskState>());
    }

    [Fact]
    public async Task McpTools_ReadAndWrite_UseBridgeService()
    {
        await using var fixture = await TaskWorkflowFixture.CreateAsync();
        fixture.SetCallerAgent("iris");
        var tools = CreateTools(fixture);

        var createResult = await tools.CreateTask(
            title: "MCP parent",
            detail: "Created through MCP tool facade",
            priority: "High",
            assignedTo: "iris",
            ct: CancellationToken.None);

        Assert.True(createResult.Ok);
        Assert.NotNull(createResult.Data);
        Assert.Equal("MCP parent", createResult.Data!.Title);

        var activityResult = await tools.AppendActivity(
            createResult.Data.Id,
            "MCP checkpoint",
            "comment",
            CancellationToken.None);

        Assert.True(activityResult.Ok);
        Assert.Equal("MCP checkpoint", activityResult.Data!.Message);

        var statusResult = await tools.UpdateStatus(
            createResult.Data.Id,
            NexusMcpTaskState.InProgress,
            CancellationToken.None);

        Assert.True(statusResult.Ok);
        Assert.Equal(TaskStateHelper.ToStateString(TaskState.InProgress), statusResult.Data!.State);

        var board = await tools.GetBoard(CancellationToken.None);
        Assert.Contains(board.InProgress, task => task.Id == createResult.Data.Id);

        var taskResult = await tools.GetTask(createResult.Data.Id, CancellationToken.None);
        Assert.True(taskResult.Ok);
        Assert.Equal("MCP parent", taskResult.Data!.Title);
    }

    [Fact]
    public async Task McpTools_UpdateStatus_RejectsUnauthorizedAgent()
    {
        await using var fixture = await TaskWorkflowFixture.CreateAsync();
        var task = await fixture.TaskService.CreateDashboardTaskAsync(
            "Programmer cannot move",
            "State changes stay with Iris/Bao.",
            "iris",
            "Normal",
            "programmer",
            null,
            CancellationToken.None);

        fixture.SetCallerAgent("programmer");
        var tools = CreateTools(fixture);

        var result = await tools.UpdateStatus(task.Id, NexusMcpTaskState.Done, CancellationToken.None);

        Assert.False(result.Ok);
        Assert.Equal("nexus_update_status", result.Command);
        Assert.Contains("not authorized", result.Error, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public async Task McpTools_ServiceKey_ResolvesAsNexusSystem()
    {
        await using var fixture = await TaskWorkflowFixture.CreateAsync();
        fixture.HttpContextAccessor.HttpContext = TaskWorkflowFixture.CreateHttpContext(
            headers: new Dictionary<string, string>
            {
                ["X-Nexus-Api-Key"] = "test-service-key"
            });

        var tools = CreateTools(fixture);
        var result = await tools.CreateTask(
            title: "System MCP task",
            assignedTo: "iris",
            ct: CancellationToken.None);

        Assert.True(result.Ok);
        Assert.Equal("bao", result.Data!.Source);
    }

    private static NexusMcpTools CreateTools(TaskWorkflowFixture fixture)
        => new(
            fixture.TaskBridgeService,
            fixture.AgentService,
            fixture.HttpContextAccessor,
            fixture.Configuration,
            NullLogger<NexusMcpTools>.Instance);
}
