using System.Net.Http.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Skills;
using NeonSprawl.Server.Tests.Game.PositionState;
using Npgsql;
using Xunit;

namespace NeonSprawl.Server.Tests.Game.Skills;

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

    [RequirePostgresFact]
    public async Task PostGrantThenGetAcrossNewFactory_ShouldPersistSkillXp()
    {
        // Arrange
        await ResetProgressionTableAsync();
        var grant = new SkillProgressionGrantRequest
        {
            SchemaVersion = SkillProgressionGrantRequest.CurrentSchemaVersion,
            SkillId = "salvage",
            Amount = 50,
            SourceKind = "activity",
        };
        HttpResponseMessage postResponse;

        // Act — write grant through first host
        using (var firstClient = Factory.CreateClient())
        {
            postResponse = await firstClient.PostAsJsonAsync(
                "/game/players/dev-local-1/skill-progression",
                grant);
        }

        // Act — read back through a fresh host (forces DB round-trip / new DI scope)
        await using var secondFactory = new PostgresWebApplicationFactory();
        using var secondClient = secondFactory.CreateClient();
        var snapshot =
            await secondClient.GetFromJsonAsync<SkillProgressionSnapshotResponse>(
                "/game/players/dev-local-1/skill-progression");

        // Assert
        Assert.True(postResponse.IsSuccessStatusCode);
        Assert.NotNull(snapshot);
        var salvage = Assert.Single(snapshot!.Skills!, static s => s.Id == "salvage");
        Assert.Equal(50, salvage.Xp);
        Assert.Equal(1, salvage.Level);
    }

    private async Task ResetProgressionTableAsync()
    {
        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 progressionDdlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V003__player_skill_progression.sql");
        if (!File.Exists(positionDdlPath))
        {
            throw new FileNotFoundException($"Test DDL not found at '{positionDdlPath}'.", positionDdlPath);
        }

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

        var positionDdl = await File.ReadAllTextAsync(positionDdlPath);
        var progressionDdl = await File.ReadAllTextAsync(progressionDdlPath);
        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 applyProgression = new NpgsqlCommand(progressionDdl, conn))
        {
            await applyProgression.ExecuteNonQueryAsync();
        }

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