using AutoMapper;
using Edge.Core.Configuration;
using Edge.Core.Database;
using Edge.Core.Processor.Dispatcher;
using Edge.Core.Processor.Dispatcher.ProcessorLoader;
using Edge.Core.UniversalApi;
using Edge.Core.UniversalApi.Auditing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Protocol;
using MQTTnet.Server;
using NLog;
using NLog.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace LiteFccCoreMain.Service
{
    internal class ServiceBuilder
    {
        private static IServiceCollection services;

        static ServiceBuilder()
        {
        }

        public void ConfigureServices(IServiceCollection services)
        {
        }

        /// <summary>
        /// Build necessary services.
        /// </summary>
        /// <returns></returns>
        public static IServiceProvider Build()
        {
            services = new ServiceCollection();

            #region add the most underlying service: Logging and Configuration(settings.xml) Service

            services.AddLogging(loggingBuilder =>
            {
                // configure Logging with NLog
                loggingBuilder.ClearProviders();
                loggingBuilder.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                var fa = loggingBuilder.AddNLog();
            });
            // must have below 2 lines
            NLog.LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration("nlog.config");
            NLog.LogManager.LoadConfiguration("nlog.config");

            Configurator.Default.LoadAsync().Wait();
            services.AddSingleton(Configurator.Default);

            #endregion

            #region AutoMapper, resolve all assemblies under entry folder.

            //var applicationAssFileInfos = new DirectoryInfo(Directory.GetCurrentDirectory())
            //            .GetFiles().Where(f => //f.Name.ToLower().StartsWith("application") &&
            //                f.Extension != null
            //                && (f.Extension.ToLower() == ".dll" || f.Extension.ToLower() == ".exe"));
            var assembliesWithAutoMapperProfileDefined = new List<Assembly>();
            foreach (var ass in ObjectInstanceCreator.CurrentDomainAssemblies)//applicationAssFileInfos)
            {
                try
                {
                    //var ass = Assembly.LoadFrom(ai.FullName);
                    if (ass.GetTypes().Any(t => typeof(Profile).IsAssignableFrom(t)))
                        assembliesWithAutoMapperProfileDefined.Add(ass);
                }
                catch
                { }
            }

            services.AddAutoMapper(assembliesWithAutoMapperProfileDefined);

            #endregion

            /* ServiceLifetime.Transient will make the internal service colletion 
             * keep reference to all objects and cause memory leak.*/
            //services.AddDbContext<SqliteDbContext>(ServiceLifetime.Transient);
            services.AddScoped<SqliteDbContext>();

            #region Mqtt server setup

            //var mqttServer = new MqttFactory().CreateMqttServer();
            //services.AddSingleton<IMqttServer>(mqttServer);

            #endregion

            #region Mqtt client

            services.AddTransient<IManagedMqttClient>(p => new MqttFactory().CreateManagedMqttClient());

            #endregion

            #region UniversalApi

            services.AddSingleton(AuditingStoreFactory.Create);
            services.AddSingleton(UniversalApiHubFactory.Create);
            services.AddSingleton(UniversalApiInvokerFactory.Create);

            #endregion

            #region IProcessorLoader and IProcessorMetaConfigAccessor

            services.AddSingleton(ProcessorLoaderFactory.Create);
            services.AddSingleton(ProcessorMetaConfigAccessorFactory.Create);

            #endregion

            var serviceProvider = services.BuildServiceProvider();
            return serviceProvider;
        }

        //public static IServiceProvider Provider => serviceProvider;

        //public static IServiceProvider Build1()
        //{
        //    var services = new ServiceCollection();
        //    services.AddSingleton<ILoggerFactory, LoggerFactory>();
        //    //services.AddLogging((builder) => builder.SetMinimumLevel(LogLevel.Trace));
        //    var serviceProvider = services.BuildServiceProvider();
        //    var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
        //    loggerFactory.ConfigureNLog("testnlog.config");
        //    return serviceProvider;
        //}
    }
}