using System;
using System.Diagnostics;
using System.IO;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Application;
using TweetDuck.Browser;
using TweetDuck.Browser.Adapters;
using TweetDuck.Browser.Handling;
using TweetDuck.Configuration;
using TweetDuck.Dialogs;
using TweetDuck.Management;
using TweetDuck.Updates;
using TweetDuck.Utils;
using TweetLib.Core;
using TweetLib.Core.Application;
using TweetLib.Core.Features;
using TweetLib.Core.Features.Chromium;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.TweetDeck;
using TweetLib.Core.Resources;
using TweetLib.Utils.Collections;
using TweetLib.Utils.Static;
using Win = System.Windows.Forms;

namespace TweetDuck {
	static class Program {
		public const string BrandName = Lib.BrandName;
		public const string VersionTag = Version.Tag;

		public const string Website = "https://tweetduck.chylex.com";

		private const string PluginDataFolder = "TD_Plugins";
		private const string InstallerFolder = "TD_Updates";
		private const string CefDataFolder = "TD_Chromium";

		private const string ProgramLogFile = "TD_Log.txt";
		private const string ConsoleLogFile = "TD_Console.txt";

		public static string ExecutablePath => Win.Application.ExecutablePath;

		public static uint WindowRestoreMessage;

		private static LockManager lockManager;
		private static Reporter errorReporter;
		private static bool hasCleanedUp;

		public static ConfigManager Config { get; private set; }

		internal static void SetupWinForms() {
			Win.Application.EnableVisualStyles();
			Win.Application.SetCompatibleTextRenderingDefault(false);
		}

		[STAThread]
		private static void Main() {
			AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;

			SetupWinForms();
			Cef.EnableHighDPISupport();

			var startup = new AppStartup {
				CustomDataFolder = Arguments.GetValue(Arguments.ArgDataFolder)
			};

			var reporter = new Reporter();
			var userConfig = new UserConfig();

			Lib.Initialize(new AppBuilder {
				Startup = startup,
				Logger = new Logger(ProgramLogFile),
				ErrorHandler = reporter,
				SystemHandler = new SystemHandler(),
				DialogHandler = new DialogHandler(),
				UserConfiguration = userConfig
			});

			LaunchApp(reporter, userConfig);
		}

		private static void LaunchApp(Reporter reporter, UserConfig userConfig) {
			App.Launch();

			errorReporter = reporter;
			string storagePath = App.StoragePath;

			Config = new ConfigManager(userConfig, new ConfigManager.Paths {
				UserConfig   = Path.Combine(storagePath, "TD_UserConfig.cfg"),
				SystemConfig = Path.Combine(storagePath, "TD_SystemConfig.cfg"),
				PluginConfig = Path.Combine(storagePath, "TD_PluginConfig.cfg")
			});

			lockManager = new LockManager(Path.Combine(storagePath, ".lock"));
			WindowRestoreMessage = NativeMethods.RegisterWindowMessage("TweetDuckRestore");

			if (!lockManager.Lock(Arguments.HasFlag(Arguments.ArgRestart))) {
				return;
			}

			Config.LoadAll();

			if (Arguments.HasFlag(Arguments.ArgImportCookies)) {
				ProfileManager.ImportCookies();
			}
			else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)) {
				ProfileManager.DeleteCookies();
			}

			var installerFolderPath = Path.Combine(storagePath, InstallerFolder);

			if (Arguments.HasFlag(Arguments.ArgUpdated)) {
				WindowsUtils.TryDeleteFolderWhenAble(installerFolderPath, 8000);
				WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(storagePath, "Service Worker"), 4000);
				BrowserCache.TryClearNow();
			}

			try {
				BaseResourceRequestHandler.LoadResourceRewriteRules(Arguments.GetValue(Arguments.ArgFreeze));
			} catch (Exception e) {
				FormMessage.Error("Resource Freeze", "Error parsing resource rewrite rules: " + e.Message, FormMessage.OK);
				return;
			}

			WebUtils.DefaultUserAgent = BrowserUtils.UserAgentVanilla;

			if (Config.User.UseSystemProxyForAllConnections) {
				WebUtils.EnableSystemProxy();
			}

			BrowserCache.RefreshTimer();

			CefSharpSettings.WcfEnabled = false;

			CefSettings settings = new CefSettings {
				UserAgent = BrowserUtils.UserAgentChrome,
				BrowserSubprocessPath = Path.Combine(App.ProgramPath, BrandName + ".Browser.exe"),
				CachePath = storagePath,
				UserDataPath = Path.Combine(storagePath, CefDataFolder),
				LogFile = Path.Combine(storagePath, ConsoleLogFile),
				#if !DEBUG
				LogSeverity = Arguments.HasFlag(Arguments.ArgLogging) ? LogSeverity.Info : LogSeverity.Disable
				#endif
			};

			var resourceProvider = new CachingResourceProvider<IResourceHandler>(new ResourceProvider());
			var pluginManager = new PluginManager(Config.Plugins, Path.Combine(storagePath, PluginDataFolder));

			CefSchemeHandlerFactory.Register(settings, new TweetDuckSchemeHandler<IResourceHandler>(resourceProvider));
			CefSchemeHandlerFactory.Register(settings, new PluginSchemeHandler<IResourceHandler>(resourceProvider, pluginManager));

			CefUtils.ParseCommandLineArguments(Config.User.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
			BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);

			Cef.Initialize(settings, false, new BrowserProcessHandler());

			Win.Application.ApplicationExit += (sender, args) => ExitCleanup();
			FormBrowser mainForm = new FormBrowser(resourceProvider, pluginManager, new UpdateCheckClient(installerFolderPath));
			Win.Application.Run(mainForm);

			if (mainForm.UpdateInstaller != null) {
				ExitCleanup();

				if (mainForm.UpdateInstaller.Launch()) {
					Win.Application.Exit();
				}
				else {
					RestartWithArgsInternal(Arguments.GetCurrentClean());
				}
			}
		}

		private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) {
			if (e.ExceptionObject is Exception ex) {
				AppException appEx = ex.GetBaseException() as AppException;
				string title = appEx?.Title ?? "TweetDuck Has Failed :(";
				string message = appEx?.Message ?? "An unhandled exception has occurred: " + ex.Message;

				if (errorReporter == null) {
					Debug.WriteLine(ex);
					Reporter.HandleEarlyFailure(title, message);
				}
				else {
					errorReporter.HandleException(title, message, false, ex);
				}
			}
		}

		public static void Restart(params string[] extraArgs) {
			CommandLineArgs args = Arguments.GetCurrentClean();
			CommandLineArgs.ReadStringArray('-', extraArgs, args);
			RestartWithArgs(args);
		}

		public static void RestartWithArgs(CommandLineArgs args) {
			FormBrowser browserForm = FormManager.TryFind<FormBrowser>();

			if (browserForm != null) {
				browserForm.ForceClose();

				ExitCleanup();
				RestartWithArgsInternal(args);
			}
		}

		private static void RestartWithArgsInternal(CommandLineArgs args) {
			args.AddFlag(Arguments.ArgRestart);
			Process.Start(ExecutablePath, args.ToString());
			Win.Application.Exit();
		}

		private static void ExitCleanup() {
			if (hasCleanedUp) {
				return;
			}

			Config.SaveAll();

			Cef.Shutdown();
			BrowserCache.Exit();

			lockManager.Unlock();
			hasCleanedUp = true;
		}
	}
}