using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RacePlannerApi.Data;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Xunit;

namespace backend.Tests.Integration;

public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
    // Use a static database name so all tests in the same process share the database
    private static readonly string _databaseName = $"IntegrationTestDb_{Guid.NewGuid():N}";
    
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            // Remove all DbContextOptions registrations
            var descriptors = services.Where(
                d => d.ServiceType == typeof(DbContextOptions<RacePlannerDbContext>) ||
                     d.ServiceType.Name.Contains("DbContextOptions")).ToList();
            
            foreach (var descriptor in descriptors)
            {
                services.Remove(descriptor);
            }

            // Add in-memory database with consistent name
            services.AddDbContext<RacePlannerDbContext>(options =>
            {
                options.UseInMemoryDatabase(_databaseName);
            });
        });
    }
}

public abstract class IntegrationTestBase : IClassFixture<CustomWebApplicationFactory>, IDisposable
{
    protected readonly CustomWebApplicationFactory _factory;
    protected readonly HttpClient _client;
    protected readonly JsonSerializerOptions _jsonOptions = new(JsonSerializerDefaults.Web);
    protected readonly IServiceScope _scope;
    protected readonly RacePlannerDbContext _dbContext;

    protected IntegrationTestBase(CustomWebApplicationFactory factory)
    {
        _factory = factory;
        _client = _factory.CreateClient();
        _scope = _factory.Services.CreateScope();
        _dbContext = _scope.ServiceProvider.GetRequiredService<RacePlannerDbContext>();
        _dbContext.Database.EnsureCreated();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }

    protected HttpClient CreateAuthenticatedClient(string token = "")
    {
        var client = _factory.CreateClient();
        if (!string.IsNullOrEmpty(token))
        {
            client.DefaultRequestHeaders.Authorization = 
                new AuthenticationHeaderValue("Bearer", token);
        }
        return client;
    }

    protected async Task<T?> GetAsync<T>(string url)
    {
        var response = await _client.GetAsync(url);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<T>(_jsonOptions);
    }

    protected async Task<TResponse?> PostAsync<TRequest, TResponse>(string url, TRequest data)
    {
        var response = await _client.PostAsJsonAsync(url, data, _jsonOptions);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<TResponse>(_jsonOptions);
    }

    protected async Task<TResponse?> PutAsync<TRequest, TResponse>(string url, TRequest data)
    {
        var response = await _client.PutAsJsonAsync(url, data, _jsonOptions);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<TResponse>(_jsonOptions);
    }

    protected async Task DeleteAsync(string url)
    {
        var response = await _client.DeleteAsync(url);
        response.EnsureSuccessStatusCode();
    }
}
