using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Management;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.Win32;
using TweetDuck.Browser;
using TweetDuck.Configuration;
using TweetLib.Core;
using TweetLib.Core.Features.Notifications;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Utils;

namespace TweetDuck.Management.Analytics {
	static class AnalyticsReportGenerator {
		public static AnalyticsReport Create(AnalyticsFile file, ExternalInfo info, PluginManager plugins) {
			Dictionary<string, string> editLayoutDesign = EditLayoutDesignPluginData;

			return new AnalyticsReport {
				{ "App Version"   , Program.VersionTag },
				{ "App Type"      , Program.IsPortable ? "portable" : "installed" },
				{ "App Dev Tools" , Bool(UserConfig.DevToolsInContextMenu) },
				0,
				{ "System Name"        , SystemName },
				{ "System Edition"     , SystemEdition },
				{ "System Environment" , Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit" },
				{ "System Build"       , SystemBuild },
				{ "System Locale"      , Lib.Culture.Name.ToLower() },
				0,
				{ "RAM" , Exact(RamSize) },
				{ "GPU" , GpuVendor },
				0,
				{ "Screen Count"      , Exact(Screen.AllScreens.Length) },
				{ "Screen Resolution" , info.Resolution ?? "(unknown)" },
				{ "Screen DPI"        , info.DPI != null ? Exact(info.DPI.Value) : "(unknown)" },
				0,
				{ "Hardware Acceleration"     , Bool(SysConfig.HardwareAcceleration) },
				{ "Clear Cache Automatically" , Bool(SysConfig.ClearCacheAutomatically) },
				{ "Clear Cache Threshold"     , Exact(SysConfig.ClearCacheThreshold) },
				0,
				{ "Expand Links"                  , Bool(UserConfig.ExpandLinksOnHover) },
				{ "Search In First Column"        , Bool(UserConfig.OpenSearchInFirstColumn) },
				{ "Keep Like Follow Dialogs Open" , Bool(UserConfig.KeepLikeFollowDialogsOpen) },
				{ "Best Image Quality"            , Bool(UserConfig.BestImageQuality) },
				{ "Animated Images"               , Bool(UserConfig.EnableAnimatedImages) },
				0,
				{ "Smooth Scrolling" , Bool(UserConfig.EnableSmoothScrolling) },
				{ "Custom Browser"   , CustomBrowser },
				{ "Zoom"             , Exact(UserConfig.ZoomLevel) },
				0,
				{ "Spell Check"                 , Bool(UserConfig.EnableSpellCheck) },
				{ "Spell Check Language"        , UserConfig.SpellCheckLanguage.ToLower() },
				{ "Translation Target Language" , UserConfig.TranslationTarget },
				0,
				{ "Updates"          , Bool(UserConfig.EnableUpdateCheck) },
				{ "Update Dismissed" , Bool(!string.IsNullOrEmpty(UserConfig.DismissedUpdate)) },
				0,
				{ "Tray"           , TrayMode },
				{ "Tray Highlight" , Bool(UserConfig.EnableTrayHighlight) },
				0,
				{ "Notification Position"            , NotificationPosition },
				{ "Notification Size"                , NotificationSize },
				{ "Notification Timer"               , NotificationTimer },
				{ "Notification Timer Speed"         , RoundUp(UserConfig.NotificationDurationValue, 5) },
				{ "Notification Scroll Speed"        , Exact(UserConfig.NotificationScrollSpeed) },
				{ "Notification Column Title"        , Bool(UserConfig.DisplayNotificationColumn) },
				{ "Notification Media Previews"      , Bool(UserConfig.NotificationMediaPreviews) },
				{ "Notification Link Skip"           , Bool(UserConfig.NotificationSkipOnLinkClick) },
				{ "Notification Non-Intrusive"       , Bool(UserConfig.NotificationNonIntrusiveMode) },
				{ "Notification Idle Pause"          , Exact(UserConfig.NotificationIdlePauseSeconds) },
				{ "Custom Sound Notification"        , string.IsNullOrEmpty(UserConfig.NotificationSoundPath) ? "off" : Path.GetExtension(UserConfig.NotificationSoundPath) },
				{ "Custom Sound Notification Volume" , RoundUp(UserConfig.NotificationSoundVolume, 5) },
				0,
				{ "Program Arguments"       , List(ProgramArguments) },
				{ "Custom CEF Arguments"    , RoundUp((UserConfig.CustomCefArgs ?? string.Empty).Length, 10) },
				{ "Custom Browser CSS"      , RoundUp((UserConfig.CustomBrowserCSS ?? string.Empty).Length, 50) },
				{ "Custom Notification CSS" , RoundUp((UserConfig.CustomNotificationCSS ?? string.Empty).Length, 50) },
				0,
				{ "Plugins All"     , List(plugins.Plugins.Select(Plugin)) },
				{ "Plugins Enabled" , List(plugins.Plugins.Where(plugins.Config.IsEnabled).Select(Plugin)) },
				0,
				{ "Theme"               , Dict(editLayoutDesign, "_theme",                  "light/def") },
				{ "Column Width"        , Dict(editLayoutDesign, "columnWidth",             "310px/def") },
				{ "Font Size"           , Dict(editLayoutDesign, "fontSize",                "12px/def") },
				{ "Large Quote Font"    , Dict(editLayoutDesign, "increaseQuoteTextSize",   "false/def") },
				{ "Small Compose Font"  , Dict(editLayoutDesign, "smallComposeTextSize",    "false/def") },
				{ "Avatar Radius"       , Dict(editLayoutDesign, "avatarRadius",            "2/def") },
				{ "Hide Tweet Actions"  , Dict(editLayoutDesign, "hideTweetActions",        "true/def") },
				{ "Move Tweet Actions"  , Dict(editLayoutDesign, "moveTweetActionsToRight", "true/def") },
				{ "Theme Color Tweaks"  , Dict(editLayoutDesign, "themeColorTweaks",        "true/def") },
				{ "Revert Icons"        , Dict(editLayoutDesign, "revertIcons",             "true/def") },
				{ "Optimize Animations" , Dict(editLayoutDesign, "optimizeAnimations",      "true/def") },
				{ "Reply Account Mode"  , ReplyAccountConfigFromPlugin },
				{ "Template Count"      , Exact(TemplateCountFromPlugin) },
				0,
				{ "Opened Options"                   , LogRound(file.OpenOptions, 4) },
				{ "Opened Plugins"                   , LogRound(file.OpenPlugins, 4) },
				{ "Opened About"                     , LogRound(file.OpenAbout, 4) },
				{ "Opened Guide"                     , LogRound(file.OpenGuide, 4) },
				{ "Desktop Notifications"            , LogRound(file.DesktopNotifications, 5) },
				{ "Sound Notifications"              , LogRound(file.SoundNotifications, 5) },
				{ "Notification Mutes"               , LogRound(file.NotificationMutes, 2) },
				{ "Browser Context Menus"            , LogRound(file.BrowserContextMenus, 2) },
				{ "Browser Extra Mouse Buttons"      , LogRound(file.BrowserExtraMouseButtons, 2) },
				{ "Notification Context Menus"       , LogRound(file.NotificationContextMenus, 2) },
				{ "Notification Extra Mouse Buttons" , LogRound(file.NotificationExtraMouseButtons, 2) },
				{ "Notification Keyboard Shortcuts"  , LogRound(file.NotificationKeyboardShortcuts, 2) },
				{ "Browser Reloads"                  , LogRound(file.BrowserReloads, 2) },
				{ "Copied Usernames"                 , LogRound(file.CopiedUsernames, 2) },
				{ "Viewed Images"                    , LogRound(file.ViewedImages, 2) },
				{ "Downloaded Images"                , LogRound(file.DownloadedImages, 2) },
				{ "Downloaded Videos"                , LogRound(file.DownloadedVideos, 2) },
				{ "Used ROT13"                       , LogRound(file.UsedROT13, 2) },
				{ "Tweet Screenshots"                , LogRound(file.TweetScreenshots, 2) },
				{ "Tweet Details"                    , LogRound(file.TweetDetails, 2) },
				{ "Video Plays"                      , LogRound(file.VideoPlays, 4) }
			}.FinalizeReport();
		}

		private static UserConfig UserConfig => Program.Config.User;
		private static SystemConfig SysConfig => Program.Config.System;

		private static string Bool(bool value) => value ? "on" : "off";
		private static string Exact(int value) => value.ToString();
		private static string RoundUp(int value, int multiple) => (multiple * (int) Math.Ceiling((double) value / multiple)).ToString();
		private static string LogRound(int value, int logBase) => (value <= 0 ? 0 : (int) Math.Pow(logBase, Math.Floor(Math.Log(value, logBase)))).ToString();
		private static string Plugin(Plugin plugin) => plugin.Group.GetIdentifierPrefixShort() + plugin.Identifier.Substring(plugin.Group.GetIdentifierPrefix().Length);
		private static string Dict(Dictionary<string, string> dict, string key, string def = "(unknown)") => dict.TryGetValue(key, out string value) ? value : def;
		private static string List(IEnumerable<string> list) => string.Join("|", list.DefaultIfEmpty("(none)"));

		private static string SystemName { get; }
		private static string SystemEdition { get; }
		private static string SystemBuild { get; }
		private static int RamSize { get; }
		private static string GpuVendor { get; }
		private static string[] ProgramArguments { get; }

		static AnalyticsReportGenerator() {
			string osName, osEdition, osBuild;

			try {
				using RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false);

				// ReSharper disable once PossibleNullReferenceException
				osName = key.GetValue("ProductName") as string;
				osBuild = key.GetValue("CurrentBuild") as string;
				osEdition = null;

				if (osName != null) {
					Match match = Regex.Match(osName, @"^(.*?\d+(?:\.\d+)?) (.*)$");

					if (match.Success) {
						osName = match.Groups[1].Value;
						osEdition = match.Groups[2].Value;
					}
				}
			} catch {
				osName = osEdition = osBuild = null;
			}

			SystemName = osName ?? "Windows (unknown)";
			SystemEdition = osEdition ?? "(unknown)";
			SystemBuild = osBuild ?? "(unknown)";

			try {
				using ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Capacity FROM Win32_PhysicalMemory");

				foreach (ManagementBaseObject obj in searcher.Get()) {
					RamSize += (int) ((ulong) obj["Capacity"] / (1024L * 1024L));
				}
			} catch {
				RamSize = 0;
			}

			string gpu = null;

			try {
				using ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT Caption FROM Win32_VideoController");

				foreach (ManagementBaseObject obj in searcher.Get()) {
					string vendor = obj["Caption"] as string;

					if (!string.IsNullOrEmpty(vendor)) {
						gpu = vendor;
					}
				}
			} catch {
				// rip
			}

			GpuVendor = gpu ?? "(unknown)";

			Dictionary<string, string> args = new Dictionary<string, string>();
			Arguments.GetCurrentClean().ToDictionary(args);
			ProgramArguments = args.Keys.Select(key => key.TrimStart('-')).ToArray();
		}

		private static string CustomBrowser {
			get { return Path.GetFileName(UserConfig.BrowserPath) ?? string.Empty; }
		}

		private static string TrayMode {
			get => UserConfig.TrayBehavior switch {
				TrayIcon.Behavior.DisplayOnly    => "icon",
				TrayIcon.Behavior.MinimizeToTray => "minimize",
				TrayIcon.Behavior.CloseToTray    => "close",
				TrayIcon.Behavior.Combined       => "combined",
				_                                => "off"
			};
		}

		private static string NotificationPosition {
			get => UserConfig.NotificationPosition switch {
				DesktopNotification.Position.TopLeft     => "top left",
				DesktopNotification.Position.TopRight    => "top right",
				DesktopNotification.Position.BottomLeft  => "bottom left",
				DesktopNotification.Position.BottomRight => "bottom right",
				_                                        => "custom"
			};
		}

		private static string NotificationSize {
			get => UserConfig.NotificationSize switch {
				DesktopNotification.Size.Auto => "auto",
				_                             => RoundUp(UserConfig.CustomNotificationSize.Width, 20) + "x" + RoundUp(UserConfig.CustomNotificationSize.Height, 20)
			};
		}

		private static string NotificationTimer {
			get {
				if (!UserConfig.DisplayNotificationTimer) {
					return "off";
				}
				else {
					return UserConfig.NotificationTimerCountDown ? "count down" : "count up";
				}
			}
		}

		private static Dictionary<string, string> EditLayoutDesignPluginData {
			get {
				Dictionary<string, string> dict = new Dictionary<string, string>();

				try {
					string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "edit-design", "config.json"));

					foreach (Match match in Regex.Matches(data, "\"(\\w+?)\":(.*?)[,}]")) {
						dict[match.Groups[1].Value] = match.Groups[2].Value.Trim('"');
					}
				} catch {
					// rip
				}

				return dict;
			}
		}

		private static int TemplateCountFromPlugin {
			get {
				try {
					string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "templates", "config.json"));
					return Math.Min(StringUtils.CountOccurrences(data, "{\"name\":"), StringUtils.CountOccurrences(data, ",\"contents\":"));
				} catch {
					return 0;
				}
			}
		}

		private static string ReplyAccountConfigFromPlugin {
			get {
				try {
					string data = File.ReadAllText(Path.Combine(Program.PluginDataPath, "official", "reply-account", "configuration.js")).Replace(" ", "");

					Match matchType = Regex.Match(data, "defaultAccount:\"([#@])(.*?)\"(?:,|$)");
					Match matchAdvanced = Regex.Match(data, "useAdvancedSelector:(.*?)(?:,|$)", RegexOptions.Multiline);

					if (!matchType.Success) {
						return data.Contains("defaultAccount:\"\"") ? "(legacy)" : "(unknown)";
					}

					string accType = matchType.Groups[1].Value == "#" ? matchType.Groups[2].Value : "account";
					return matchAdvanced.Success && !matchAdvanced.Value.Contains("false") ? "advanced/" + accType : accType;
				} catch {
					return "(unknown)";
				}
			}
		}

		public class ExternalInfo {
			public static ExternalInfo From(Form form) {
				if (form == null) {
					return new ExternalInfo();
				}
				else {
					Screen screen = Screen.FromControl(form); // works on multi-monitor setups even in tray
					int dpi;

					using (Graphics graphics = form.CreateGraphics()) {
						dpi = (int) graphics.DpiY;
					}

					return new ExternalInfo {
						Resolution = screen.Bounds.Width + "x" + screen.Bounds.Height,
						DPI = dpi
					};
				}
			}

			public string Resolution { get; private set; }
			public int? DPI { get; private set; }

			private ExternalInfo() {}
		}
	}
}