using FluentValidation;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PARR.Core.Common.Helpers;
using PARR.Core.Common.Implementations;
using PARR.Core.Common.Interfaces;
using PARR.Core.Services.MatchingStatusService;
using PARR.Core.Services.NextRunServices;
using PARR.Core.Services.NextRunServices.Subservices;
using PARR.Core.Services.RobotTask.Implementations;
using PARR.Core.Services.RobotTask.Interfaces;
using PARR.Core.Services.Shortcodes;
using PARR.Core.Services.Shortcodes.Handlers;
using PARR.Core.Services.TaskServices.Handlers;
using PARR.Core.Services.TaskServices.Handlers.Factory;
using PARR.Core.Services.TaskServices.Implementations;
using PARR.Core.Services.TaskServices.Interfaces;
using PARR.Core.Services.TaskServices.Providers;
using PARR.Core.Services.TaskServices.ReconciliationHosted;
using PARR.Core.Services.UnitFilterService;
using PARR.Core.Services.UnitFilterService.Models;
using PARR.Core.Services.UnitService.Implementations;
using PARR.Core.Services.UnitService.Interfaces;
using PARR.Core.Services.Workload.Implementations;
using PARR.Core.Services.Workload.Interfaces;
using PARR.Domain.Enums;
using PARR.Domain.Settings;

namespace PARR.Core
{
    public static class DependencyInjection
    {

        /// <summary>
        /// Регистрация Core (BLL) сервисов
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static IServiceCollection AddCoreServices(this IServiceCollection services, IConfiguration configuration)
        {
            // Регим профили AutoMapper
            services.AddBllMapping();

            // Регим все валидаторы в Core
            services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly, includeInternalTypes: true);

            // Регим настройки
            services.RegisterSettings(configuration);

            // --- Регистрируем сревисы Core ---

            #region Common

            services.AddTransient<ITransformService, TransformService>();

            #region Helpers

            services.AddScoped<JobGroupUnitFilterSynchronizeHelper>();
            services.AddScoped<JobGroupCloneHelper>();

            #endregion

            #endregion

            #region Task

            // ---------> Регистрируем в AddTaskManagement <---------

            //// Регистрация хендлеров (нужно регистировать каждый отдельно)
            //services.AddScoped<WorkloadReportHandler>();
            //services.AddScoped<BaseTaskHandler>(sp => sp.GetRequiredService<WorkloadReportHandler>());// регим для фабрики
            //// Еще хэндлеры...

            //// Фабрика хэндлеров
            //services.AddScoped<ITaskHandlerFactory, TaskHandlerFactory>();

            //services.AddScoped<ITaskManagementService, TaskManagementService>();

            //// Провайдер настроек MQ
            //services.AddSingleton<ITaskMqSettingsProvider, TaskMqSettingsProvider>();

            #endregion

            #region Shortсodes

            services.AddScoped<IShortcodeHandler, ConstantsShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, StandardShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, IndexShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, GroupedFieldShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, MaxShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, RelationshipUnitShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, LettersShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, RelationshipsShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, FieldShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, TagShortcodeHandler>();
            services.AddScoped<IShortcodeHandler, RelatedUnitTagShortcodeHandler>();

            services.AddScoped<IShortcodesService, ShortcodesService>();
            #endregion

            #region Services

            services.AddTransient<IMatchingStatusService, MatchingStatusService>();

            services.AddScoped<IRobotTaskService, RobotTaskService>();

            services.AddScoped<IUnitService, UnitService>();
            services.AddScoped<UnitCacheService>();

            //services.AddScoped<IUserService, UserService>();

            #endregion

            #region Workload

            services.AddScoped<WorkloadCacheService>();
            services.AddScoped<IWorkloadService, WorkloadService>();

            services.AddSingleton<IWorkloadIntervalService, WorkloadIntervalService>();

            #endregion

            #region NextRun

            services.AddTransient<IEsppScheduleTransformService, EsppScheduleTransformService>();
            services.AddTransient<ITemplateDistributor, TemplateDistributor>();
            services.AddTransient<INextRunService, NextRunService>();

            #endregion

            #region UnitFilterService

            services.AddTransient<IUnitFilterService, UnitFilterService>();
            services.Configure<UnitFilterServiceOptions>(options =>
            {
                options.LoadBatchSize = 50;
            });

            #endregion

            #region IMessageConsumer services

            services.AddSingleton<IWorkloadCacheBuilderService, WorkloadCacheBuilderService>();

            #endregion

            return services;
        }

        /// <summary>
        /// Регистрация сервисов по управлению задчами (Task)
        /// </summary>
        /// <param name="services"></param>
        /// <param name="mqSettingsFactory">Настройки MQ для всех TaskTypeEnum</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IServiceCollection AddTaskManagement(this IServiceCollection services, Func<IServiceProvider, TaskTypeEnum, IMqSettings> mqSettingsFactory)
        {
            if (mqSettingsFactory == null)
                throw new ArgumentNullException(nameof(mqSettingsFactory), "Ты забыл зарегистрировать настройки MQ для Task! Бестолочь! :)");

            // Регистрация хендлеров (нужно регистировать каждый отдельно)
            services.AddScoped<WorkloadReportHandler>();
            services.AddScoped<BaseTaskHandler>(sp => sp.GetRequiredService<WorkloadReportHandler>());// регим для фабрики хэндлеров
            // Еще хэндлеры...

            // Фабрика хэндлеров
            services.AddScoped<ITaskHandlerFactory, TaskHandlerFactory>();

            services.AddScoped<ITaskManagementService, TaskManagementService>();

            // Провайдер настроек MQ
            services.AddTransient<Func<TaskTypeEnum, IMqSettings>>(sp => (taskType) => mqSettingsFactory(sp, taskType)); // регим фабрику настроек для ITaskMqSettingsProvider
            services.AddSingleton<ITaskMqSettingsProvider, TaskMqSettingsProvider>();

            return services;
        }


        /// <summary>
        /// Подключение сервисов для TaskReconciliation
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configureSettings"></param>
        /// <returns></returns>
        public static IServiceCollection AddTaskReconciliation(this IServiceCollection services, IReconciliationSettings reconciliationSettings)
        {
            if (reconciliationSettings == null)
                throw new ArgumentNullException(nameof(reconciliationSettings));

            // Настройки 
            services.AddSingleton(reconciliationSettings);

            // Сервис
            services.AddScoped<ITaskReconciliationService, TaskReconciliationService>();

            // Воркер
            services.AddHostedService<ReconciliationHostedService>();

            return services;
        }


        private static IServiceCollection AddBllMapping(this IServiceCollection services)
        {
            // AutoMapper
            services.AddAutoMapper(cfg =>
            {
                // регим все в Core
                cfg.AddMaps(typeof(DependencyInjection).Assembly);
            });

            return services;
        }


        /// <summary>
        /// Регистрация настроек 
        /// </summary>
        /// <param name="services"></param>
        /// <param name="configuration"></param>
        /// <returns></returns>
        private static IServiceCollection RegisterSettings(this IServiceCollection services, IConfiguration configuration)
        {
            var groupedShortcodesCacheSettings = new GroupedShortcodesCacheSettings();
            configuration.GetSection(nameof(GroupedShortcodesCacheSettings)).Bind(groupedShortcodesCacheSettings);
            services.AddSingleton(groupedShortcodesCacheSettings);

            return services;
        }

    }
}
