using Baya.Application.Common;
using Baya.Application.Contracts.Common;
using FluentValidation;
using Mediator;
using Microsoft.Extensions.DependencyInjection;

namespace Baya.Application.ServiceConfiguration;

public static class ServiceCollectionExtension
{
    public static IServiceCollection AddApplicationServices(this IServiceCollection services)
    {
        services.AddMediator(options =>
        {
            options.ServiceLifetime = ServiceLifetime.Scoped;
            options.Namespace = "Baya.Application.Mediator";
        });

        services.AddScoped(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));

        services.AddScoped(typeof(IPipelineBehavior<,>), typeof(MetricsBehaviour<,>));

        services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidateCommandBehavior<,>));

        // Pure, stateless serializer that b8 consumes to freeze a variant onto a booking.
        services.AddSingleton<IVariantSnapshotSerializer, VariantSnapshotSerializer>();

        RegisterCommandValidators(services);

        return services;
    }

    // Registers every FluentValidation AbstractValidator<T> in this assembly as IValidator<T> so the
    // ValidateCommandBehavior can resolve and run them. Done by hand (rather than pulling in the
    // FluentValidation.DependencyInjectionExtensions package) to keep the dependency surface minimal.
    private static void RegisterCommandValidators(IServiceCollection services)
    {
        var assembly = typeof(ValidateCommandBehavior<,>).Assembly;

        foreach (var type in assembly.GetTypes())
        {
            if (type.IsAbstract || type.IsInterface)
                continue;

            var validatorInterface = Array.Find(
                type.GetInterfaces(),
                i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidator<>));

            if (validatorInterface is not null)
                services.AddScoped(validatorInterface, type);
        }
    }
}