using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using ZeroLevel.Services.Applications;
namespace ZeroLevel
{
    public class Bootstrap
    {
        static Bootstrap()
        {
            // Tricks for minimize config changes for dependency resolve
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
        }
        private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            try
            {
                Log.Debug($"[Bootstrap] Resolve assembly '{args.Name}'");
                if (args.Name.StartsWith("Newtonsoft.Json", StringComparison.Ordinal))
                {
                    return Assembly.LoadFile(Path.Combine(Configuration.BaseDirectory, "Newtonsoft.Json.dll"));
                }
                else if (args.Name.Equals("Microsoft.Owin", StringComparison.Ordinal))
                {
                    return Assembly.LoadFile(Path.Combine(Configuration.BaseDirectory, "Microsoft.Owin.dll"));
                }
                var candidates = Directory.GetFiles(Path.Combine(Configuration.BaseDirectory), args.Name, SearchOption.TopDirectoryOnly);
                if (candidates != null && candidates.Any())
                {
                    return Assembly.LoadFile(candidates.First());
                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, $"[Bootstrap] Fault load assembly '{args.Name}'");
            }
            return null;
        }
        /// 
        /// Self-install as windows service
        /// 
        private static void InstallApplication()
        {
            try
            {
                Configuration.Save(Configuration.ReadFromApplicationConfig());
                Log.AddTextFileLogger("install.log");
                BasicServiceInstaller.Install(Configuration.Default);
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "[Bootstrap] Fault service install");
            }
        }
        /// 
        /// Uninstall from windows services
        /// 
        private static void UninstallApplication()
        {
            try
            {
                Configuration.Save(Configuration.ReadFromApplicationConfig());
                Log.AddTextFileLogger("uninstall.log");
                BasicServiceInstaller.Uninstall(Configuration.Default);
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "[Bootstrap] Fault service uninstall");
            }
        }
        public static void Startup(string[] args, Func preStartConfiguration = null, Func postStartConfiguration = null)
            where T : IZeroService, new()
        {
            var cmd = Configuration.ReadFromCommandLine(args);
            if (cmd.Contains("install", "setup"))
            {
                InstallApplication();
            }
            else if (cmd.Contains("uninstall", "remove"))
            {
                UninstallApplication();
            }
            else
            {
                Configuration.Save(Configuration.ReadFromApplicationConfig());
                Log.CreateLoggingFromConfiguration(Configuration.Default);
                IZeroService service = null;
                if (preStartConfiguration != null)
                {
                    try
                    {
                        if (preStartConfiguration() == false)
                        {
                            Log.SystemInfo("[Bootstrap] Service start canceled, because custom preconfig return false");
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.SystemError(ex, "[Bootstrap] Service start canceled, preconfig faulted");
                        return;
                    }
                }
                try
                {
                    service = new T();
                }
                catch (Exception ex)
                {
                    Log.SystemError(ex, "[Bootstrap] Service start canceled, service constructor call fault");
                }
                if (postStartConfiguration != null)
                {
                    try
                    {
                        if (postStartConfiguration() == false)
                        {
                            Log.SystemInfo("[Bootstrap] Service start canceled, because custom postconfig return false");
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.SystemError(ex, "[Bootstrap] Service start canceled, postconfig faulted");
                        return;
                    }
                }
                if (Environment.UserInteractive)
                {
                    try
                    {
                        Log.Debug("[Bootstrap] The service starting (interactive mode)");
                        service?.InteractiveStart(args);
                        Log.Debug("[Bootstrap] The service stopped (interactive mode)");
                    }
                    catch (Exception ex)
                    {
                        Log.Fatal(ex, "[Bootstrap] The service start in interactive mode was faulted with error");
                    }
                }
                else
                {
                    try
                    {
                        Log.Debug("[Bootstrap] The service starting (windows service)");
                        ServiceBase.Run(new ServiceBase[] { service as ServiceBase });
                        Log.Debug("[Bootstrap] The service stopped (windows service)");
                    }
                    catch (Exception ex)
                    {
                        Log.Fatal(ex, "[Bootstrap] The service start was faulted with error");
                    }
                }
            }
            try { Sheduller.Dispose(); } catch { }
            try { Log.Dispose(); } catch { }
            try { Injector.Default.Dispose(); Injector.Dispose(); } catch { }
        }
    }
}