using System.Reflection;
using adas_core.Domain.Models.AppSettings;
using adas_core.Infrastructure.Utils.MongoMaps.Interfaz;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver;
using MongoMigrations.Core;
using MongoClient = MongoDB.Driver.MongoClient;

namespace adas_core.Infrastructure.Utils;

public static class MongoDbHostBuilderExtension
{
    public static IHostBuilder UseMongo(this IHostBuilder hostBuilder)
    {
        ConfigureMongoDbConventions();

        ConfigureRegisterMapClass();

        return hostBuilder;
    }
    public static IHost RunMongoMigrations(this IHost host)
    {
        using var scope = host.Services.CreateScope();

        var database = scope.ServiceProvider.GetRequiredService<IMongoDatabase>();

        var locator = new MigrationLocator();
        locator.LookForMigrationsInAssembly(
            typeof(adas_core.Infrastructure.Migrations.MongoMigrations.U_0_1_0_UpdateDataPatien).Assembly
        );

        var runner = new MigrationRunner(
            database,
            collectionName: "migrations",
            migrationLocator: locator
        );

        runner.UpdateToLatest();

        return host;
    }
    private static IMongoDatabase ConfigureMongoDbConnection(IOptions<DatabaseSettings> dbSettings)
    {
        var connectionString = dbSettings.Value.ConnectionString;

        var databaseName = dbSettings.Value.DatabaseName;

        if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(databaseName))
            throw new Exception("DataBase connection string and name is requiered");

        _mongoClient = new MongoClient(connectionString);

        var mongoDb = _mongoClient.GetDatabase(databaseName);

        if (mongoDb == null) throw new Exception("DataBase doesn't exist");

        return mongoDb;
    }

    public static void ConfigureRegisterMapClass()
    {
        var assembly = Assembly.GetExecutingAssembly();

        // Busca todos los tipos que implementan la interfaz
        var contributors = assembly.GetTypes()
            .Where(t => typeof(IEntityMapContributor).IsAssignableFrom(t) &&
                        t is { IsInterface: false, IsAbstract: false });

        // Crea una instancia de cada contribuidor y ejecuta su método
        foreach (var contributorType in contributors)
        {
            var contributorInstance = (IEntityMapContributor)Activator.CreateInstance(contributorType)!;
            contributorInstance.RegisterMaps();
        }
    }

    public static void ConfigureMongoDbConventions()
    {
        var pack = new ConventionPack
        {
            new IgnoreExtraElementsConvention(true),
            new CamelCaseElementNameConvention()
        };

        ConventionRegistry.Register(
            "Ignore Extra Elements Convention",
            pack,
            _ => true);

        ConventionRegistry.Register(
            "Camel Case Convention",
            pack,
            _ => true);
    }

    #region MongoDB

    private static MongoClient? _mongoClient;

    #endregion
}