using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using Testcontainers.PostgreSql;
using Testcontainers.MongoDb;
using Xunit;
using System.Threading.Tasks;
using System.Collections.Generic;
using Knot.Shared.Kernel;
using NSubstitute;

namespace Knot.IntegrationTests;

public abstract class BaseIntegrationTest : IAsyncLifetime
{
    protected readonly PostgreSqlContainer _postgresContainer = new PostgreSqlBuilder()
        .WithImage("postgres:15-alpine")
        .Build();

    protected readonly MongoDbContainer _mongoContainer = new MongoDbBuilder()
        .WithImage("mongo:6.0")
        .Build();

    protected HttpClient _client;
    protected WebApplicationFactory<Program> _factory;
    protected readonly Guid _userId = Guid.NewGuid();
    protected readonly IUserContext _userContextMock = Substitute.For<IUserContext>();

    public virtual async Task InitializeAsync()
    {
        await _postgresContainer.StartAsync();
        await _mongoContainer.StartAsync();

        _userContextMock.UserId.Returns(_userId);
        _userContextMock.IsAuthenticated.Returns(true);

        _factory = new IntegrationTestWebApplicationFactory(
            _postgresContainer.GetConnectionString(),
            _mongoContainer.GetConnectionString(),
            _userContextMock);

        _client = _factory.CreateClient();
    }

    public virtual async Task DisposeAsync()
    {
        await _postgresContainer.StopAsync();
        await _mongoContainer.StopAsync();
        _factory?.Dispose();
        _client?.Dispose();
    }

    private class IntegrationTestWebApplicationFactory : WebApplicationFactory<Program>
    {
        private readonly string _pgConnectionString;
        private readonly string _mongoConnectionString;
        private readonly IUserContext _userContext;

        public IntegrationTestWebApplicationFactory(string pg, string mongo, IUserContext userContext)
        {
            _pgConnectionString = pg;
            _mongoConnectionString = mongo;
            _userContext = userContext;
        }

        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureAppConfiguration((context, config) =>
            {
                config.AddInMemoryCollection(new Dictionary<string, string?>
                {
                    ["ConnectionStrings:DefaultConnection"] = _pgConnectionString,
                    ["ConnectionStrings:MongoConnection"] = _mongoConnectionString,
                    ["DATABASE_URL"] = _pgConnectionString,
                    ["MONGO_CONNECTION"] = _mongoConnectionString,
                    ["KNOT_MASTER_ENCRYPTION_KEY"] = "TestEncryptionKey_32CharactersLong!"
                });
            });

            builder.ConfigureTestServices(services => {
                services.AddScoped(_ => _userContext);
            });
        }
    }
}
