using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Time.Testing;
using NeonSprawl.Server.Game.AbilityInput;
using NeonSprawl.Server.Game.Items;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Game.Skills;
using Npgsql;

namespace NeonSprawl.Server.Tests;

/// <summary>Forces the in-memory position store by replacing Postgres registrations after the host builds services (overrides process env e.g. CI <c>ConnectionStrings__NeonSprawl</c>).</summary>
public sealed class InMemoryWebApplicationFactory : WebApplicationFactory<Program>
{
    /// <summary>Controllable UTC clock shared by cast + cooldown routes in tests (NEO-32).</summary>
    public FakeTimeProvider? FakeClock { get; private set; }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        // Avoid Development + DeveloperExceptionPage: invalid JSON / binding failures must stay 400 for API tests,
        // and Release CI runs rely on deterministic status codes (e.g. HotbarLoadoutApiTests malformed-body case).
        builder.UseEnvironment("Testing");

        var skillsDir = SkillCatalogPathResolution.TryDiscoverSkillsDirectory(AppContext.BaseDirectory)
            ?? throw new InvalidOperationException(
                "Could not discover repo content/skills from AppContext.BaseDirectory; run tests from the neon-sprawl clone.");
        var masteryDir = MasteryCatalogPathResolution.TryDiscoverMasteryDirectory(AppContext.BaseDirectory)
            ?? throw new InvalidOperationException(
                "Could not discover repo content/mastery from AppContext.BaseDirectory; run tests from the neon-sprawl clone.");
        var itemsDir = ItemCatalogPathResolution.TryDiscoverItemsDirectory(AppContext.BaseDirectory)
            ?? throw new InvalidOperationException(
                "Could not discover repo content/items from AppContext.BaseDirectory; run tests from the neon-sprawl clone.");
        builder.UseSetting("Content:SkillsDirectory", skillsDir);
        builder.UseSetting("Content:MasteryDirectory", masteryDir);
        builder.UseSetting("Content:ItemsDirectory", itemsDir);
        builder.UseSetting("Game:EnableMasteryFixtureApi", "true");

        builder.ConfigureTestServices(services =>
        {
            for (var i = services.Count - 1; i >= 0; i--)
            {
                var d = services[i];
                if (d.ServiceType == typeof(IPositionStateStore) ||
                    d.ServiceType == typeof(IPlayerHotbarLoadoutStore) ||
                    d.ServiceType == typeof(IPlayerSkillProgressionStore) ||
                    d.ServiceType == typeof(IPlayerPerkStateStore) ||
                    d.ServiceType == typeof(TimeProvider) ||
                    d.ServiceType == typeof(IPlayerAbilityCooldownStore) ||
                    d.ServiceType == typeof(NpgsqlDataSource) ||
                    (d.ServiceType == typeof(IHostedService) &&
                     (d.ImplementationType == typeof(PostgresDevPlayerSeedHostedService) ||
                      d.ImplementationType == typeof(NpgsqlDataSourceShutdownHostedService))))
                {
                    services.RemoveAt(i);
                }
            }

            var fakeTime = new FakeTimeProvider(new DateTimeOffset(2026, 4, 30, 12, 0, 0, TimeSpan.Zero));
            FakeClock = fakeTime;
            services.AddSingleton<TimeProvider>(fakeTime);
            services.AddSingleton<IPositionStateStore, InMemoryPositionStateStore>();
            services.AddSingleton<IPlayerHotbarLoadoutStore, InMemoryPlayerHotbarLoadoutStore>();
            services.AddSingleton<IPlayerSkillProgressionStore, InMemoryPlayerSkillProgressionStore>();
            services.AddSingleton<IPlayerPerkStateStore, InMemoryPlayerPerkStateStore>();
            services.AddSingleton<IPlayerAbilityCooldownStore, InMemoryPlayerAbilityCooldownStore>();
        });
    }
}
