using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using GreenTree.Nachtragsmanagement.Core.Configuration;
using GreenTree.Nachtragsmanagement.Core.Data;
using GreenTree.Nachtragsmanagement.Data;
using GreenTree.Nachtragsmanagement.Services.Configuration;
using GreenTree.Nachtragsmanagement.Services.DbContext;
using GreenTree.Nachtragsmanagement.Services.Site;
using GreenTree.Nachtragsmanagement.Services.Test;
using GreenTree.Nachtragsmanagement.Services.User;
using GreenTree.Nachtragsmanagement.Services.Deviation;
using GreenTree.Nachtragsmanagement.Services.Misc;
using GreenTree.Nachtragsmanagement.Core;
using GreenTree.Nachtragsmanagement.Core.Plugins;
using GreenTree.Nachtragsmanagement.Web.Framework.Mvc.Routes;
using System.Web.Routing;
using GreenTree.Nachtragsmanagement.Core.Authentication;
using GreenTree.Nachtragsmanagement.Services.Appendix;
using GreenTree.Nachtragsmanagement.Services.Scheduling;
using GreenTree.Nachtragsmanagement.Services.Logging;
using Autofac.Core;

namespace GreenTree.Nachtragsmanagement.Web.Framework
{
    public class ApplicationContext
    {
        #region Statics

        /// <summary>
        /// Current ApplicationContext
        /// </summary>
        private static ApplicationContext _current;

        /// <summary>
        /// Returns the current ApplicationContext or creates one if doesn't exist already
        /// </summary>
        public static ApplicationContext Current
        {
            get
            {
                if (_current == null)
                {
                    _current = new ApplicationContext();
                }

                return _current;
            }
            private set
            {
                _current = value;
            }
        }

        #endregion

        #region Fields

        /// <summary>
        /// Returns the application wide app container builder
        /// </summary>
        private static IContainer _appContainer;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes a new instance of the ApplicationContext class
        /// </summary>
        public ApplicationContext()
        {
            var appContext = Singleton<ApplicationContext>.Instance;

            if (appContext == null)
                Singleton<ApplicationContext>.Instance = this;
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Registers all application ressources
        /// </summary>
        public static void InitApplication()
        {
            var builder = new ContainerBuilder();

            var connection = WebConfigurationManager.ConnectionStrings["DefaultConnection"];

            // Register object instances
            builder.RegisterInstance(new AppendixObjectContext(connection.ConnectionString)).As<IDbContext>().SingleInstance();

            // Register generic data repositorys
            builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>));

            // Register web helper
            builder.RegisterType<WebHelper>().As<IWebHelper>();

            // Register logger
            builder.RegisterType<DefaultLogger>().As<ILogger>();
            builder.RegisterType<ServiceLogger>().As<IServiceLogger>();

            // Register service types
            builder.RegisterType<DbRelationService>().As<IDbRelationService>();
            builder.RegisterType<UserService>().As<IUserService>();
            builder.RegisterType<UserHelper>().As<IUserHelper>();
            builder.RegisterType<AuthenticationService>().As<IAuthenticationService>();
            builder.RegisterType<SiteService>().As<ISiteService>();
            builder.RegisterType<DeviationService>().As<IDeviationService>();
            builder.RegisterType<AppendixService>().As<IAppendixService>();
            builder.RegisterType<MiscService>().As<IMiscService>();
            builder.RegisterType<NotificationService>().As<INotificationService>();
            builder.RegisterType<ConfigurationService>().As<IConfigurationService>();
            builder.RegisterType<PluginFinder>().As<IPluginFinder>();
            builder.RegisterType<WebAppTypeFinder>().As<ITypeFinder>();
            builder.RegisterType<RoutePublisher>().As<IRoutePublisher>();
            builder.RegisterType<NotificationScheduler>().As<INotificationScheduler>();

            // Register controllers
            builder.RegisterControllers(Assembly.GetCallingAssembly());

            // Register modules
            builder.RegisterModule(new AutofacWebTypesModule());

            Current = new ApplicationContext();

            _appContainer = builder.Build();

            var selfBuilder = new ContainerBuilder();
            selfBuilder.RegisterInstance(_appContainer).As<IContainer>();
            selfBuilder.Update(_appContainer);

            Singleton<IContainer>.Instance = _appContainer;

            // Register plugin controllers in a new container builder
            var pluginsControllerBuilder = new ContainerBuilder();
            var pluginsFinder = _appContainer.Resolve<IPluginFinder>();
            var pluginsInstalled = pluginsFinder.GetPlugins<IPlugin>(LoadPluginsMode.InstalledOnly);

            foreach (var plugin in pluginsInstalled)
            {
                pluginsControllerBuilder.RegisterControllers(plugin.PluginDescriptor.ReferencedAssembly);
            }

            pluginsControllerBuilder.Update(_appContainer);

            // Register notification plugin types
            var notificationPluginBuilder = new ContainerBuilder();
            var notificationPluginService = _appContainer.Resolve<INotificationService>();
            var notificationPlugins = notificationPluginService.LoadNotificationPlugins();

            foreach (var notificationPlugin in notificationPlugins)
            {
                notificationPluginBuilder.RegisterType(notificationPlugin.GetType())
                    .Named<INotificationPlugin>(notificationPlugin.SystemName)
                    .As<INotificationPlugin>();
            }

            notificationPluginBuilder.Update(_appContainer);

            DependencyResolver.SetResolver(new AutofacDependencyResolver(_appContainer));
        }

        /// <summary>
        /// Registers all routes provided by every installed Plugin
        /// </summary>
        /// <param name="routes">Registered routes where plugin routes should be append to.</param>
        public static void InitPluginRoutes(RouteCollection routes)
        {
            var routePublisher = _appContainer.Resolve<IRoutePublisher>();

            routePublisher.RegisterRoutes(routes);
        }

        #endregion
    }
}