using System.Net;
using System.Net.Http.Json;
using NeonSprawl.Server.Game.AbilityInput;
using NeonSprawl.Server.Game.Targeting;
using NeonSprawl.Server.Tests;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.AbilityInput;

public sealed class CooldownSnapshotApiTests
{
    private static async Task BindSlot0PulseAsync(HttpClient client)
    {
        var post = await client.PostAsJsonAsync(
            "/game/players/dev-local-1/hotbar-loadout",
            new HotbarLoadoutUpdateRequest
            {
                SchemaVersion = HotbarLoadoutUpdateRequest.CurrentSchemaVersion,
                Slots = [new HotbarSlotBindingJson { SlotIndex = 0, AbilityId = PrototypeAbilityRegistry.PrototypePulse }],
            });
        post.EnsureSuccessStatusCode();
    }

    private static async Task LockPrototypeTargetAlphaAsync(HttpClient client)
    {
        var response = await client.PostAsJsonAsync(
            "/game/players/dev-local-1/target/select",
            new TargetSelectRequest
            {
                SchemaVersion = TargetSelectRequest.CurrentSchemaVersion,
                TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
            });
        response.EnsureSuccessStatusCode();
    }

    [Fact]
    public async Task GetCooldownSnapshot_ShouldReturnNotFound_WhenPlayerUnknown()
    {
        // Arrange
        await using var factory = new InMemoryWebApplicationFactory();
        var client = factory.CreateClient();

        // Act
        var response = await client.GetAsync("/game/players/missing-player/cooldown-snapshot");

        // Assert
        Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    }

    [Fact]
    public async Task GetCooldownSnapshot_ShouldReturnV1Schema_WithEightSlots_WhenPlayerKnown()
    {
        // Arrange
        await using var factory = new InMemoryWebApplicationFactory();
        var client = factory.CreateClient();

        // Act
        var response = await client.GetAsync("/game/players/dev-local-1/cooldown-snapshot");

        // Assert
        var body = await response.Content.ReadFromJsonAsync<CooldownSnapshotResponse>();
        Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        Assert.NotNull(body);
        Assert.Equal(CooldownSnapshotResponse.CurrentSchemaVersion, body!.SchemaVersion);
        Assert.Equal("dev-local-1", body.PlayerId);
        Assert.Equal(HotbarLoadoutResponse.SlotCountV1, body.Slots.Count);
        Assert.All(body.Slots, s => Assert.Null(s.CooldownEndsAtUtc));
    }

    [Fact]
    public async Task GetCooldownSnapshot_ShouldExposeSlotEnd_WhenCastAccepted_ThenClear_WhenAdvancedPastDuration()
    {
        // Arrange
        await using var factory = new InMemoryWebApplicationFactory();
        var client = factory.CreateClient();
        Assert.NotNull(factory.FakeClock);
        await BindSlot0PulseAsync(client);
        await LockPrototypeTargetAlphaAsync(client);
        var cast = new AbilityCastRequest
        {
            SchemaVersion = AbilityCastRequest.CurrentSchemaVersion,
            SlotIndex = 0,
            AbilityId = PrototypeAbilityRegistry.PrototypePulse,
            TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
        };

        // Act
        var castResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/ability-cast", cast);
        var snapDuring = await client.GetAsync("/game/players/dev-local-1/cooldown-snapshot");
        factory.FakeClock!.Advance(AbilityPrototypeCooldown.GlobalDuration + TimeSpan.FromMilliseconds(50));
        var snapAfter = await client.GetAsync("/game/players/dev-local-1/cooldown-snapshot");

        // Assert
        castResponse.EnsureSuccessStatusCode();
        var during = await snapDuring.Content.ReadFromJsonAsync<CooldownSnapshotResponse>();
        var after = await snapAfter.Content.ReadFromJsonAsync<CooldownSnapshotResponse>();
        Assert.NotNull(during);
        Assert.NotNull(after);
        var slot0During = during!.Slots.Single(s => s.SlotIndex == 0);
        Assert.NotNull(slot0During.CooldownEndsAtUtc);
        Assert.True(slot0During.CooldownEndsAtUtc > during.ServerTimeUtc);
        var slot0After = after!.Slots.Single(s => s.SlotIndex == 0);
        Assert.Null(slot0After.CooldownEndsAtUtc);
    }
}
