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.DependencyInjection.Extensions;
using Testcontainers.PostgreSql;
using WorkClub.Infrastructure.Data;

namespace WorkClub.Tests.Integration.Infrastructure;

public class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TProgram> where TProgram : class
{
    private readonly PostgreSqlContainer _postgresContainer = new PostgreSqlBuilder()
        .WithImage("postgres:16-alpine")
        .WithDatabase("workclub_test")
        .WithUsername("test")
        .WithPassword("test")
        .Build();

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        // Start container (async wait)
        _postgresContainer.StartAsync().GetAwaiter().GetResult();

        builder.ConfigureAppConfiguration((context, config) =>
        {
            // Override connection string for tests
            config.AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["ConnectionStrings:DefaultConnection"] = _postgresContainer.GetConnectionString()
            });
        });

        builder.ConfigureServices(services =>
        {
            // Remove existing DbContext registration
            var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
            if (descriptor != null)
            {
                services.Remove(descriptor);
            }

            // Add Testcontainers DbContext
            services.AddDbContext<AppDbContext>(options =>
                options.UseNpgsql(_postgresContainer.GetConnectionString()));

            // Replace authentication with TestAuthHandler
            services.RemoveAll<IAuthenticationSchemeProvider>();
            services.AddAuthentication(defaultScheme: "Test")
                .AddScheme<AuthenticationSchemeOptions, TestAuthHandler>("Test", options => { });

            // Build service provider and run migrations
            var sp = services.BuildServiceProvider();
            using var scope = sp.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
            db.Database.Migrate();
            
    using var conn = new Npgsql.NpgsqlConnection(_postgresContainer.GetConnectionString());
    conn.Open();
    using var cmd = conn.CreateCommand();
    cmd.CommandText = @"
      DO $$ BEGIN
      -- Create test user for RLS
      IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'rls_test_user') THEN
        CREATE USER rls_test_user WITH PASSWORD 'rlspass';
      END IF;
      
      -- Grant basic permissions to test user
      GRANT CONNECT ON DATABASE workclub_test TO rls_test_user;
      GRANT USAGE ON SCHEMA public TO rls_test_user;
      GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO rls_test_user;
      GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO rls_test_user;
      
      -- Create app_admin role for bypassing RLS
      IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_admin') THEN
        CREATE ROLE app_admin WITH BYPASSRLS;
      END IF;
      
      -- Grant app_admin full access to tables
      GRANT CONNECT ON DATABASE workclub_test TO app_admin;
      GRANT USAGE ON SCHEMA public TO app_admin;
      GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO app_admin;
      GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO app_admin;
      
      -- Allow rls_test_user to assume app_admin role
      GRANT app_admin TO rls_test_user;
      END $$;
    ";
    cmd.ExecuteNonQuery();
        });

        builder.UseEnvironment("Test");
    }

    public override async ValueTask DisposeAsync()
    {
        await _postgresContainer.DisposeAsync();
        await base.DisposeAsync();
    }
}
