// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;

using global::PowerToys.GPOWrapper;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
using Microsoft.PowerToys.Settings.UI.SerializationContext;
using Microsoft.PowerToys.Telemetry;
using Microsoft.Win32;
using Windows.System.Profile;

namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
    public partial class GeneralViewModel : PageViewModelBase
    {
        public enum InstallScope
        {
            PerMachine = 0,
            PerUser,
        }

        protected override string ModuleName => "GeneralSettings";

        public override Dictionary<string, HotkeySettings[]> GetAllHotkeySettings()
        {
            return new Dictionary<string, HotkeySettings[]>
            {
                { ModuleName, new HotkeySettings[] { QuickAccessShortcut } },
            };
        }

        private GeneralSettings GeneralSettingsConfig { get; set; }

        private UpdatingSettings UpdatingSettingsConfig { get; set; }

        public ButtonClickCommand CheckForUpdatesEventHandler { get; set; }

        public Windows.ApplicationModel.Resources.ResourceLoader ResourceLoader { get; set; }

        private Action HideBackupAndRestoreMessageAreaAction { get; set; }

        private Action<int> DoBackupAndRestoreDryRun { get; set; }

        public ButtonClickCommand BackupConfigsEventHandler { get; set; }

        public ButtonClickCommand RestoreConfigsEventHandler { get; set; }

        public ButtonClickCommand RefreshBackupStatusEventHandler { get; set; }

        public ButtonClickCommand SelectSettingBackupDirEventHandler { get; set; }

        public ButtonClickCommand RestartElevatedButtonEventHandler { get; set; }

        public ButtonClickCommand UpdateNowButtonEventHandler { get; set; }

        public Func<string, int> SendConfigMSG { get; }

        public Func<string, int> SendRestartAsAdminConfigMSG { get; }

        public Func<string, int> SendCheckForUpdatesConfigMSG { get; }

        public string RunningAsUserDefaultText { get; set; }

        public string RunningAsAdminDefaultText { get; set; }

        private string _settingsConfigFileFolder = string.Empty;

        private ISettingsRepository<GeneralSettings> _settingsRepository;
        private Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue;

        private IFileSystemWatcher _fileWatcher;

        private Func<Task<string>> PickSingleFolderDialog { get; }

        private SettingsBackupAndRestoreUtils settingsBackupAndRestoreUtils = SettingsBackupAndRestoreUtils.Instance;

        private const string InstallScopeRegKey = @"Software\Classes\powertoys\";

        public GeneralViewModel(ISettingsRepository<GeneralSettings> settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func<string, int> ipcMSGCallBackFunc, Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc, Func<string, int> ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "", Action dispatcherAction = null, Action hideBackupAndRestoreMessageAreaAction = null, Action<int> doBackupAndRestoreDryRun = null, Func<Task<string>> pickSingleFolderDialog = null, Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = null)
        {
            CheckForUpdatesEventHandler = new ButtonClickCommand(CheckForUpdatesClick);
            RestartElevatedButtonEventHandler = new ButtonClickCommand(RestartElevated);
            UpdateNowButtonEventHandler = new ButtonClickCommand(UpdateNowClick);
            BackupConfigsEventHandler = new ButtonClickCommand(BackupConfigsClick);
            SelectSettingBackupDirEventHandler = new ButtonClickCommand(SelectSettingBackupDir);
            RestoreConfigsEventHandler = new ButtonClickCommand(RestoreConfigsClick);
            RefreshBackupStatusEventHandler = new ButtonClickCommand(RefreshBackupStatusEventHandlerClick);
            HideBackupAndRestoreMessageAreaAction = hideBackupAndRestoreMessageAreaAction;
            DoBackupAndRestoreDryRun = doBackupAndRestoreDryRun;
            PickSingleFolderDialog = pickSingleFolderDialog;
            ResourceLoader = resourceLoader;

            // To obtain the general settings configuration of PowerToys if it exists, else to create a new file and return the default configurations.
            ArgumentNullException.ThrowIfNull(settingsRepository);

            _settingsRepository = settingsRepository;
            _settingsRepository.SettingsChanged += OnSettingsChanged;
            _dispatcherQueue = GetDispatcherQueue();

            GeneralSettingsConfig = settingsRepository.SettingsConfig;
            UpdatingSettingsConfig = UpdatingSettings.LoadSettings();
            if (UpdatingSettingsConfig == null)
            {
                UpdatingSettingsConfig = new UpdatingSettings();
            }

            // set the callback functions value to handle outgoing IPC message.
            SendConfigMSG = ipcMSGCallBackFunc;
            SendCheckForUpdatesConfigMSG = ipcMSGCheckForUpdatesCallBackFunc;
            SendRestartAsAdminConfigMSG = ipcMSGRestartAsAdminMSGCallBackFunc;

            // Update Settings file folder:
            _settingsConfigFileFolder = configFileSubfolder;

            // Using Invariant here as these are internal strings and the analyzer
            // expects strings to be normalized to uppercase. While the theme names
            // are represented in lowercase everywhere else, we'll use uppercase
            // normalization for switch statements
            switch (GeneralSettingsConfig.Theme.ToUpperInvariant())
            {
                case "DARK":
                    _themeIndex = 0;
                    break;
                case "LIGHT":
                    _themeIndex = 1;
                    break;
                case "SYSTEM":
                    _themeIndex = 2;
                    break;
            }

            _isDevBuild = Helper.GetProductVersion() == "v0.0.1";

            _runAtStartupGpoRuleConfiguration = GPOWrapper.GetConfiguredRunAtStartupValue();
            if (_runAtStartupGpoRuleConfiguration == GpoRuleConfigured.Disabled || _runAtStartupGpoRuleConfiguration == GpoRuleConfigured.Enabled)
            {
                // Get the enabled state from GPO.
                _runAtStartupIsGPOConfigured = true;
                _startup = _runAtStartupGpoRuleConfiguration == GpoRuleConfigured.Enabled;
            }
            else
            {
                _startup = GeneralSettingsConfig.Startup;
            }

            _showSysTrayIcon = GeneralSettingsConfig.ShowSysTrayIcon;
            _showThemeAdaptiveSysTrayIcon = GeneralSettingsConfig.ShowThemeAdaptiveTrayIcon;
            _showNewUpdatesToastNotification = GeneralSettingsConfig.ShowNewUpdatesToastNotification;
            _autoDownloadUpdates = GeneralSettingsConfig.AutoDownloadUpdates;
            _showWhatsNewAfterUpdates = GeneralSettingsConfig.ShowWhatsNewAfterUpdates;
            _enableExperimentation = GeneralSettingsConfig.EnableExperimentation;

            _isElevated = isElevated;
            _runElevated = GeneralSettingsConfig.RunElevated;
            _enableWarningsElevatedApps = GeneralSettingsConfig.EnableWarningsElevatedApps;
            _enableQuickAccess = GeneralSettingsConfig.EnableQuickAccess;
            _quickAccessShortcut = GeneralSettingsConfig.QuickAccessShortcut;
            if (_quickAccessShortcut != null)
            {
                _quickAccessShortcut.PropertyChanged += QuickAccessShortcut_PropertyChanged;
            }

            RunningAsUserDefaultText = runAsUserText;
            RunningAsAdminDefaultText = runAsAdminText;

            _isAdmin = isAdmin;

            _updatingState = UpdatingSettingsConfig.State;
            _newAvailableVersion = UpdatingSettingsConfig.NewVersion;
            _newAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
            _updateCheckedDate = FriendlyDateHelper.Format(UpdatingSettingsConfig.LastCheckedDateTime);

            _newUpdatesToastIsGpoDisabled = GPOWrapper.GetDisableNewUpdateToastValue() == GpoRuleConfigured.Enabled;
            _autoDownloadUpdatesIsGpoDisabled = GPOWrapper.GetDisableAutomaticUpdateDownloadValue() == GpoRuleConfigured.Enabled;
            _experimentationIsGpoDisallowed = GPOWrapper.GetAllowExperimentationValue() == GpoRuleConfigured.Disabled;
            _showWhatsNewAfterUpdatesIsGpoDisabled = GPOWrapper.GetDisableShowWhatsNewAfterUpdatesValue() == GpoRuleConfigured.Enabled;
            _enableDataDiagnosticsIsGpoDisallowed = GPOWrapper.GetAllowDataDiagnosticsValue() == GpoRuleConfigured.Disabled;

            if (_enableDataDiagnosticsIsGpoDisallowed)
            {
                _enableDataDiagnostics = false;
            }
            else
            {
                _enableDataDiagnostics = DataDiagnosticsSettings.GetEnabledValue();
            }

            _enableViewDataDiagnostics = DataDiagnosticsSettings.GetViewEnabledValue();
            _enableViewDataDiagnosticsOnLoad = _enableViewDataDiagnostics;

            if (dispatcherAction != null)
            {
                _fileWatcher = Helper.GetFileWatcher(string.Empty, UpdatingSettings.SettingsFile, dispatcherAction);
            }

            // Diagnostic data retention policy
            string etwDirPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\etw");
            DeleteDiagnosticDataOlderThan28Days(etwDirPath);

            string localLowEtwDirPath = Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), "AppData", "LocalLow", "Microsoft", "PowerToys", "etw");
            DeleteDiagnosticDataOlderThan28Days(localLowEtwDirPath);

            InitializeLanguages();
        }

        // Supported languages. Taken from Resources.wxs + default + en-US
        private Dictionary<string, string> langTagsAndIds = new Dictionary<string, string>
        {
            { string.Empty, "Default_Language" },
            { "ar-SA", "Arabic_Saudi_Arabia_Language" },
            { "cs-CZ", "Czech_Language" },
            { "de-DE", "German_Language" },
            { "en-US", "English_Language" },
            { "es-ES", "Spanish_Language" },
            { "fa-IR", "Persian_Farsi_Language" },
            { "fr-FR", "French_Language" },
            { "he-IL", "Hebrew_Israel_Language" },
            { "hu-HU", "Hungarian_Language" },
            { "it-IT", "Italian_Language" },
            { "ja-JP", "Japanese_Language" },
            { "ko-KR", "Korean_Language" },
            { "nl-NL", "Dutch_Language" },
            { "pl-PL", "Polish_Language" },
            { "pt-BR", "Portuguese_Brazil_Language" },
            { "pt-PT", "Portuguese_Portugal_Language" },
            { "ru-RU", "Russian_Language" },
            { "sv-SE", "Swedish_Language" },
            { "tr-TR", "Turkish_Language" },
            { "uk-UA", "Ukrainian_Language" },
            { "zh-CN", "Chinese_Simplified_Language" },
            { "zh-TW", "Chinese_Traditional_Language" },
        };

        private static bool _isDevBuild;
        private bool _startup;
        private bool _showSysTrayIcon;
        private bool _showThemeAdaptiveSysTrayIcon;
        private GpoRuleConfigured _runAtStartupGpoRuleConfiguration;
        private bool _runAtStartupIsGPOConfigured;
        private bool _isElevated;
        private bool _runElevated;
        private bool _isAdmin;
        private bool _enableWarningsElevatedApps;
        private bool _enableQuickAccess;
        private HotkeySettings _quickAccessShortcut;
        private int _themeIndex;

        private bool _showNewUpdatesToastNotification;
        private bool _newUpdatesToastIsGpoDisabled;
        private bool _autoDownloadUpdates;
        private bool _autoDownloadUpdatesIsGpoDisabled;
        private bool _showWhatsNewAfterUpdates;
        private bool _showWhatsNewAfterUpdatesIsGpoDisabled;
        private bool _enableExperimentation;
        private bool _experimentationIsGpoDisallowed;
        private bool _enableDataDiagnostics;
        private bool _enableDataDiagnosticsIsGpoDisallowed;
        private bool _enableViewDataDiagnostics;
        private bool _enableViewDataDiagnosticsOnLoad;
        private bool _viewDiagnosticDataViewerChanged;

        private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate;
        private string _newAvailableVersion = string.Empty;
        private string _newAvailableVersionLink = string.Empty;
        private string _updateCheckedDate = string.Empty;

        private bool _isNewVersionDownloading;
        private bool _isNewVersionChecked;
        private bool _isNoNetwork;
        private bool _isBugReportRunning;

        private bool _settingsBackupRestoreMessageVisible;
        private string _settingsBackupMessage;
        private string _backupRestoreMessageSeverity;

        private int _languagesIndex;
        private int _initLanguagesIndex;
        private bool _languageChanged;

        private string reportBugLink;

        // Gets or sets a value indicating whether run powertoys on start-up.
        public string ReportBugLink
        {
            get => reportBugLink;
            set
            {
                reportBugLink = value;
                OnPropertyChanged(nameof(ReportBugLink));
            }
        }

        public void InitializeReportBugLink()
        {
            var version = GetPowerToysVersion();

            string isElevatedString = "PowerToys is running " + (IsElevated ? "as admin (elevated)" : "as user (non-elevated)");

            string installScope = GetCurrentInstallScope() == InstallScope.PerMachine ? "per machine (system)" : "per user";

            var info = $"OS Version: {GetOSVersion()} \n.NET Version: {GetDotNetVersion()}\n{isElevatedString}\nInstall scope: {installScope}\nOperating System Language: {CultureInfo.InstalledUICulture.DisplayName}\nSystem locale: {CultureInfo.InstalledUICulture.Name}";

            var gitHubURL = "https://github.com/microsoft/PowerToys/issues/new?template=bug_report.yml&labels=Issue-Bug%2CTriage-Needed" +
                "&version=" + version + "&additionalInfo=" + System.Web.HttpUtility.UrlEncode(info);

            ReportBugLink = gitHubURL;
        }

        private string GetPowerToysVersion()
        {
            return Helper.GetProductVersion().TrimStart('v');
        }

        private string GetOSVersion()
        {
            return Environment.OSVersion.VersionString;
        }

        public static string GetDotNetVersion()
        {
            return $".NET {Environment.Version}";
        }

        public static InstallScope GetCurrentInstallScope()
        {
            // Check HKLM first
            if (Registry.LocalMachine.OpenSubKey(InstallScopeRegKey) != null)
            {
                return InstallScope.PerMachine;
            }

            // If not found, check HKCU
            var userKey = Registry.CurrentUser.OpenSubKey(InstallScopeRegKey);
            if (userKey != null)
            {
                var installScope = userKey.GetValue("InstallScope") as string;
                userKey.Close();
                if (!string.IsNullOrEmpty(installScope) && installScope.Contains("perUser"))
                {
                    return InstallScope.PerUser;
                }
            }

            return InstallScope.PerMachine; // Default if no specific registry key found
        }

        // Gets or sets a value indicating whether run powertoys on start-up.
        public bool Startup
        {
            get
            {
                return _startup;
            }

            set
            {
                if (_runAtStartupIsGPOConfigured)
                {
                    // If it's GPO configured, shouldn't be able to change this state.
                    return;
                }

                if (_startup != value)
                {
                    _startup = value;
                    GeneralSettingsConfig.Startup = value;
                    NotifyPropertyChanged();
                }
            }
        }

        // Gets or sets a value indicating whether the PowerToys icon should be shown in the system tray.
        public bool ShowSysTrayIcon
        {
            get
            {
                return _showSysTrayIcon;
            }

            set
            {
                if (_showSysTrayIcon != value)
                {
                    _showSysTrayIcon = value;
                    GeneralSettingsConfig.ShowSysTrayIcon = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool ShowThemeAdaptiveTrayIcon
        {
            get
            {
                return _showThemeAdaptiveSysTrayIcon;
            }

            set
            {
                if (_showThemeAdaptiveSysTrayIcon != value)
                {
                    _showThemeAdaptiveSysTrayIcon = value;
                    GeneralSettingsConfig.ShowThemeAdaptiveTrayIcon = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public string RunningAsText
        {
            get
            {
                if (!IsElevated)
                {
                    return RunningAsUserDefaultText;
                }
                else
                {
                    return RunningAsAdminDefaultText;
                }
            }

            set
            {
                OnPropertyChanged("RunningAsAdminText");
            }
        }

        // Gets or sets a value indicating whether the powertoy elevated.
        public bool IsElevated
        {
            get
            {
                return _isElevated;
            }

            set
            {
                if (_isElevated != value)
                {
                    _isElevated = value;
                    OnPropertyChanged(nameof(IsElevated));
                    OnPropertyChanged(nameof(IsAdminButtonEnabled));
                    OnPropertyChanged("RunningAsAdminText");
                }
            }
        }

        public bool IsAdminButtonEnabled
        {
            get
            {
                return !IsElevated;
            }

            set
            {
                OnPropertyChanged(nameof(IsAdminButtonEnabled));
            }
        }

        // Gets or sets a value indicating whether powertoys should run elevated.
        public bool RunElevated
        {
            get
            {
                return _runElevated;
            }

            set
            {
                if (_runElevated != value)
                {
                    _runElevated = value;
                    GeneralSettingsConfig.RunElevated = value;
                    NotifyPropertyChanged();
                }
            }
        }

        // Gets a value indicating whether the user is part of administrators group.
        public bool IsAdmin
        {
            get
            {
                return _isAdmin;
            }
        }

        public bool EnableWarningsElevatedApps
        {
            get
            {
                return _enableWarningsElevatedApps;
            }

            set
            {
                if (_enableWarningsElevatedApps != value)
                {
                    _enableWarningsElevatedApps = value;
                    GeneralSettingsConfig.EnableWarningsElevatedApps = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool EnableQuickAccess
        {
            get
            {
                return _enableQuickAccess;
            }

            set
            {
                if (_enableQuickAccess != value)
                {
                    _enableQuickAccess = value;
                    GeneralSettingsConfig.EnableQuickAccess = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public HotkeySettings QuickAccessShortcut
        {
            get
            {
                return _quickAccessShortcut;
            }

            set
            {
                if (_quickAccessShortcut != value)
                {
                    if (_quickAccessShortcut != null)
                    {
                        _quickAccessShortcut.PropertyChanged -= QuickAccessShortcut_PropertyChanged;
                    }

                    _quickAccessShortcut = value;
                    if (_quickAccessShortcut != null)
                    {
                        _quickAccessShortcut.PropertyChanged += QuickAccessShortcut_PropertyChanged;
                    }

                    GeneralSettingsConfig.QuickAccessShortcut = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private void QuickAccessShortcut_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            NotifyPropertyChanged(nameof(QuickAccessShortcut));
        }

        public bool SomeUpdateSettingsAreGpoManaged
        {
            get
            {
                return _newUpdatesToastIsGpoDisabled ||
                    (_isAdmin && _autoDownloadUpdatesIsGpoDisabled) ||
                    _showWhatsNewAfterUpdatesIsGpoDisabled;
            }
        }

        public bool ShowNewUpdatesToastNotification
        {
            get
            {
                return _showNewUpdatesToastNotification && !_newUpdatesToastIsGpoDisabled;
            }

            set
            {
                if (_showNewUpdatesToastNotification != value)
                {
                    _showNewUpdatesToastNotification = value;
                    GeneralSettingsConfig.ShowNewUpdatesToastNotification = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool IsShowNewUpdatesToastNotificationCardEnabled
        {
            get => !_isDevBuild && !_newUpdatesToastIsGpoDisabled;
        }

        public bool AutoDownloadUpdates
        {
            get
            {
                return _autoDownloadUpdates && !_autoDownloadUpdatesIsGpoDisabled;
            }

            set
            {
                if (_autoDownloadUpdates != value)
                {
                    _autoDownloadUpdates = value;
                    GeneralSettingsConfig.AutoDownloadUpdates = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool IsAutoDownloadUpdatesCardEnabled
        {
            get => !_isDevBuild && !_autoDownloadUpdatesIsGpoDisabled;
        }

        public bool ShowWhatsNewAfterUpdates
        {
            get
            {
                return _showWhatsNewAfterUpdates && !_showWhatsNewAfterUpdatesIsGpoDisabled;
            }

            set
            {
                if (_showWhatsNewAfterUpdates != value)
                {
                    _showWhatsNewAfterUpdates = value;
                    GeneralSettingsConfig.ShowWhatsNewAfterUpdates = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool IsShowWhatsNewAfterUpdatesCardEnabled
        {
            get => !_isDevBuild && !_showWhatsNewAfterUpdatesIsGpoDisabled;
        }

        public bool EnableExperimentation
        {
            get
            {
                return _enableExperimentation && !_experimentationIsGpoDisallowed;
            }

            set
            {
                if (_enableExperimentation != value)
                {
                    _enableExperimentation = value;
                    GeneralSettingsConfig.EnableExperimentation = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool EnableDataDiagnostics
        {
            get
            {
                return _enableDataDiagnostics;
            }

            set
            {
                if (_enableDataDiagnostics != value)
                {
                    _enableDataDiagnostics = value;

                    if (_enableDataDiagnostics == false)
                    {
                        EnableViewDataDiagnostics = false;
                    }

                    DataDiagnosticsSettings.SetEnabledValue(_enableDataDiagnostics);
                    NotifyPropertyChanged();
                }
            }
        }

        public bool ViewDiagnosticDataViewerChanged
        {
            get => _viewDiagnosticDataViewerChanged;
        }

        public bool EnableViewDataDiagnostics
        {
            get
            {
                return _enableViewDataDiagnostics;
            }

            set
            {
                if (_enableViewDataDiagnostics != value)
                {
                    _enableViewDataDiagnostics = value;

                    if (_enableViewDataDiagnostics != _enableViewDataDiagnosticsOnLoad)
                    {
                        _viewDiagnosticDataViewerChanged = true;
                    }
                    else
                    {
                        _viewDiagnosticDataViewerChanged = false;
                    }

                    DataDiagnosticsSettings.SetViewEnabledValue(_enableViewDataDiagnostics);
                    OnPropertyChanged(nameof(EnableViewDataDiagnostics));
                    OnPropertyChanged(nameof(ViewDiagnosticDataViewerChanged));
                }
            }
        }

        public bool IsExperimentationGpoDisallowed
        {
            get => _experimentationIsGpoDisallowed;
        }

        public bool IsDataDiagnosticsGPOManaged
        {
            get => _enableDataDiagnosticsIsGpoDisallowed;
        }

        public bool IsRunAtStartupGPOManaged
        {
            get => _runAtStartupIsGPOConfigured;
        }

        public string SettingsBackupAndRestoreDir
        {
            get
            {
                return settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();
            }

            set
            {
                if (settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir() != value)
                {
                    SettingsBackupAndRestoreUtils.SetRegSettingsBackupAndRestoreItem("SettingsBackupAndRestoreDir", value);
                    NotifyPropertyChanged();
                }
            }
        }

        public int ThemeIndex
        {
            get
            {
                return _themeIndex;
            }

            set
            {
                if (_themeIndex != value)
                {
                    switch (value)
                    {
                        case 0: GeneralSettingsConfig.Theme = "dark"; break;
                        case 1: GeneralSettingsConfig.Theme = "light"; break;
                        case 2: GeneralSettingsConfig.Theme = "system"; break;
                    }

                    _themeIndex = value;

                    App.ThemeService.ApplyTheme();

                    NotifyPropertyChanged();
                }
            }
        }

        public string PowerToysVersion
        {
            get
            {
                return Helper.GetProductVersion();
            }
        }

        public string UpdateCheckedDate
        {
            get
            {
                RequestUpdateCheckedDate();
                return _updateCheckedDate;
            }

            set
            {
                if (_updateCheckedDate != value)
                {
                    _updateCheckedDate = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public string LastSettingsBackupDate
        {
            get
            {
                try
                {
                    var manifest = settingsBackupAndRestoreUtils.GetLatestSettingsBackupManifest();
                    if (manifest != null)
                    {
                        if (manifest["CreateDateTime"] != null)
                        {
                            if (DateTime.TryParse(manifest["CreateDateTime"].ToString(), CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var theDateTime))
                            {
                                return theDateTime.ToString("G", CultureInfo.CurrentCulture);
                            }
                            else
                            {
                                Logger.LogError("Failed to parse time from backup");
                                return GetResourceString("General_SettingsBackupAndRestore_FailedToParseTime");
                            }
                        }
                        else
                        {
                            return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupTime");
                        }
                    }
                    else
                    {
                        return GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
                    }
                }
                catch (Exception e)
                {
                    Logger.LogError("Error getting LastSettingsBackupDate", e);
                    return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupTime");
                }
            }
        }

        public string CurrentSettingMatchText
        {
            get
            {
                try
                {
                    var results = settingsBackupAndRestoreUtils.GetLastBackupSettingsResults();

                    var resultText = string.Empty;

                    if (!results.LastRan.HasValue)
                    {
                        // not ran since started.
                        return GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsNoChecked"); // "Current Settings Unknown";
                    }
                    else
                    {
                        if (results.Success)
                        {
                            if (results.LastBackupExists)
                            {
                                // if true, it means a backup would have been made
                                resultText = GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsDiffer"); // "Current Settings Differ";
                            }
                            else
                            {
                                // would have done the backup, but there also was not an existing one there.
                                resultText = GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
                            }
                        }
                        else
                        {
                            if (results.HadError)
                            {
                                // if false and error we don't really know
                                resultText = GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsUnknown"); // "Current Settings Unknown";
                            }
                            else
                            {
                                // if false, it means a backup would not have been needed/made
                                resultText = GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsMatch"); // "Current Settings Match";
                            }
                        }

                        return $"{resultText} {GetResourceString("General_SettingsBackupAndRestore_CurrentSettingsStatusAt")} {results.LastRan.Value.ToLocalTime().ToString("G", CultureInfo.CurrentCulture)}";
                    }
                }
                catch (Exception e)
                {
                    Logger.LogError("Error getting CurrentSettingMatchText", e);
                    return string.Empty;
                }
            }
        }

        public string LastSettingsBackupSource
        {
            get
            {
                try
                {
                    var manifest = settingsBackupAndRestoreUtils.GetLatestSettingsBackupManifest();
                    if (manifest != null)
                    {
                        if (manifest["BackupSource"] != null)
                        {
                            if (manifest["BackupSource"].ToString().Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase))
                            {
                                return GetResourceString("General_SettingsBackupAndRestore_ThisMachine");
                            }
                            else
                            {
                                return manifest["BackupSource"].ToString();
                            }
                        }
                        else
                        {
                            return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupSource");
                        }
                    }
                    else
                    {
                        return GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
                    }
                }
                catch (Exception e)
                {
                    Logger.LogError("Error getting LastSettingsBackupSource", e);
                    return GetResourceString("General_SettingsBackupAndRestore_UnknownBackupSource");
                }
            }
        }

        public string LastSettingsBackupFileName
        {
            get
            {
                try
                {
                    var fileName = settingsBackupAndRestoreUtils.GetLatestBackupFileName();
                    return !string.IsNullOrEmpty(fileName) ? fileName : GetResourceString("General_SettingsBackupAndRestore_NoBackupFound");
                }
                catch (Exception e)
                {
                    Logger.LogError("Error getting LastSettingsBackupFileName", e);
                    return string.Empty;
                }
            }
        }

        public UpdatingSettings.UpdatingState PowerToysUpdatingState
        {
            get
            {
                return _updatingState;
            }

            private set
            {
                if (value != _updatingState)
                {
                    _updatingState = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public string PowerToysNewAvailableVersion
        {
            get
            {
                return _newAvailableVersion;
            }

            private set
            {
                if (value != _newAvailableVersion)
                {
                    _newAvailableVersion = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public string PowerToysNewAvailableVersionLink
        {
            get
            {
                return _newAvailableVersionLink;
            }

            private set
            {
                if (value != _newAvailableVersionLink)
                {
                    _newAvailableVersionLink = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool IsNewVersionDownloading
        {
            get
            {
                return _isNewVersionDownloading;
            }

            set
            {
                if (value != _isNewVersionDownloading)
                {
                    _isNewVersionDownloading = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool IsNewVersionCheckedAndUpToDate
        {
            get
            {
                return _isNewVersionChecked;
            }
        }

        public bool IsNoNetwork
        {
            get
            {
                return _isNoNetwork;
            }
        }

        public bool IsBugReportRunning
        {
            get
            {
                return _isBugReportRunning;
            }

            set
            {
                if (value != _isBugReportRunning)
                {
                    _isBugReportRunning = value;
                    NotifyPropertyChanged();
                }
            }
        }

        public bool SettingsBackupRestoreMessageVisible
        {
            get
            {
                return _settingsBackupRestoreMessageVisible;
            }
        }

        public string BackupRestoreMessageSeverity
        {
            get
            {
                return _backupRestoreMessageSeverity;
            }
        }

        public string SettingsBackupMessage
        {
            get
            {
                return _settingsBackupMessage;
            }
        }

        public bool IsDownloadAllowed
        {
            get
            {
                return !_isDevBuild && !IsNewVersionDownloading;
            }
        }

        public bool IsUpdatePanelVisible
        {
            get
            {
                return PowerToysUpdatingState == UpdatingSettings.UpdatingState.UpToDate || PowerToysUpdatingState == UpdatingSettings.UpdatingState.NetworkError;
            }
        }

        public ObservableCollection<LanguageModel> Languages { get; } = new ObservableCollection<LanguageModel>();

        public int LanguagesIndex
        {
            get
            {
                return _languagesIndex;
            }

            set
            {
                if (_languagesIndex != value)
                {
                    _languagesIndex = value;
                    OnPropertyChanged(nameof(LanguagesIndex));
                    NotifyLanguageChanged();
                    if (_initLanguagesIndex != value)
                    {
                        LanguageChanged = true;
                    }
                    else
                    {
                        LanguageChanged = false;
                    }
                }
            }
        }

        public bool LanguageChanged
        {
            get
            {
                return _languageChanged;
            }

            set
            {
                if (_languageChanged != value)
                {
                    _languageChanged = value;
                    OnPropertyChanged(nameof(LanguageChanged));
                }
            }
        }

        public void NotifyPropertyChanged([CallerMemberName] string propertyName = null, bool reDoBackupDryRun = true)
        {
            // Notify UI of property change
            OnPropertyChanged(propertyName);

            OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);

            SendConfigMSG(outsettings.ToString());

            if (reDoBackupDryRun && DoBackupAndRestoreDryRun != null)
            {
                DoBackupAndRestoreDryRun(500);
            }
        }

        /// <summary>
        /// Method <c>SelectSettingBackupDir</c> opens folder browser to select a backup and restore location.
        /// </summary>
        private async void SelectSettingBackupDir()
        {
            var currentDir = settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();

            var newPath = await PickSingleFolderDialog();

            if (!string.IsNullOrEmpty(newPath))
            {
                SettingsBackupAndRestoreDir = newPath;
                NotifyAllBackupAndRestoreProperties();
            }
        }

        private void RefreshBackupStatusEventHandlerClick()
        {
            DoBackupAndRestoreDryRun(0);
        }

        /// <summary>
        /// Method <c>RestoreConfigsClick</c> starts the restore.
        /// </summary>
        private void RestoreConfigsClick()
        {
            string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();

            if (string.IsNullOrEmpty(settingsBackupAndRestoreDir))
            {
                SelectSettingBackupDir();
            }

            var results = SettingsUtils.RestoreSettings();
            _backupRestoreMessageSeverity = results.Severity;

            if (!results.Success)
            {
                _settingsBackupRestoreMessageVisible = true;

                _settingsBackupMessage = GetResourceString(results.Message);

                NotifyAllBackupAndRestoreProperties();

                HideBackupAndRestoreMessageAreaAction();
            }
            else
            {
                // make sure not to do NotifyPropertyChanged here, else it will persist the configs from memory and
                // undo the settings restore.
                SettingsBackupAndRestoreUtils.SetRegSettingsBackupAndRestoreItem("LastSettingsRestoreDate", DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture));

                Restart();
            }
        }

        /// <summary>
        /// Method <c>BackupConfigsClick</c> starts the backup.
        /// </summary>
        private void BackupConfigsClick()
        {
            string settingsBackupAndRestoreDir = settingsBackupAndRestoreUtils.GetSettingsBackupAndRestoreDir();

            if (string.IsNullOrEmpty(settingsBackupAndRestoreDir))
            {
                SelectSettingBackupDir();
            }

            var results = SettingsUtils.BackupSettings();

            _settingsBackupRestoreMessageVisible = true;
            _backupRestoreMessageSeverity = results.Severity;
            _settingsBackupMessage = GetResourceString(results.Message) + results.OptionalMessage;

            // now we do a dry run to get the results for "setting match"
            var settingsUtils = SettingsUtils.Default;
            var appBasePath = Path.GetDirectoryName(settingsUtils.GetSettingsFilePath());
            settingsBackupAndRestoreUtils.BackupSettings(appBasePath, settingsBackupAndRestoreDir, true);

            NotifyAllBackupAndRestoreProperties();

            HideBackupAndRestoreMessageAreaAction();
        }

        public void NotifyAllBackupAndRestoreProperties()
        {
            NotifyPropertyChanged(nameof(LastSettingsBackupDate), false);
            NotifyPropertyChanged(nameof(LastSettingsBackupSource), false);
            NotifyPropertyChanged(nameof(LastSettingsBackupFileName), false);
            NotifyPropertyChanged(nameof(CurrentSettingMatchText), false);
            NotifyPropertyChanged(nameof(SettingsBackupMessage), false);
            NotifyPropertyChanged(nameof(BackupRestoreMessageSeverity), false);
            NotifyPropertyChanged(nameof(SettingsBackupRestoreMessageVisible), false);
        }

        // callback function to launch the URL to check for updates.
        private void CheckForUpdatesClick()
        {
            GeneralSettingsConfig.CustomActionName = "check_for_updates";

            OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
            GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);

            SendCheckForUpdatesConfigMSG(customaction.ToString());
        }

        private void UpdateNowClick()
        {
            IsNewVersionDownloading = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
            NotifyPropertyChanged(nameof(IsDownloadAllowed));

            Process.Start(new ProcessStartInfo(Helper.GetPowerToysInstallationFolder() + "\\PowerToys.exe") { Arguments = "powertoys://update_now/" });
        }

        /// <summary>
        /// Class <c>GetResourceString</c> gets a localized text.
        /// </summary>
        /// <remarks>
        /// To do: see if there is a betting way to do this, there should be. It does allow us to return missing localization in a way that makes it obvious they were missed.
        /// </remarks>
        public string GetResourceString(string resource)
        {
            if (ResourceLoader != null)
            {
                var result = ResourceLoader.GetString(resource);
                if (string.IsNullOrEmpty(result))
                {
                    return resource.ToUpperInvariant() + "!!!";
                }
                else
                {
                    return result;
                }
            }
            else
            {
                return resource;
            }
        }

        public void RequestUpdateCheckedDate()
        {
            GeneralSettingsConfig.CustomActionName = "request_update_state_date";

            OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
            GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);

            SendCheckForUpdatesConfigMSG(customaction.ToString());
        }

        public void RestartElevated()
        {
            GeneralSettingsConfig.CustomActionName = "restart_elevation";

            OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
            GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);

            SendRestartAsAdminConfigMSG(customaction.ToString());
        }

        /// <summary>
        /// Class <c>Restart</c> begin a restart and signal we want to maintain elevation
        /// </summary>
        /// <remarks>
        /// Other restarts either raised or lowered elevation
        /// </remarks>
        public void Restart()
        {
            GeneralSettingsConfig.CustomActionName = "restart_maintain_elevation";

            OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
            GeneralSettingsCustomAction customaction = new GeneralSettingsCustomAction(outsettings);

            var dataToSend = customaction.ToString();
            dataToSend = JsonSerializer.Serialize(ActionMessage.Create("restart_maintain_elevation"), SourceGenerationContextContext.Default.ActionMessage);
            SendRestartAsAdminConfigMSG(dataToSend);
        }

        /// <summary>
        /// Class <c>HideBackupAndRestoreMessageArea</c> hides the backup/restore message area
        /// </summary>
        /// <remarks>
        /// We want to have it go away after a short period.
        /// </remarks>
        public void HideBackupAndRestoreMessageArea()
        {
            _settingsBackupRestoreMessageVisible = false;
            NotifyAllBackupAndRestoreProperties();
        }

        public void RefreshUpdatingState()
        {
            object oLock = new object();
            lock (oLock)
            {
                var config = UpdatingSettings.LoadSettings();

                // Retry loading if failed
                for (int i = 0; i < 3 && config == null; i++)
                {
                    System.Threading.Thread.Sleep(100);
                    config = UpdatingSettings.LoadSettings();
                }

                if (config == null)
                {
                    return;
                }

                UpdatingSettingsConfig = config;

                if (PowerToysUpdatingState != config.State)
                {
                    IsNewVersionDownloading = false;
                }
                else
                {
                    bool dateChanged = UpdateCheckedDate == FriendlyDateHelper.Format(UpdatingSettingsConfig.LastCheckedDateTime);
                    bool fileDownloaded = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
                    IsNewVersionDownloading = !(dateChanged || fileDownloaded);
                }

                PowerToysUpdatingState = UpdatingSettingsConfig.State;
                PowerToysNewAvailableVersion = UpdatingSettingsConfig.NewVersion;
                PowerToysNewAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
                UpdateCheckedDate = FriendlyDateHelper.Format(UpdatingSettingsConfig.LastCheckedDateTime);

                _isNoNetwork = PowerToysUpdatingState == UpdatingSettings.UpdatingState.NetworkError;
                NotifyPropertyChanged(nameof(IsNoNetwork));
                NotifyPropertyChanged(nameof(IsNewVersionDownloading));
                NotifyPropertyChanged(nameof(IsUpdatePanelVisible));
                _isNewVersionChecked = PowerToysUpdatingState == UpdatingSettings.UpdatingState.UpToDate && !IsNewVersionDownloading;
                NotifyPropertyChanged(nameof(IsNewVersionCheckedAndUpToDate));

                NotifyPropertyChanged(nameof(IsDownloadAllowed));
            }
        }

        private void InitializeLanguages()
        {
            var lang = LanguageModel.LoadSetting();
            var selectedLanguageIndex = 0;

            foreach (var item in langTagsAndIds)
            {
                var language = new LanguageModel { Tag = item.Key, ResourceID = item.Value, Language = GetResourceString(item.Value) };
                var index = GetLanguageIndex(language.Language, item.Key == string.Empty);
                Languages.Insert(index, language);

                if (item.Key.Equals(lang, StringComparison.Ordinal))
                {
                    selectedLanguageIndex = index;
                }
                else if (index <= selectedLanguageIndex)
                {
                    selectedLanguageIndex++;
                }
            }

            _initLanguagesIndex = selectedLanguageIndex;
            LanguagesIndex = selectedLanguageIndex;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1309:Use ordinal string comparison", Justification = "Building a user facing list")]
        private int GetLanguageIndex(string language, bool isDefault)
        {
            if (Languages.Count == 0 || isDefault)
            {
                return 0;
            }

            for (var i = 1; i < Languages.Count; i++)
            {
                if (string.Compare(Languages[i].Language, language, StringComparison.CurrentCultureIgnoreCase) > 0)
                {
                    return i;
                }
            }

            return Languages.Count;
        }

        private void NotifyLanguageChanged()
        {
            OutGoingLanguageSettings outsettings = new OutGoingLanguageSettings(Languages[_languagesIndex].Tag);

            SendConfigMSG(outsettings.ToString());
        }

        internal void RefreshSettingsOnExternalChange()
        {
            EnableDataDiagnostics = DataDiagnosticsSettings.GetEnabledValue();

            NotifyPropertyChanged(nameof(EnableDataDiagnostics));
        }

        // Per retention policy
        private void DeleteDiagnosticDataOlderThan28Days(string etwDirPath)
        {
            if (!Directory.Exists(etwDirPath))
            {
                return;
            }

            var directoryInfo = new DirectoryInfo(etwDirPath);
            var cutoffDate = DateTime.Now.AddDays(-28);

            foreach (var file in directoryInfo.GetFiles())
            {
                if (file.LastWriteTime < cutoffDate)
                {
                    try
                    {
                        file.Delete();
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError($"Failed to delete file: {file.FullName}. Error: {ex.Message}");
                    }
                }
            }
        }

        internal void ViewDiagnosticData()
        {
            string localLowEtwDirPath = Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), "AppData", "LocalLow", "Microsoft", "PowerToys", "etw");
            string etwDirPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\\PowerToys\\etw");

            if (Directory.Exists(localLowEtwDirPath))
            {
                if (!Directory.Exists(etwDirPath))
                {
                    Directory.CreateDirectory(etwDirPath);
                }

                string[] localLowEtlFiles = Directory.GetFiles(localLowEtwDirPath, "*.etl");

                foreach (string file in localLowEtlFiles)
                {
                    string fileName = Path.GetFileName(file);
                    string destFile = Path.Combine(etwDirPath, fileName);

                    try
                    {
                        File.Copy(file, destFile, overwrite: true);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError($"Failed to copy etl file: {fileName}. Error: {ex.Message}");
                    }
                }
            }

            string tracerptPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "system32");

            ETLConverter converter = new ETLConverter(etwDirPath, tracerptPath);
            Task.Run(() => converter.ConvertDiagnosticsETLsAsync()).Wait();

            if (Directory.Exists(etwDirPath))
            {
                // Open etw dir in FileExplorer
                Process.Start("explorer.exe", etwDirPath);
            }
        }

        private void OnSettingsChanged(GeneralSettings newSettings)
        {
            _dispatcherQueue?.TryEnqueue(() =>
            {
                GeneralSettingsConfig = newSettings;

                if (_enableQuickAccess != newSettings.EnableQuickAccess)
                {
                    _enableQuickAccess = newSettings.EnableQuickAccess;
                    OnPropertyChanged(nameof(EnableQuickAccess));
                }
            });
        }

        public override void Dispose()
        {
            base.Dispose();
            if (_settingsRepository != null)
            {
                _settingsRepository.SettingsChanged -= OnSettingsChanged;
            }

            GC.SuppressFinalize(this);
        }

        protected virtual Microsoft.UI.Dispatching.DispatcherQueue GetDispatcherQueue()
        {
            return Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
        }
    }
}
