using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NeonSprawl.Server.Game.Mastery;
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.Mastery;

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

    [RequirePostgresFact]
    public async Task BranchPickAndUnlock_ShouldPersistAcrossNewFactory()
    {
        // Arrange
        await ResetPerkTablesAsync();
        const string playerId = "dev-local-1";
        using (var scope = Factory.Services.CreateScope())
        {
            var xpStore = scope.ServiceProvider.GetRequiredService<IPlayerSkillProgressionStore>();
            var engine = scope.ServiceProvider.GetRequiredService<PerkUnlockEngine>();
            Assert.True(xpStore.TryApplyXpDelta(playerId, "salvage", 450, out _, out _));
            Assert.True(engine.TrySelectBranch(playerId, "salvage", 1, "scrap_efficiency").Success);
            _ = engine.ReevaluateAfterLevelUp(playerId, "salvage", newLevel: 4);
        }

        // Act
        PerkStateSnapshot snapshot;
        await using (var secondFactory = new PostgresWebApplicationFactory())
        {
            using var scope = secondFactory.Services.CreateScope();
            var perkStore = scope.ServiceProvider.GetRequiredService<IPlayerPerkStateStore>();
            snapshot = perkStore.GetSnapshot(playerId);
        }

        // Assert
        Assert.Equal("scrap_efficiency", snapshot.BranchPicksBySkillId["salvage"][1]);
        Assert.Contains("salvage_scrap_efficiency_1", snapshot.UnlockedPerkIds);
    }

    private async Task ResetPerkTablesAsync()
    {
        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 perkDdlPath = Path.Combine(AppContext.BaseDirectory, "db", "migrations", "V004__player_perk_state.sql");
        if (!File.Exists(positionDdlPath) || !File.Exists(perkDdlPath))
        {
            throw new FileNotFoundException("Test DDL for perk persistence not found.");
        }

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