using System.Linq;
using System.Net.Http.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NeonSprawl.Server.Game.AbilityInput;
using NeonSprawl.Server.Game.Gigs;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Targeting;
using NeonSprawl.Server.Tests.Game.PositionState;
using Npgsql;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Gigs;

[Collection("Postgres integration")]
public sealed class GigProgressionGrantPersistenceIntegrationTests(PostgresIntegrationHarness harness)
{
    private PostgresWebApplicationFactory Factory => harness.Factory;

    [RequirePostgresFact]
    public async Task DefeatGrantThenGetAcrossNewFactory_ShouldPersistGigXp()
    {
        // Arrange
        await ResetGigProgressionTableAsync();
        var cast = new AbilityCastRequest
        {
            SchemaVersion = AbilityCastRequest.CurrentSchemaVersion,
            SlotIndex = 0,
            AbilityId = PrototypeAbilityRegistry.PrototypePulse,
            TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
        };

        // Act — defeat alpha through first host (four pulses)
        using (var firstClient = Factory.CreateClient())
        {
            await firstClient.PostAsJsonAsync(
                "/game/players/dev-local-1/hotbar-loadout",
                new HotbarLoadoutUpdateRequest
                {
                    SchemaVersion = HotbarLoadoutUpdateRequest.CurrentSchemaVersion,
                    Slots = [new HotbarSlotBindingJson { SlotIndex = 0, AbilityId = PrototypeAbilityRegistry.PrototypePulse }],
                });
            await firstClient.PostAsJsonAsync(
                "/game/players/dev-local-1/target/select",
                new TargetSelectRequest
                {
                    SchemaVersion = TargetSelectRequest.CurrentSchemaVersion,
                    TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
                });
            for (var i = 0; i < 4; i++)
            {
                var response = await firstClient.PostAsJsonAsync("/game/players/dev-local-1/ability-cast", cast);
                response.EnsureSuccessStatusCode();
                if (i < 3)
                {
                    await Task.Delay(TimeSpan.FromSeconds(3.2));
                }
            }
        }

        await using var secondFactory = new PostgresWebApplicationFactory();
        using var secondClient = secondFactory.CreateClient();
        var snapshot = await secondClient.GetFromJsonAsync<GigProgressionSnapshotResponse>(
            "/game/players/dev-local-1/gig-progression");

        // Assert
        Assert.NotNull(snapshot);
        var breach = Assert.Single(snapshot!.Gigs!);
        Assert.Equal(GigProgressionConstants.CombatDefeatXpAmount, breach.Xp);
    }

    private async Task ResetGigProgressionTableAsync()
    {
        var cs = Environment.GetEnvironmentVariable("ConnectionStrings__NeonSprawl");
        if (string.IsNullOrWhiteSpace(cs))
        {
            throw new InvalidOperationException("ConnectionStrings__NeonSprawl is not set.");
        }

        _ = Factory.Services;
        var options = Factory.Services.GetRequiredService<IOptions<GamePositionOptions>>().Value;

        var positionDdlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V001__player_position.sql");
        var gigDdlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V007__player_gig_progression.sql");
        if (!File.Exists(positionDdlPath))
        {
            throw new FileNotFoundException($"Test DDL not found at '{positionDdlPath}'.", positionDdlPath);
        }

        if (!File.Exists(gigDdlPath))
        {
            throw new FileNotFoundException($"Test DDL not found at '{gigDdlPath}'.", gigDdlPath);
        }

        var positionDdl = await File.ReadAllTextAsync(positionDdlPath);
        var gigDdl = await File.ReadAllTextAsync(gigDdlPath);
        await using var conn = new NpgsqlConnection(cs);
        await conn.OpenAsync();
        await using (var applyPosition = new NpgsqlCommand(positionDdl, conn))
        {
            await applyPosition.ExecuteNonQueryAsync();
        }

        await using (var truncate = new NpgsqlCommand("TRUNCATE player_position CASCADE;", conn))
        {
            await truncate.ExecuteNonQueryAsync();
        }

        PostgresPositionBootstrap.SeedDevPlayer(conn, options);

        await using (var applyGig = new NpgsqlCommand(gigDdl, conn))
        {
            await applyGig.ExecuteNonQueryAsync();
        }

        await using (var truncateGig = new NpgsqlCommand("TRUNCATE player_gig_progression;", conn))
        {
            await truncateGig.ExecuteNonQueryAsync();
        }
    }
}
