using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Sonex.Client.Dialogs;
using Sonex.Client.Helpers;
using Sonex.DBWrapper;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Windows.Graphics;
using Windows.System;
using Windows.UI;
using WinRT.Interop;

namespace Sonex.Client.AppWindows
{
    public sealed partial class LoginWindow : Window
    {
        private const double TargetWidthDip = 680;
        private const double TargetHeightDip = 420;

        private const int WM_DPICHANGED = 0x02E0;
        private const int GWL_WNDPROC = -4;

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 5;

        private const string AutoThemeName = "Auto";
        private const string DefaultThemeName = "Default";

        private IntPtr _hwnd;
        private WndProcDelegate? _newWndProc;
        private IntPtr _oldWndProc;

        private bool _windowInitialized = false;
        private bool _startupInitialized = false;
        private bool _inLogin = false;
        private int _loginAttempts = 0;

        private readonly Dictionary<string, ThemeDescriptor> _themes = new(StringComparer.OrdinalIgnoreCase);

        private static readonly JsonSerializerOptions ThemeJsonOptions = new()
        {
            PropertyNameCaseInsensitive = true,
            ReadCommentHandling = JsonCommentHandling.Skip,
            AllowTrailingCommas = true
        };

        public LoginWindow()
        {
            InitializeComponent();
            Closed += LoginWindow_Closed;
        }

        /// <summary>
        /// Wywołaj przed Activate().
        /// </summary>
        public void PrepareBeforeShow()
        {
            if (_windowInitialized)
                return;

            _windowInitialized = true;

            _hwnd = WindowNative.GetWindowHandle(this);
            HookWndProc();

            ExtendsContentIntoTitleBar = true;
            SetTitleBar(_TitleBar);

            ConfigureWindowChrome();
            ApplyFixedSizeDip(center: true);
            ApplyWindowIcon();

            _VersionInfo.Text = $"Version {AppInfo.InformationalVersion.Trim()}";

            string userName = Config.GetStringValue("USER_NAME", "");
            bool keepLogIn = Config.GetBoolValue("KEEP_LOG_IN", false);

            _UserName.Text = userName;
            _KeepLogIn.IsChecked = keepLogIn;


            LoadThemes();
            BuildThemeMenu();

            string themeLight = Config.GetStringValue("THEME_LIGHT", AutoThemeName);
            string themeName = Config.GetStringValue("THEME_NAME", AutoThemeName);

            SetThemeLight(themeLight);
            SetThemeMode(themeName, reloadBackground: false);

            RootGrid.Opacity = 0;

            _BackgroundAnimation.Loaded += BackgroundAnimation_Loaded;
        }

        public void HideNative()
        {
            if (_hwnd != IntPtr.Zero)
                ShowWindow(_hwnd, SW_HIDE);
        }

        public void ShowNative()
        {
            if (_hwnd != IntPtr.Zero)
                ShowWindow(_hwnd, SW_SHOW);
        }

        private void ConfigureWindowChrome()
        {
            if (AppWindow.Presenter is OverlappedPresenter presenter)
            {
                presenter.IsMaximizable = false;
                presenter.IsResizable = false;
                presenter.IsMinimizable = true;
                presenter.SetBorderAndTitleBar(true, true);
            }

            AppWindow.Changed += AppWindow_Changed;
        }

        private void ApplyWindowIcon()
        {
            string iconPath = Path.Combine(AppContext.BaseDirectory, "Assets", "AppIcon.ico");
            if (File.Exists(iconPath))
                AppWindow.SetIcon(iconPath);
        }

        private async void BackgroundAnimation_Loaded(object sender, RoutedEventArgs e)
        {
            if (_startupInitialized)
                return;

            _startupInitialized = true;
            _BackgroundAnimation.Loaded -= BackgroundAnimation_Loaded;

            try
            {
               //var minDelayTask = Task.Delay(500);

                await Task.Yield();
                await ReloadBackgroundAnimationSafeAsync();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("LoginWindow startup error:");
                Debug.WriteLine(ex);
            }
            finally
            {
                RootGrid.Opacity = 1;
                ShowNative();
                AppWindowsHelper.BringToFront(this);
                if (!string.IsNullOrEmpty(_UserName.Text))
                {
                    _Password.Focus(FocusState.Programmatic);
                }
            }
        }

        private void _Login_Click(object sender, RoutedEventArgs e)
        {
            LoginAsync();
        }

        private void _TextBox_KeyDown(object sender, KeyRoutedEventArgs e)
        {
            if (e.Key == VirtualKey.Enter)
                LoginAsync();
        }

        private async void LoginAsync()
        {
            if (_inLogin)
                return;

            _inLogin = true;
            _loginAttempts++;

            string username = _UserName.Text ?? "";
            string password = _Password.Password ?? "";
            bool keepLogIn = _KeepLogIn.IsChecked ?? false;

            Config.SetValue("USER_NAME", username);
            Config.SetValue("KEEP_LOG_IN", keepLogIn);

            string dbHost = Config.GetStringValue("DB_HOST", "X-WEB-TEXT");
            int dbPort = Config.GetIntValue("DB_PORT", 5432);
            string dbDataBase = Config.GetStringValue("DB_DATA_BASE", "sonex");
            int timeout = Config.GetIntValue("DB_TIMEOUT", 15);
            int commandTimeout = Config.GetIntValue("DB_COMMAND_TIMEOUT", 30);
            bool pooling = Config.GetBoolValue("DB_POOLING", true);
            int minPoolSize = Config.GetIntValue("DB_MIN_POOL_SIZE", 0);
            int maxPoolSize = Config.GetIntValue("DB_MAXPOOLSIZE", 100);
            bool includeErrorDetail = Config.GetBoolValue("DB_INCLUDE_ERROR_DETAIL", true);

            string applicationVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0000.0000.0000";
            string applicationName = Assembly.GetExecutingAssembly().GetName().Name ?? "";
            string applicationNameAndVersion = $"{applicationName} {applicationVersion}";

            bool success = false;

            try
            {
                await Task.Run(() =>
                {
                    DB.InitPostgreSQL(
                        host: dbHost,
                        port: dbPort,
                        database: dbDataBase,
                        username: username,
                        password: password,
                        applicationName: applicationNameAndVersion,
                        timeout: timeout,
                        commandTimeout: commandTimeout,
                        pooling: pooling,
                        minPoolSize: minPoolSize,
                        maxPoolSize: maxPoolSize,
                        includeErrorDetail: includeErrorDetail);

                    using var connection = DB.Open();
                });

                success = true;
            }
            catch (Exception ex)
            {
                await this.ShowErrorAsync("Error", ex.Message);
                Debug.WriteLine(ex);
            }
            finally
            {
                _inLogin = false;
            }

            if (success)
                OpenMainWindow();
        }

        public void OpenMainWindow()
        {
            if (Application.Current is App app)
            {
                app.OpenMainWindow();
            }
        }

        private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
        {
            if (sender.Presenter is OverlappedPresenter presenter)
            {
                if (presenter.State == OverlappedPresenterState.Maximized)
                    presenter.Restore();
            }
        }

        private void ApplyFixedSizeDip(bool center)
        {
            if (_hwnd == IntPtr.Zero)
                return;

            uint dpi = GetDpiForWindow(_hwnd);

            int pxW = DipToPx(TargetWidthDip, dpi);
            int pxH = DipToPx(TargetHeightDip, dpi);

            AppWindow.Resize(new SizeInt32(pxW, pxH));

            if (center)
                AppWindowsHelper.CenterWindow(this, pxW, pxH);
        }

        private static int DipToPx(double dip, uint dpi)
            => (int)Math.Round(dip * dpi / 96.0);

        private void HookWndProc()
        {
            if (_hwnd == IntPtr.Zero)
                return;

            _newWndProc = WndProc;
            _oldWndProc = SetWindowLongPtr(_hwnd, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(_newWndProc));
        }

        private void UnhookWndProc()
        {
            if (_hwnd == IntPtr.Zero || _oldWndProc == IntPtr.Zero)
                return;

            SetWindowLongPtr(_hwnd, GWL_WNDPROC, _oldWndProc);
            _oldWndProc = IntPtr.Zero;
            _newWndProc = null;
        }

        private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg == WM_DPICHANGED)
                ApplyFixedSizeDip(center: false);

            return CallWindowProc(_oldWndProc, hWnd, msg, wParam, lParam);
        }

        private void LoginWindow_Closed(object sender, WindowEventArgs args)
        {
            try
            {
                AppWindow.Changed -= AppWindow_Changed;
            }
            catch
            {
            }

            try
            {
                _BackgroundAnimation.Loaded -= BackgroundAnimation_Loaded;
            }
            catch
            {
            }

            UnhookWndProc();
        }

        private delegate IntPtr WndProcDelegate(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr newProc);

        [DllImport("user32.dll")]
        private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern uint GetDpiForWindow(IntPtr hwnd);

        [DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        private static string NormalizeThemeLight(string themeLight)
        {
            return themeLight switch
            {
                "Auto" => "Auto",
                "Dark" => "Dark",
                "Light" => "Light",
                _ => "Auto"
            };
        }

        private void _ThemeLight_Change(object sender, RoutedEventArgs e)
        {
            if (sender is not RadioMenuFlyoutItem button)
                return;

            string text = NormalizeThemeLight(button.Text);
            Config.SetValue("THEME_LIGHT", text);
            SetThemeLight(text);
        }

        private void SetThemeLight(string themeLight)
        {
            themeLight = NormalizeThemeLight(themeLight);

            RootGrid.RequestedTheme = themeLight switch
            {
                "Auto" => ElementTheme.Default,
                "Dark" => ElementTheme.Dark,
                "Light" => ElementTheme.Light,
                _ => ElementTheme.Default
            };

            ApplyTitleBarTheme(this, RootGrid.ActualTheme == ElementTheme.Dark);
            SelectRadioMenuItem("Color mode", themeLight, useTag: false);

            RootGrid.UpdateLayout();
        }

        private void _ThemeMode_Change(object sender, RoutedEventArgs e)
        {
            if (sender is not RadioMenuFlyoutItem button)
                return;

            string themeName = button.Tag?.ToString() ?? button.Text;
            Config.SetValue("THEME_NAME", themeName);
            SetThemeMode(themeName, reloadBackground: true);
        }

        private void SetThemeMode(string requestedThemeName, bool reloadBackground)
        {
            requestedThemeName = string.IsNullOrWhiteSpace(requestedThemeName)
                ? AutoThemeName
                : requestedThemeName.Trim();

            string actualThemeName = ResolveThemeName(requestedThemeName);
            ApplyTheme(actualThemeName, reloadBackground);

            string menuSelection = requestedThemeName;
            if (!string.Equals(menuSelection, AutoThemeName, StringComparison.OrdinalIgnoreCase) &&
                !_themes.ContainsKey(menuSelection))
            {
                menuSelection = GetFallbackThemeName();
            }

            SelectRadioMenuItem("Theme", menuSelection, useTag: true);
        }

        private void LoadThemes()
        {
            var themes = DiscoverThemes();

            _themes.Clear();

            foreach (var pair in themes)
                _themes[pair.Key] = pair.Value;
        }

        private Dictionary<string, ThemeDescriptor> DiscoverThemes()
        {
            var result = new Dictionary<string, ThemeDescriptor>(StringComparer.OrdinalIgnoreCase);

            string themesRoot = Path.Combine(AppContext.BaseDirectory, "Assets", "Themes");
            if (!Directory.Exists(themesRoot))
                return result;

            foreach (string directory in Directory.GetDirectories(themesRoot))
            {
                string folderName = Path.GetFileName(directory);
                ThemeFileModel fileModel = LoadThemeFile(directory);

                var descriptor = new ThemeDescriptor
                {
                    Name = folderName,
                    DisplayName = string.IsNullOrWhiteSpace(fileModel.DisplayName) ? folderName : fileModel.DisplayName.Trim(),
                    AbsoluteDirectory = directory,
                    RelativeDirectory = $"Assets/Themes/{folderName}",
                    Settings = fileModel.Settings ?? new Dictionary<string, JsonElement>()
                };

                result[descriptor.Name] = descriptor;
            }

            return result;
        }

        private ThemeFileModel LoadThemeFile(string directory)
        {
            string jsonPath = Path.Combine(directory, "theme.json");

            if (!File.Exists(jsonPath))
                return new ThemeFileModel();

            try
            {
                string json = File.ReadAllText(jsonPath);
                return JsonSerializer.Deserialize<ThemeFileModel>(json, ThemeJsonOptions) ?? new ThemeFileModel();
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Theme load error: {jsonPath}");
                Debug.WriteLine(ex);
                return new ThemeFileModel();
            }
        }

        private void BuildThemeMenu()
        {
            MenuFlyoutSubItem? themeMenu = FindMenuSubItem("Theme");
            if (themeMenu is null)
                return;

            themeMenu.Items.Clear();

            var autoItem = new RadioMenuFlyoutItem
            {
                Text = AutoThemeName,
                Tag = AutoThemeName,
                GroupName = "ThemeMode"
            };
            autoItem.Click += _ThemeMode_Change;
            themeMenu.Items.Add(autoItem);

            foreach (ThemeDescriptor theme in _themes.Values
                         .OrderBy(x => string.Equals(x.Name, DefaultThemeName, StringComparison.OrdinalIgnoreCase) ? 0 : 1)
                         .ThenBy(x => x.DisplayName, StringComparer.OrdinalIgnoreCase))
            {
                var item = new RadioMenuFlyoutItem
                {
                    Text = theme.DisplayName,
                    Tag = theme.Name,
                    GroupName = "ThemeMode"
                };
                item.Click += _ThemeMode_Change;
                themeMenu.Items.Add(item);
            }
        }

        private MenuFlyoutSubItem? FindMenuSubItem(string text)
        {
            if (_SettingButton.Flyout is not MenuFlyout flyout)
                return null;

            foreach (var item in flyout.Items)
            {
                if (item is MenuFlyoutSubItem subItem &&
                    string.Equals(subItem.Text, text, StringComparison.OrdinalIgnoreCase))
                {
                    return subItem;
                }
            }

            return null;
        }

        private bool SelectRadioMenuItem(string subMenuText, string itemValue, bool useTag)
        {
            MenuFlyoutSubItem? subMenu = FindMenuSubItem(subMenuText);
            if (subMenu is null)
                return false;

            foreach (var item in subMenu.Items)
            {
                if (item is not RadioMenuFlyoutItem radio)
                    continue;

                string? compareValue = useTag ? radio.Tag?.ToString() : radio.Text;

                if (string.Equals(compareValue, itemValue, StringComparison.OrdinalIgnoreCase))
                {
                    radio.IsChecked = true;
                    return true;
                }
            }

            return false;
        }

        private string ResolveThemeName(string requestedThemeName)
        {
            if (string.Equals(requestedThemeName, AutoThemeName, StringComparison.OrdinalIgnoreCase))
                return ResolveAutoThemeName();

            if (_themes.ContainsKey(requestedThemeName))
                return requestedThemeName;

            return GetFallbackThemeName();
        }

        private string ResolveAutoThemeName()
        {
            var today = DateTime.Today;
            int year = today.Year;

            static bool InRange(DateTime d, DateTime from, DateTime to) => d >= from && d <= to;

            var easter = GetEasterSunday(year);

            if (HasTheme("New Year"))
            {
                var newYearStart = new DateTime(year - 1, 12, 27);
                var newYearEnd = new DateTime(year, 1, 3);

                if (InRange(today, newYearStart, newYearEnd))
                    return "New Year";
            }

            if (HasTheme("Valentine"))
            {
                var valentine = new DateTime(year, 2, 14);
                if (InRange(today, valentine.AddDays(-7), valentine))
                    return "Valentine";
            }

            if (HasTheme("Easter"))
            {
                if (InRange(today, easter.AddDays(-7), easter.AddDays(1)))
                    return "Easter";
            }

            if (HasTheme("Koningsdag"))
            {
                var koningsdag = new DateTime(year, 4, 27);
                if (koningsdag.DayOfWeek == DayOfWeek.Sunday)
                    koningsdag = koningsdag.AddDays(-1);

                if (InRange(today, koningsdag.AddDays(-7), koningsdag))
                    return "Koningsdag";
            }

            if (HasTheme("Labor Day"))
            {
                var laborDay = new DateTime(year, 5, 1);
                if (InRange(today, laborDay.AddDays(-7), laborDay))
                    return "Labor Day";
            }

            if (HasTheme("Halloween"))
            {
                var halloween = new DateTime(year, 10, 31);
                if (InRange(today, halloween.AddDays(-7), halloween))
                    return "Halloween";
            }

            if (HasTheme("Christmas"))
            {
                var christmasStart = new DateTime(year, 12, 18);
                var christmasEnd = new DateTime(year, 12, 26);

                if (InRange(today, christmasStart, christmasEnd))
                    return "Christmas";
            }

            if ((today.Month == 12 || today.Month == 1 || today.Month == 2) && HasTheme("Winter"))
                return "Winter";

            if ((today.Month >= 3 && today.Month <= 5) && HasTheme("Spring"))
                return "Spring";

            if ((today.Month >= 6 && today.Month <= 8) && HasTheme("Summer"))
                return "Summer";

            if ((today.Month >= 9 && today.Month <= 11) && HasTheme("Autumn"))
                return "Autumn";

            return GetFallbackThemeName();
        }

        private bool HasTheme(string name) => _themes.ContainsKey(name);

        private string GetFallbackThemeName()
        {
            if (_themes.ContainsKey(DefaultThemeName))
                return DefaultThemeName;

            return _themes.Keys.FirstOrDefault() ?? DefaultThemeName;
        }

        private void ApplyTheme(string themeName, bool reloadBackground)
        {
            if (_themes.Count == 0)
                return;

            if (!_themes.TryGetValue(themeName, out ThemeDescriptor? theme))
            {
                string fallback = GetFallbackThemeName();
                if (!_themes.TryGetValue(fallback, out theme))
                    return;
            }

            _BackgroundAnimation.ResetParametersToDefaults();

            ApplyBackgroundAnimationSettings(_BackgroundAnimation, theme.Settings);

            _BackgroundAnimation.BackgroundImageUriLight = ResolveThemeAssetPath(theme, "BackgroundLight.png");
            _BackgroundAnimation.BackgroundImageUriDark = ResolveThemeAssetPath(theme, "BackgroundDark.png");
            _BackgroundAnimation.IconUris = BuildThemeIconUris(theme);

            if (reloadBackground)
                _ = ReloadBackgroundAnimationSafeAsync();
        }

        private async Task ReloadBackgroundAnimationSafeAsync()
        {
            try
            {
                await _BackgroundAnimation.ReloadAllAsync();
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Background reload error:");
                Debug.WriteLine(ex);
            }
        }

        private void ApplyBackgroundAnimationSettings(object target, IReadOnlyDictionary<string, JsonElement> settings)
        {
            Type targetType = target.GetType();

            foreach (var pair in settings)
            {
                try
                {
                    var property = targetType.GetProperty(
                        pair.Key,
                        System.Reflection.BindingFlags.Public |
                        System.Reflection.BindingFlags.Instance |
                        System.Reflection.BindingFlags.IgnoreCase);

                    if (property is null || !property.CanWrite)
                        continue;

                    object? value = ConvertJsonValue(pair.Value, property.PropertyType);

                    if (value is null &&
                        property.PropertyType.IsValueType &&
                        Nullable.GetUnderlyingType(property.PropertyType) is null)
                    {
                        continue;
                    }

                    property.SetValue(target, value);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Theme property set error: {pair.Key}");
                    Debug.WriteLine(ex);
                }
            }
        }

        private static object? ConvertJsonValue(JsonElement element, Type targetType)
        {
            Type actualType = Nullable.GetUnderlyingType(targetType) ?? targetType;

            if (element.ValueKind == JsonValueKind.Null)
                return null;

            if (actualType == typeof(string))
                return element.GetString();

            if (actualType == typeof(bool))
                return element.GetBoolean();

            if (actualType == typeof(int))
                return element.GetInt32();

            if (actualType == typeof(long))
                return element.GetInt64();

            if (actualType == typeof(float))
                return element.GetSingle();

            if (actualType == typeof(double))
                return element.GetDouble();

            if (actualType == typeof(decimal))
                return element.GetDecimal();

            if (actualType == typeof(Color))
                return ParseColor(element.GetString());

            if (actualType.IsEnum)
            {
                if (element.ValueKind == JsonValueKind.String)
                    return Enum.Parse(actualType, element.GetString()!, true);

                return Enum.ToObject(actualType, element.GetInt32());
            }

            return JsonSerializer.Deserialize(element.GetRawText(), actualType, ThemeJsonOptions);
        }

        private static Color ParseColor(string? value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return Colors.Transparent;

            string hex = value.Trim().TrimStart('#');

            return hex.Length switch
            {
                6 => Color.FromArgb(
                    255,
                    Convert.ToByte(hex.Substring(0, 2), 16),
                    Convert.ToByte(hex.Substring(2, 2), 16),
                    Convert.ToByte(hex.Substring(4, 2), 16)),

                8 => Color.FromArgb(
                    Convert.ToByte(hex.Substring(0, 2), 16),
                    Convert.ToByte(hex.Substring(2, 2), 16),
                    Convert.ToByte(hex.Substring(4, 2), 16),
                    Convert.ToByte(hex.Substring(6, 2), 16)),

                _ => Colors.Transparent
            };
        }

        private string ResolveThemeAssetPath(ThemeDescriptor theme, string fileName)
        {
            string candidateAbsolute = Path.Combine(theme.AbsoluteDirectory, fileName);
            if (File.Exists(candidateAbsolute))
                return $"{theme.RelativeDirectory}/{fileName}";

            if (_themes.TryGetValue(DefaultThemeName, out ThemeDescriptor? fallbackTheme))
            {
                string fallbackAbsolute = Path.Combine(fallbackTheme.AbsoluteDirectory, fileName);
                if (File.Exists(fallbackAbsolute))
                    return $"{fallbackTheme.RelativeDirectory}/{fileName}";
            }

            return $"{theme.RelativeDirectory}/{fileName}";
        }

        private string BuildThemeIconUris(ThemeDescriptor theme)
        {
            var files = GetThemeIconFiles(theme).ToList();

            if (files.Count == 0)
                return string.Empty;

            return string.Join(";" + Environment.NewLine,
                files.Select(x => $"{theme.RelativeDirectory}/{Path.GetFileName(x)}"));
        }

        private static IEnumerable<string> GetThemeIconFiles(ThemeDescriptor theme)
        {
            if (!Directory.Exists(theme.AbsoluteDirectory))
                return Enumerable.Empty<string>();

            return Directory
                .GetFiles(theme.AbsoluteDirectory, "icon*.png", SearchOption.TopDirectoryOnly)
                .OrderBy(x => ExtractNumber(Path.GetFileNameWithoutExtension(x)))
                .ThenBy(x => x, StringComparer.OrdinalIgnoreCase);
        }

        private static int ExtractNumber(string? value)
        {
            if (string.IsNullOrWhiteSpace(value))
                return int.MaxValue;

            string digits = new string(value.Where(char.IsDigit).ToArray());
            return int.TryParse(digits, out int number) ? number : int.MaxValue;
        }

        public static void ApplyTitleBarTheme(Window window, bool dark)
        {
            var hwnd = WindowNative.GetWindowHandle(window);
            var id = Win32Interop.GetWindowIdFromWindow(hwnd);
            var appWindow = AppWindow.GetFromWindowId(id);

            if (appWindow.TitleBar is null)
                return;

            if (dark)
            {
                appWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent;
                appWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
                appWindow.TitleBar.ButtonForegroundColor = Colors.White;
                appWindow.TitleBar.ButtonInactiveForegroundColor = Color.FromArgb(255, 180, 180, 180);
                appWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(255, 45, 45, 45);
                appWindow.TitleBar.ButtonHoverForegroundColor = Colors.White;
                appWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(255, 30, 30, 30);
                appWindow.TitleBar.ButtonPressedForegroundColor = Colors.White;
            }
            else
            {
                appWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent;
                appWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
                appWindow.TitleBar.ButtonForegroundColor = Colors.Black;
                appWindow.TitleBar.ButtonInactiveForegroundColor = Color.FromArgb(255, 120, 120, 120);
                appWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(255, 230, 230, 230);
                appWindow.TitleBar.ButtonHoverForegroundColor = Colors.Black;
                appWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(255, 210, 210, 210);
                appWindow.TitleBar.ButtonPressedForegroundColor = Colors.Black;
            }
        }

        private void _About_Click(object sender, RoutedEventArgs e)
        {
            var window = new AboutWindow();
            window.Activate();
        }

        public static DateTime GetEasterSunday(int year)
        {
            int a = year % 19;
            int b = year / 100;
            int c = year % 100;
            int d = b / 4;
            int e = b % 4;
            int f = (b + 8) / 25;
            int g = (b - f + 1) / 3;
            int h = (19 * a + b - d - g + 15) % 30;
            int i = c / 4;
            int k = c % 4;
            int l = (32 + 2 * e + 2 * i - h - k) % 7;
            int m = (a + 11 * h + 22 * l) / 451;

            int month = (h + l - 7 * m + 114) / 31;
            int day = ((h + l - 7 * m + 114) % 31) + 1;

            return new DateTime(year, month, day);
        }

        private sealed class ThemeFileModel
        {
            public string? DisplayName { get; set; }

            [JsonExtensionData]
            public Dictionary<string, JsonElement>? Settings { get; set; }
        }

        private sealed class ThemeDescriptor
        {
            public string Name { get; set; } = "";
            public string DisplayName { get; set; } = "";
            public string AbsoluteDirectory { get; set; } = "";
            public string RelativeDirectory { get; set; } = "";
            public IReadOnlyDictionary<string, JsonElement> Settings { get; set; } = new Dictionary<string, JsonElement>();
        }
    }
}