using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using StellaOps.Authority.Storage.Mongo.Migrations;

namespace StellaOps.Authority.Storage.Mongo.Initialization;

/// <summary>
/// Performs MongoDB bootstrap tasks for the Authority service.
/// </summary>
public sealed class AuthorityMongoInitializer
{
    private readonly IEnumerable<IAuthorityCollectionInitializer> collectionInitializers;
    private readonly AuthorityMongoMigrationRunner migrationRunner;
    private readonly ILogger<AuthorityMongoInitializer> logger;

    public AuthorityMongoInitializer(
        IEnumerable<IAuthorityCollectionInitializer> collectionInitializers,
        AuthorityMongoMigrationRunner migrationRunner,
        ILogger<AuthorityMongoInitializer> logger)
    {
        this.collectionInitializers = collectionInitializers ?? throw new ArgumentNullException(nameof(collectionInitializers));
        this.migrationRunner = migrationRunner ?? throw new ArgumentNullException(nameof(migrationRunner));
        this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <summary>
    /// Ensures collections exist, migrations run, and indexes are applied.
    /// </summary>
    public async ValueTask InitialiseAsync(IMongoDatabase database, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(database);

        await migrationRunner.RunAsync(database, cancellationToken).ConfigureAwait(false);

        foreach (var initializer in collectionInitializers)
        {
            try
            {
                logger.LogInformation(
                    "Ensuring Authority Mongo indexes via {InitializerType}.",
                    initializer.GetType().FullName);

                await initializer.EnsureIndexesAsync(database, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                logger.LogError(
                    ex,
                    "Authority Mongo index initialisation failed for {InitializerType}.",
                    initializer.GetType().FullName);
                throw;
            }
        }
    }
}
