using System.Net;
using System.Net.Http.Json;
using System.Text;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.PositionState;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.PositionState;

public sealed class MoveStreamApiTests
{
    [Fact]
    public async Task PostMoveStream_ShouldApplyChainAndIncrementSequence_WhenTwoSmallSteps()
    {
        // Arrange

        await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b =>
        {
            b.ConfigureTestServices(services =>
            {
                services.PostConfigure<GamePositionOptions>(o =>
                {
                    o.MovementValidation.HorizontalStepEnabled = true;
                    o.MovementValidation.MaxHorizontalStep = 1.0;
                });
            });
        });
        var client = factory.CreateClient();
        var before = await client.GetFromJsonAsync<PositionStateResponse>("/game/players/dev-local-1/position");
        var body = new MoveStreamRequest
        {
            SchemaVersion = MoveStreamRequest.CurrentSchemaVersion,
            Targets =
            [
                new PositionVector { X = -4.9, Y = 0.9, Z = -5.0 },
                new PositionVector { X = -4.8, Y = 0.9, Z = -5.0 },
            ],
        };

        // Act
        var postResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/move-stream", body);

        // Assert
        Assert.NotNull(before);
        var seq0 = before!.Sequence;
        var postBody = await postResponse.Content.ReadFromJsonAsync<PositionStateResponse>();
        Assert.Equal(HttpStatusCode.OK, postResponse.StatusCode);
        Assert.NotNull(postBody);
        Assert.Equal(seq0 + 2, postBody!.Sequence);
        Assert.Equal(-4.8, postBody.Position.X);
        Assert.Equal(0.9, postBody.Position.Y);
        Assert.Equal(-5.0, postBody.Position.Z);
    }

    [Fact]
    public async Task PostMoveStream_ShouldReturnNotFound_WhenPlayerIsUnknown()
    {
        // Arrange

        await using var factory = new InMemoryWebApplicationFactory();
        var client = factory.CreateClient();
        var body = new MoveStreamRequest
        {
            SchemaVersion = MoveStreamRequest.CurrentSchemaVersion,
            Targets = [new PositionVector { X = 0, Y = 0.9, Z = 0 }],
        };
        // Act

        var response = await client.PostAsJsonAsync("/game/players/unknown-player/move-stream", body);
        // Assert

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

    [Fact]
    public async Task PostMoveStream_ShouldReturn400_WhenTargetsEmpty()
    {
        // Arrange

        await using var factory = new InMemoryWebApplicationFactory();
        var client = factory.CreateClient();
        var content = new StringContent(
            "{\"schemaVersion\":1,\"targets\":[]}",
            Encoding.UTF8,
            "application/json");
        // Act

        var response = await client.PostAsync("/game/players/dev-local-1/move-stream", content);
        // Assert

        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    [Fact]
    public async Task PostMoveStream_ShouldReturn400WithReason_WhenSecondStepExceedsHorizontalLimit()
    {
        // Arrange

        await using var factory = new InMemoryWebApplicationFactory().WithWebHostBuilder(b =>
        {
            b.ConfigureTestServices(services =>
            {
                services.PostConfigure<GamePositionOptions>(o =>
                {
                    o.MovementValidation.HorizontalStepEnabled = true;
                    o.MovementValidation.MaxHorizontalStep = 0.2;
                });
            });
        });
        var client = factory.CreateClient();
        var body = new MoveStreamRequest
        {
            SchemaVersion = MoveStreamRequest.CurrentSchemaVersion,
            Targets =
            [
                new PositionVector { X = -4.95, Y = 0.9, Z = -5.0 },
                new PositionVector { X = -4.0, Y = 0.9, Z = -5.0 },
            ],
        };
        // Act
        var response = await client.PostAsJsonAsync("/game/players/dev-local-1/move-stream", body);

        // Assert
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
        var rej = await response.Content.ReadFromJsonAsync<MoveCommandRejectedResponse>();
        Assert.NotNull(rej);
        Assert.Equal(MoveCommandReasonCodes.HorizontalStepExceeded, rej!.ReasonCode);

        var after = await client.GetFromJsonAsync<PositionStateResponse>("/game/players/dev-local-1/position");
        Assert.NotNull(after);
        Assert.Equal(-5.0, after!.Position.X);
    }
}
