using adas_core.Infrastructure.Utils;
using Mongo2Go;
using MongoDB.Bson;
using MongoDB.Driver;

namespace adas_core.Test.Repositories;

[SetUpFixture]
[Category("Integration")]
public class IntegrationDb
{
    private const int TimeoutInSeconds = 60; // Set the desired timeout in seconds.
    public static MongoDbRunner? Runner { get; private set; }

    private static MongoClient? Client { get; set; }

    public static IMongoDatabase Database { get; private set; } = null!;
    public static string DatabaseName { get; } = "IntegrationTestDb";

    /// <summary>
    /// Initializes the MongoDB integration test environment by starting a MongoDB runner, configuring MongoDB conventions and BSON class mappings, establishing a client from the runner's connection string, and obtaining the target database used by integration tests.
    /// </summary>
    [OneTimeSetUp]
        public void InitIntegrationTests()
        {
            StartMongoDbRunner().Wait();
            MongoDbHostBuilderExtension.ConfigureMongoDbConventions();
            MongoDbHostBuilderExtension.ConfigureRegisterMapClass();
            Client = new MongoClient(Runner?.ConnectionString);
            Database = Client.GetDatabase(DatabaseName);
        }

    /// <summary>
    /// Starts the MongoDB test runner and waits for the server to become available by issuing a ping command, retrying on failure until the configured timeout is reached.
    /// If the server does not become available within the timeout, the runner is disposed and a <see cref="TimeoutException"/> is thrown.
    /// </summary>
    /// <exception cref="TimeoutException">Thrown when the MongoDB server does not respond within the configured timeout period.</exception>
    private static async Task StartMongoDbRunner()
        {
            Runner = MongoDbRunner.Start();
    
            // Wait for the MongoDB server to become available or timeout.
            var startTime = DateTime.UtcNow;
            while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(TimeoutInSeconds))
                try
                {
                    var testClient = new MongoClient(Runner.ConnectionString);
                    var adminDb = testClient.GetDatabase("admin");
                    await adminDb.RunCommandAsync((Command<BsonDocument>)"{ping:1}");
                    return; // MongoDB server is available, continue.
                }
                catch
                {
                    // Retry after a short delay.
                    await Task.Delay(500);
                }
    
            // Timeout reached, dispose the runner and throw an exception.
            Runner.Dispose();
            throw new TimeoutException("Timeout while starting MongoDB server.");
        }

    /// <summary>
    /// Performs one-time teardown for integration tests by disposing the <see cref="Client"/> and <see cref="Runner"/> resources.
    /// Safely handles cases where either resource has not been initialized by using null-conditional disposal.
    /// </summary>
    [OneTimeTearDown]
        public void TeardownIntegrationTests()
        {
            Client?.Dispose();
            Runner?.Dispose();
            //_runner = null;
            //_client = null;
            //_fakeDb = null;
        }
}