using System;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Styling;
using Avalonia.Threading;
using AvaloniaWebView;
using LanMountainDesktop.ComponentSystem;
using LanMountainDesktop.Models;
using LanMountainDesktop.PluginSdk;
using LanMountainDesktop.Services;
using LanMountainDesktop.Services.Settings;
using LanMountainDesktop.Theme;
using LanMountainDesktop.ViewModels;
using LanMountainDesktop.Views;

namespace LanMountainDesktop;

public partial class App : Application
{
    private static readonly Color DefaultAccentColor = Color.Parse("#FF3B82F6");
    private enum DesktopShellState
    {
        ForegroundDesktop = 0,
        MinimizedToTaskbar = 1,
        TrayOnly = 2
    }

    private enum ShutdownIntent
    {
        None = 0,
        ExitRequested = 1,
        RestartRequested = 2
    }

    private readonly ISettingsFacadeService _settingsFacade = HostSettingsFacadeProvider.GetOrCreate();
    private readonly IAppearanceThemeService _appearanceThemeService = HostAppearanceThemeProvider.GetOrCreate();
    private readonly IAppLogoService _appLogoService = HostAppLogoProvider.GetOrCreate();
    private readonly LocalizationService _localizationService = new();
    private readonly IHostApplicationLifecycle _hostApplicationLifecycle = new HostApplicationLifecycleService();
    private readonly IDetachedComponentLibraryWindowService _detachedComponentLibraryWindowService = new DetachedComponentLibraryWindowService();
    private readonly ILocationService _locationService = HostLocationServiceProvider.GetOrCreate();
    private ISettingsPageRegistry? _settingsPageRegistry;
    private ISettingsWindowService? _settingsWindowService;
    private WeatherLocationRefreshService? _weatherLocationRefreshService;
    private bool _exitCleanupCompleted;
    private DesktopShellState _desktopShellState = DesktopShellState.ForegroundDesktop;
    private ShutdownIntent _shutdownIntent;

    private TrayIcons? _trayIcons;
    private PluginRuntimeService? _pluginRuntimeService;
    private MainWindow? _mainWindow;
    private bool _mainWindowClosed;
    private bool _uiUnhandledExceptionHooked;

    internal static SingleInstanceService? CurrentSingleInstanceService { get; set; }
    internal static (UserBehaviorAnalyticsService?, CrashReportService?) AnalyticsServices { get; set; }
    internal static IHostApplicationLifecycle? CurrentHostApplicationLifecycle =>
        (Current as App)?._hostApplicationLifecycle;

    // 隐私政策查看事件
    public static event Action? CurrentPrivacyPolicyViewRequested;

    // 触发隐私政策查看事件的方法
    public static void RaisePrivacyPolicyViewRequested()
    {
        CurrentPrivacyPolicyViewRequested?.Invoke();
    }

    public PluginRuntimeService? PluginRuntimeService => _pluginRuntimeService;
    public ISettingsFacadeService SettingsFacade => _settingsFacade;
    public IHostApplicationLifecycle HostApplicationLifecycle => _hostApplicationLifecycle;
    internal ISettingsWindowService? SettingsWindowService => _settingsWindowService;

    internal void OpenIndependentSettingsModule(string source, string? pageTag = null)
    {
        EnsureSettingsWindowService();
        AppLogger.Info(
            "SettingsFacade",
            $"Opening settings window. Source='{source}'; PageTag='{pageTag ?? "<default>"}'.");
        _settingsWindowService?.Open(new SettingsWindowOpenRequest(
            Source: source,
            Owner: _mainWindow is { IsVisible: true } ? _mainWindow : null,
            PageId: pageTag));
    }

    public App()
    {
        _settingsFacade.Settings.Changed += OnSettingsChanged;
        _appearanceThemeService.Changed += OnAppearanceThemeChanged;
    }

    public override void Initialize()
    {
        AppLogger.Info("App", "Initializing application resources.");
        ConfigureWebViewUserDataFolder();
        AvaloniaWebViewBuilder.Initialize(default);
        AvaloniaXamlLoader.Load(this);
        ApplyThemeFromSettings();
        ApplyCurrentCultureFromSettings();
        EnsureSettingsWindowService();
        EnsureWeatherLocationRefreshService();
    }

    public override void OnFrameworkInitializationCompleted()
    {
        AppLogger.Info("App", "Framework initialization completed.");
        RegisterUiUnhandledExceptionGuard();
        LinuxDesktopEntryInstaller.EnsureInstalled();
        InitializePluginRuntime();
        InitializeTrayIcon();

        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            // Avoid duplicate validations from both Avalonia and the CommunityToolkit.
            // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
            DisableAvaloniaDataAnnotationValidation();
            desktop.ShutdownMode = Avalonia.Controls.ShutdownMode.OnExplicitShutdown;
            desktop.Exit += (_, _) =>
            {
                AppLogger.Info("App", "Desktop lifetime exit triggered.");
                PerformExitCleanup();
            };

            CreateAndAssignMainWindow(desktop, "FrameworkInitialization");
            CurrentSingleInstanceService?.StartActivationListener(ActivateMainWindow);
        }

        StartWeatherLocationRefreshIfNeeded();

        base.OnFrameworkInitializationCompleted();
    }

    private void OnTrayExitClick(object? sender, EventArgs e)
    {
        _ = _hostApplicationLifecycle.TryExit(new HostApplicationLifecycleRequest(
            Source: "TrayMenu",
            Reason: "User selected Exit App from the tray menu."));
    }

    private void OnTrayShowDesktopClick(object? sender, EventArgs e)
    {
        RestoreOrCreateMainWindow(showSingleInstanceNotice: false, source: "TrayMenu");
    }

    private void OnTrayRestartClick(object? sender, EventArgs e)
    {
        _ = _hostApplicationLifecycle.TryRestart(new HostApplicationLifecycleRequest(
            Source: "TrayMenu",
            Reason: "User selected Restart App from the tray menu."));
    }

    private void OnTraySettingsClick(object? sender, EventArgs e)
    {
        _ = sender;
        _ = e;
        OpenIndependentSettingsModule("TrayMenu");
    }

    private void OnTrayComponentLibraryClick(object? sender, EventArgs e)
    {
        _ = sender;
        _ = e;
        if (_mainWindow is null)
        {
            return;
        }

        _detachedComponentLibraryWindowService.Open(_mainWindow);
    }

    private void DisableAvaloniaDataAnnotationValidation()
    {
        // Get an array of plugins to remove
        var dataValidationPluginsToRemove =
            BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();

        // remove each entry found
        foreach (var plugin in dataValidationPluginsToRemove)
        {
            BindingPlugins.DataValidators.Remove(plugin);
        }
    }

    private static void ConfigureWebViewUserDataFolder()
    {
        if (!OperatingSystem.IsWindows())
        {
            return;
        }

        const string userDataFolderEnvVar = "WEBVIEW2_USER_DATA_FOLDER";
        try
        {
            if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(userDataFolderEnvVar)))
            {
                return;
            }

            var userDataFolder = WebView2RuntimeProbe.ResolveUserDataFolder();
            Environment.SetEnvironmentVariable(
                userDataFolderEnvVar,
                userDataFolder,
                EnvironmentVariableTarget.Process);
        }
        catch (Exception ex)
        {
            // Keep startup resilient if user profile folders are unavailable.
            AppLogger.Warn("WebView2", "Failed to configure WebView2 user data folder.", ex);
        }
    }

    private void InitializePluginRuntime()
    {
        try
        {
            _pluginRuntimeService?.Dispose();
            _pluginRuntimeService = new PluginRuntimeService(_settingsFacade);
            HostSettingsFacadeProvider.BindPluginRuntime(_pluginRuntimeService);
            _pluginRuntimeService.LoadInstalledPlugins();
        }
        catch (Exception ex)
        {
            AppLogger.Warn("PluginRuntime", "Failed to initialize plugin runtime.", ex);
        }
    }

    private void InitializeTrayIcon()
    {
        try
        {
            DisposeTrayIcon();

            var trayIcon = new TrayIcon
            {
                Icon = _appLogoService.CreateTrayIcon(),
                ToolTipText = L("tray.tooltip", "LanMountainDesktop"),
                Menu = BuildTrayMenu(),
                IsVisible = true
            };

            _trayIcons = [trayIcon];
            TrayIcon.SetIcons(this, _trayIcons);
        }
        catch (Exception ex)
        {
            AppLogger.Warn("TrayIcon", "Failed to initialize tray icon.", ex);
        }
    }

    private NativeMenu BuildTrayMenu()
    {
        var menu = new NativeMenu();

        var showDesktopItem = new NativeMenuItem(L("tray.menu.show_desktop", "Open Desktop"));
        showDesktopItem.Click += OnTrayShowDesktopClick;
        menu.Items.Add(showDesktopItem);

        var settingsItem = new NativeMenuItem(L("tray.menu.settings", "Settings"));
        settingsItem.Click += OnTraySettingsClick;
        menu.Items.Add(settingsItem);

        var componentLibraryItem = new NativeMenuItem(L("tray.menu.component_library", "Component Library"));
        componentLibraryItem.Click += OnTrayComponentLibraryClick;
        menu.Items.Add(componentLibraryItem);

        menu.Items.Add(new NativeMenuItemSeparator());

        var restartItem = new NativeMenuItem(L("tray.menu.restart", "Restart App"));
        restartItem.Click += OnTrayRestartClick;
        menu.Items.Add(restartItem);

        menu.Items.Add(new NativeMenuItemSeparator());

        var exitItem = new NativeMenuItem(L("tray.menu.exit", "Exit App"));
        exitItem.Click += OnTrayExitClick;
        menu.Items.Add(exitItem);

        return menu;
    }

    private void DisposeTrayIcon()
    {
        if (_trayIcons is null)
        {
            return;
        }

        TrayIcon.SetIcons(this, null);
        foreach (var trayIcon in _trayIcons)
        {
            trayIcon.Dispose();
        }

        _trayIcons = null;
    }

    private void EnsureSettingsWindowService()
    {
        _settingsPageRegistry ??= new SettingsPageRegistry(
            _settingsFacade,
            _hostApplicationLifecycle,
            _localizationService,
            () => _pluginRuntimeService);
        _settingsWindowService ??= new SettingsWindowService(
            _settingsPageRegistry,
            _hostApplicationLifecycle,
            _settingsFacade);
    }

    private void EnsureWeatherLocationRefreshService()
    {
        _weatherLocationRefreshService ??= new WeatherLocationRefreshService(
            _settingsFacade,
            _locationService,
            _localizationService);
    }

    private void StartWeatherLocationRefreshIfNeeded()
    {
        EnsureWeatherLocationRefreshService();
        if (_weatherLocationRefreshService is null)
        {
            return;
        }

        _ = Task.Run(async () =>
        {
            try
            {
                await _weatherLocationRefreshService.TryRefreshOnStartupAsync();
            }
            catch (Exception ex)
            {
                AppLogger.Warn("Weather.Location", "Failed to refresh weather location during startup.", ex);
            }
        });
    }

    private void ApplyThemeFromSettings()
    {
        var snapshot = _appearanceThemeService.GetCurrent();
        RequestedThemeVariant = snapshot.IsNightMode
            ? ThemeVariant.Dark
            : ThemeVariant.Light;
        ApplyAdaptiveThemeResources();
    }

    private void ApplyCurrentCultureFromSettings()
    {
        var snapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
        var languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);

        CultureInfo culture;
        try
        {
            culture = CultureInfo.GetCultureInfo(languageCode);
        }
        catch (CultureNotFoundException)
        {
            culture = CultureInfo.GetCultureInfo("zh-CN");
        }

        CultureInfo.DefaultThreadCurrentCulture = culture;
        CultureInfo.DefaultThreadCurrentUICulture = culture;
        Thread.CurrentThread.CurrentCulture = culture;
        Thread.CurrentThread.CurrentUICulture = culture;
    }

    private void ActivateMainWindow()
    {
        RestoreOrCreateMainWindow(showSingleInstanceNotice: true, source: "SingleInstance");
    }

    private void RestoreOrCreateMainWindow(bool showSingleInstanceNotice, string source)
    {
        Dispatcher.UIThread.Post(() =>
        {
            if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
            {
                return;
            }

            try
            {
                var mainWindow = GetOrCreateMainWindow(desktop, source);
                mainWindow.ShowInTaskbar = true;

                if (!mainWindow.IsVisible)
                {
                    mainWindow.Show();
                }

                if (mainWindow.WindowState == WindowState.Minimized)
                {
                    mainWindow.WindowState = WindowState.Normal;
                }

                if (mainWindow.WindowState != WindowState.FullScreen)
                {
                    mainWindow.WindowState = WindowState.FullScreen;
                }

                mainWindow.Activate();
                mainWindow.Topmost = true;
                mainWindow.Topmost = false;
                SetDesktopShellState(DesktopShellState.ForegroundDesktop, $"Restore:{source}");
                AppLogger.Info(
                    "DesktopShell",
                    $"Desktop restored. Source='{source}'; MainWindowClosed={_mainWindowClosed}; ShowSingleInstanceNotice={showSingleInstanceNotice}; WindowState='{mainWindow.WindowState}'.");

                if (showSingleInstanceNotice)
                {
                    mainWindow.ShowSingleInstanceNotice();
                }
            }
            catch (Exception ex)
            {
                AppLogger.Warn("DesktopShell", $"Failed to restore desktop shell. Source='{source}'.", ex);
            }
        }, DispatcherPriority.Send);
    }

    internal void PrepareForShutdown(bool isRestart, string source)
    {
        void Mark()
        {
            _shutdownIntent = isRestart
                ? ShutdownIntent.RestartRequested
                : ShutdownIntent.ExitRequested;
            AppLogger.Info(
                "DesktopShell",
                $"Shutdown intent marked. Intent='{_shutdownIntent}'; Source='{source}'; CurrentShellState='{_desktopShellState}'.");
        }

        if (Dispatcher.UIThread.CheckAccess())
        {
            Mark();
            return;
        }

        Dispatcher.UIThread.InvokeAsync(Mark, DispatcherPriority.Send).GetAwaiter().GetResult();
    }

    internal void ResetShutdownIntent(string source)
    {
        void Reset()
        {
            if (_shutdownIntent == ShutdownIntent.None)
            {
                return;
            }

            AppLogger.Warn(
                "DesktopShell",
                $"Shutdown intent cleared without process exit. PreviousIntent='{_shutdownIntent}'; Source='{source}'.");
            _shutdownIntent = ShutdownIntent.None;
        }

        if (Dispatcher.UIThread.CheckAccess())
        {
            Reset();
            return;
        }

        Dispatcher.UIThread.InvokeAsync(Reset, DispatcherPriority.Send).GetAwaiter().GetResult();
    }

    private void OnSettingsChanged(object? sender, SettingsChangedEvent e)
    {
        _ = sender;

        if (e.Scope != SettingsScope.App)
        {
            return;
        }

        Dispatcher.UIThread.Post(() =>
        {
            var changedKeys = e.ChangedKeys?.ToArray();
            var refreshAll = changedKeys is null || changedKeys.Length == 0;
            var liveAppearance = _appearanceThemeService.GetCurrent();
            var themeChanged =
                refreshAll ||
                changedKeys.Contains(nameof(AppSettingsSnapshot.IsNightMode), StringComparer.OrdinalIgnoreCase) ||
                changedKeys.Contains(nameof(AppSettingsSnapshot.UseSystemChrome), StringComparer.OrdinalIgnoreCase) ||
                (string.Equals(liveAppearance.ThemeColorMode, ThemeAppearanceValues.ColorModeSeedMonet, StringComparison.OrdinalIgnoreCase) &&
                 changedKeys.Contains(nameof(AppSettingsSnapshot.ThemeColor), StringComparer.OrdinalIgnoreCase)) ||
                (string.Equals(liveAppearance.ThemeColorMode, ThemeAppearanceValues.ColorModeWallpaperMonet, StringComparison.OrdinalIgnoreCase) &&
                 (changedKeys.Contains(nameof(AppSettingsSnapshot.WallpaperPath), StringComparer.OrdinalIgnoreCase) ||
                  changedKeys.Contains(nameof(AppSettingsSnapshot.WallpaperType), StringComparer.OrdinalIgnoreCase) ||
                  changedKeys.Contains(nameof(AppSettingsSnapshot.WallpaperColor), StringComparer.OrdinalIgnoreCase)));
            var languageChanged =
                refreshAll ||
                changedKeys.Contains(nameof(AppSettingsSnapshot.LanguageCode), StringComparer.OrdinalIgnoreCase);

            if (themeChanged)
            {
                ApplyThemeFromSettings();
            }

            if (languageChanged)
            {
                // 清除本地化缓存，强制重新加载语言文件
                _localizationService.ClearCache();
                ApplyCurrentCultureFromSettings();
                if (_trayIcons is not null)
                {
                    InitializeTrayIcon();
                }
            }
        }, DispatcherPriority.Background);
    }

    private void OnAppearanceThemeChanged(object? sender, AppearanceThemeSnapshot e)
    {
        _ = sender;
        _ = e;

        Dispatcher.UIThread.Post(ApplyThemeFromSettings, DispatcherPriority.Background);
    }

    private void ApplyAdaptiveThemeResources()
    {
        _appearanceThemeService.ApplyThemeResources(Resources);
    }

    private void RegisterUiUnhandledExceptionGuard()
    {
        if (_uiUnhandledExceptionHooked)
        {
            return;
        }

        Dispatcher.UIThread.UnhandledException += OnUiThreadUnhandledException;
        _uiUnhandledExceptionHooked = true;
    }

    private void OnUiThreadUnhandledException(object? sender, DispatcherUnhandledExceptionEventArgs e)
    {
        if (!IsKnownWebViewStartupException(e.Exception))
        {
            return;
        }

        e.Handled = true;
        AppLogger.Warn(
            "WebView2",
            "Suppressed a known WebView startup exception from AvaloniaWebView.Navigate to keep the host process alive.",
            e.Exception);
    }

    private static bool IsKnownWebViewStartupException(Exception exception)
    {
        if (exception is not NullReferenceException)
        {
            return false;
        }

        var stackTrace = exception.StackTrace ?? string.Empty;
        return stackTrace.Contains("AvaloniaWebView.WebView.Navigate", StringComparison.Ordinal) &&
               stackTrace.Contains("AvaloniaWebView.WebView.OnAttachedToVisualTree", StringComparison.Ordinal);
    }

    private void PerformExitCleanup()
    {
        if (_exitCleanupCompleted)
        {
            return;
        }

        _exitCleanupCompleted = true;
        _settingsFacade.Settings.Changed -= OnSettingsChanged;
        _appearanceThemeService.Changed -= OnAppearanceThemeChanged;

        try
        {
            var (analytics, crashReport) = App.AnalyticsServices;
            analytics?.SendShutdownEvent();
            crashReport?.SendShutdownEvent();
        }
        catch (Exception ex)
        {
            AppLogger.Warn("Analytics", "Failed to send shutdown events during exit cleanup.", ex);
        }

        try
        {
            HostUpdateWorkflowServiceProvider.GetOrCreate().TryApplyPendingUpdateOnExit();
        }
        catch (Exception ex)
        {
            AppLogger.Warn("UpdateWorkflow", "Failed to apply pending update during exit cleanup.", ex);
        }

        try
        {
            _pluginRuntimeService?.Dispose();
        }
        catch (Exception ex)
        {
            AppLogger.Warn("PluginRuntime", "Failed to dispose plugin runtime during shutdown.", ex);
        }
        finally
        {
            _pluginRuntimeService = null;
        }

        _settingsWindowService?.Close();
        if (_settingsPageRegistry is IDisposable disposableRegistry)
        {
            disposableRegistry.Dispose();
        }

        AudioRecorderServiceFactory.DisposeSharedServices();
        StudyAnalyticsServiceFactory.DisposeSharedService();
        DisposeTrayIcon();
    }

    private MainWindow CreateAndAssignMainWindow(
        IClassicDesktopStyleApplicationLifetime desktop,
        string reason)
    {
        var mainWindow = new MainWindow
        {
            DataContext = new MainWindowViewModel(),
            ShowInTaskbar = true
        };

        AttachMainWindow(mainWindow);
        desktop.MainWindow = mainWindow;
        AppLogger.Info("App", $"Main window created. Reason='{reason}'. LogFile={AppLogger.LogFilePath}");
        LogBrowserStartupDiagnostics();
        SetDesktopShellState(DesktopShellState.ForegroundDesktop, $"MainWindowCreated:{reason}");
        return mainWindow;
    }

    private MainWindow GetOrCreateMainWindow(
        IClassicDesktopStyleApplicationLifetime desktop,
        string reason)
    {
        if (_mainWindow is not null && !_mainWindowClosed)
        {
            return _mainWindow;
        }

        if (desktop.MainWindow is MainWindow desktopMainWindow && !_mainWindowClosed)
        {
            AttachMainWindow(desktopMainWindow);
            return desktopMainWindow;
        }

        return CreateAndAssignMainWindow(desktop, reason);
    }

    private void AttachMainWindow(MainWindow mainWindow)
    {
        if (ReferenceEquals(_mainWindow, mainWindow))
        {
            _mainWindowClosed = false;
            return;
        }

        if (_mainWindow is not null)
        {
            _mainWindow.Closing -= OnMainWindowClosing;
            _mainWindow.Closed -= OnMainWindowClosed;
            _mainWindow.PropertyChanged -= OnMainWindowPropertyChanged;
        }

        _mainWindow = mainWindow;
        _mainWindowClosed = false;
        mainWindow.Closing += OnMainWindowClosing;
        mainWindow.Closed += OnMainWindowClosed;
        mainWindow.PropertyChanged += OnMainWindowPropertyChanged;
    }

    private void OnMainWindowClosing(object? sender, WindowClosingEventArgs e)
    {
        if (sender is not MainWindow mainWindow)
        {
            return;
        }

        AppLogger.Info(
            "DesktopShell",
            $"Main window closing requested. Intent='{_shutdownIntent}'; ShellState='{_desktopShellState}'; WindowState='{mainWindow.WindowState}'; IsVisible={mainWindow.IsVisible}.");

        if (_shutdownIntent is ShutdownIntent.ExitRequested or ShutdownIntent.RestartRequested)
        {
            AppLogger.Info(
                "DesktopShell",
                $"Main window close allowed. Intent='{_shutdownIntent}'; ShellState='{_desktopShellState}'.");
            return;
        }

        e.Cancel = true;
        HideMainWindowToTray(mainWindow, "MainWindowClosing");
    }

    private void OnMainWindowClosed(object? sender, EventArgs e)
    {
        if (sender is not MainWindow mainWindow)
        {
            return;
        }

        mainWindow.Closing -= OnMainWindowClosing;
        mainWindow.Closed -= OnMainWindowClosed;
        mainWindow.PropertyChanged -= OnMainWindowPropertyChanged;

        if (ReferenceEquals(_mainWindow, mainWindow))
        {
            _mainWindow = null;
        }

        _mainWindowClosed = true;
        AppLogger.Info(
            "DesktopShell",
            $"Main window closed. Intent='{_shutdownIntent}'; ShellState='{_desktopShellState}'.");

        if (_shutdownIntent == ShutdownIntent.None)
        {
            SetDesktopShellState(DesktopShellState.TrayOnly, "MainWindowClosedUnexpected");
        }
    }

    private void OnMainWindowPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
    {
        if (sender is not MainWindow mainWindow)
        {
            return;
        }

        if (e.Property != Window.WindowStateProperty)
        {
            return;
        }

        if (_shutdownIntent != ShutdownIntent.None || !mainWindow.IsVisible)
        {
            return;
        }

        if (mainWindow.WindowState == WindowState.Minimized)
        {
            SetDesktopShellState(DesktopShellState.MinimizedToTaskbar, "MainWindowMinimized");
            return;
        }

        SetDesktopShellState(DesktopShellState.ForegroundDesktop, "MainWindowRestored");
    }

    private void HideMainWindowToTray(MainWindow mainWindow, string source)
    {
        try
        {
            mainWindow.ShowInTaskbar = false;
            mainWindow.Hide();
            SetDesktopShellState(DesktopShellState.TrayOnly, source);
            AppLogger.Info(
                "DesktopShell",
                $"Main window hidden to tray. Source='{source}'; WindowState='{mainWindow.WindowState}'.");
        }
        catch (Exception ex)
        {
            AppLogger.Warn("DesktopShell", $"Failed to hide main window to tray. Source='{source}'.", ex);
        }
    }

    private void SetDesktopShellState(DesktopShellState state, string source)
    {
        if (_desktopShellState == state)
        {
            return;
        }

        var previous = _desktopShellState;
        _desktopShellState = state;
        AppLogger.Info(
            "DesktopShell",
            $"Shell state changed. Previous='{previous}'; Current='{state}'; Source='{source}'.");
    }

    private void LogBrowserStartupDiagnostics()
    {
        try
        {
            var snapshot = new DesktopLayoutSettingsService().Load();
            var browserPlacements = snapshot.DesktopComponentPlacements
                .Where(placement => string.Equals(
                    placement.ComponentId,
                    BuiltInComponentIds.DesktopBrowser,
                    StringComparison.OrdinalIgnoreCase))
                .ToList();
            var runtimeAvailability = WebView2RuntimeProbe.GetAvailability();

            AppLogger.Info(
                "StartupDiagnostics",
                $"Browser component diagnostics. HasBrowserPlacement={browserPlacements.Count > 0}; " +
                $"ActivePageHasBrowser={browserPlacements.Any(item => item.PageIndex == snapshot.CurrentDesktopSurfaceIndex)}; " +
                $"CurrentDesktopSurfaceIndex={snapshot.CurrentDesktopSurfaceIndex}; " +
                $"WebViewRuntimeAvailable={runtimeAvailability.IsAvailable}; " +
                $"WebViewRuntimeVersion={runtimeAvailability.Version ?? string.Empty}; " +
                $"WebViewRuntimeMessage={runtimeAvailability.Message}");
        }
        catch (Exception ex)
        {
            AppLogger.Warn("StartupDiagnostics", "Failed to log browser component diagnostics.", ex);
        }
    }

    private string L(string key, string fallback)
    {
        var snapshot = _settingsFacade.Settings.LoadSnapshot<AppSettingsSnapshot>(SettingsScope.App);
        var languageCode = _localizationService.NormalizeLanguageCode(snapshot.LanguageCode);
        return _localizationService.GetString(languageCode, key, fallback);
    }
}
