using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using StackExchange.Redis;

public class GatewayApiFixture : WebApplicationFactory<Program>, IAsyncLifetime
{
    // Prefer CI/env overrides; fall back to ward-gateway compose profile ports.
    public static string TestConnectionString { get; } =
        Environment.GetEnvironmentVariable("ConnectionStrings__GatewayDb")
        ?? "Host=localhost;Port=5437;Database=vigilcare_ward_test;Username=postgres;Password=password";

    // Never fall back to the API Redis__* env — that points at a different instance/DB index.
    public static string RedisConnection { get; } =
        Environment.GetEnvironmentVariable("Gateway__Redis__ConnectionString")
        ?? "localhost:6383,defaultDatabase=2,allowAdmin=true";

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.UseEnvironment("Testing");
        builder.ConfigureAppConfiguration((_, config) =>
        {
            config.AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["ConnectionStrings:GatewayDb"] = TestConnectionString,
                ["Redis:ConnectionString"] = RedisConnection,
                ["RabbitMq:Host"] = Environment.GetEnvironmentVariable("Gateway__RabbitMq__Host")
                    ?? Environment.GetEnvironmentVariable("RabbitMq__Host")
                    ?? "localhost",
                // Do not fall back to RabbitMq__Port (API uses 5674); gateway broker is 5675.
                ["RabbitMq:Port"] = Environment.GetEnvironmentVariable("Gateway__RabbitMq__Port")
                    ?? "5675",
                ["RabbitMq:Username"] = Environment.GetEnvironmentVariable("RabbitMq__Username") ?? "guest",
                ["RabbitMq:Password"] = Environment.GetEnvironmentVariable("RabbitMq__Password") ?? "guest",
                ["RabbitMq:PagingAckTimeoutMs"] = "5000",
                // Neutralize appsettings.Production.json if the process env was Production.
                ["RabbitMq:UseSsl"] = "false",
                ["CentralApi:BaseUrl"] = "http://127.0.0.1:1",
                ["Gateway:GatewayId"] = "22222222-2222-2222-2222-222222222222",
                ["Gateway:SiteId"] = "11111111-1111-1111-1111-111111111111",
                ["Gateway:Department"] = "ICU",
                ["Gateway:EncounterSyncIntervalMinutes"] = "60",
                ["Gateway:CentralReachabilityIntervalSeconds"] = "1",
                ["Gateway:HeartbeatIntervalSeconds"] = "1",
                ["Gateway:SyncBatchSize"] = "500",
                ["Gateway:AutoMigrate"] = "false",
                ["ApiKey:Gateway"] = "dev-gateway-key-change-in-production",
                ["Jwt:SigningKey"] = "dev-signing-key-minimum-32-bytes-long!!",
                ["Jwt:Issuer"] = "vigilcare-gateway",
                ["Jwt:Audience"] = "vigilcare-dashboard",
            });
        });

        builder.ConfigureServices(services =>
        {
            services.Configure<HostOptions>(o =>
                o.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.Ignore);

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = TestingAuthHandler.SchemeName;
                options.DefaultChallengeScheme = TestingAuthHandler.SchemeName;
            })
            .AddScheme<AuthenticationSchemeOptions, TestingAuthHandler>(
                TestingAuthHandler.SchemeName, _ => { });
        });
    }

    public async Task InitializeAsync()
    {
        var options = new DbContextOptionsBuilder<GatewayDbContext>()
            .UseNpgsql(TestConnectionString)
            .Options;
        await using (var migrateDb = new GatewayDbContext(options))
        {
            await migrateDb.Database.MigrateAsync();
            await GatewayDbResetHelper.ResetAsync(migrateDb);
        }

        using var scope = Services.CreateScope();
        var redis = scope.ServiceProvider.GetRequiredService<IConnectionMultiplexer>();
        var server = redis.GetServer(redis.GetEndPoints().First());
        // Flush only the gateway test DB index — do not FlushAll (would wipe API test DB 1).
        var dbIndex = 2;
        var cfg = RedisConnection.Split(',').FirstOrDefault(p => p.StartsWith("defaultDatabase=", StringComparison.OrdinalIgnoreCase));
        if (cfg is not null && int.TryParse(cfg.Split('=')[1], out var parsed))
            dbIndex = parsed;
        await server.FlushDatabaseAsync(dbIndex);
    }

    protected override void ConfigureClient(HttpClient client)
    {
        base.ConfigureClient(client);
        client.AsNurse();
    }

    public new async Task DisposeAsync() => await base.DisposeAsync();
}
