using System.Diagnostics;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Models;
using LanMountainDesktop.Launcher.Resources;
using LanMountainDesktop.Launcher.Services;
using LanMountainDesktop.Launcher.Services.AirApp;
using LanMountainDesktop.Launcher.Services.Ipc;
using LanMountainDesktop.Launcher.Views;
using LanMountainDesktop.Shared.Contracts.Launcher;
using LanMountainDesktop.Shared.IPC;
using LanMountainDesktop.Shared.IPC.Abstractions.Services;

namespace LanMountainDesktop.Launcher;

public partial class App : Application
{
    public override void Initialize()
    {
        if (Design.IsDesignMode)
        {
            AvaloniaXamlLoader.Load(this);
            return;
        }

        Logger.Initialize();
        var context = LauncherRuntimeContext.Current;
        var execution = LauncherExecutionContext.Capture();
        Logger.Info(
            $"Launcher App initialize. Command='{context.Command}'; IsGuiMode={context.IsGuiCommand}; " +
            $"IsPreview={context.IsPreviewCommand}; IsDebugMode={context.IsDebugMode}; " +
            $"LaunchSource='{context.LaunchSource}'; IsElevated={execution.IsElevated}; " +
            $"UserSid='{execution.UserSid ?? string.Empty}'; ExplicitAppRoot='{context.ExplicitAppRoot ?? "<none>"}'.");

        AvaloniaXamlLoader.Load(this);
    }

    public override void OnFrameworkInitializationCompleted()
    {
        if (Design.IsDesignMode)
        {
            base.OnFrameworkInitializationCompleted();
            return;
        }

        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;

            var context = LauncherRuntimeContext.Current;
            var execution = LauncherExecutionContext.Capture();
            Logger.Info(
                $"Framework initialization completed. Command='{context.Command}'; IsPreview={context.IsPreviewCommand}; " +
                $"IsDebugMode={context.IsDebugMode}; LaunchSource='{context.LaunchSource}'; " +
                $"IsElevated={execution.IsElevated}; UserSid='{execution.UserSid ?? string.Empty}'.");

            if (HandlePreviewCommand(context, desktop))
            {
                base.OnFrameworkInitializationCompleted();
                return;
            }

            if (context.IsAirAppBrokerCommand)
            {
                _ = RunAirAppBrokerAsync(desktop, context);
                base.OnFrameworkInitializationCompleted();
                return;
            }

            // 调试模式：只显示 DevDebugWindow，不走正常启动流程
            // 避免启动主程序后 Launcher 自动退出，导致开发者无法预览 UI
            if (context.IsDebugMode && !context.IsPreviewCommand &&
                !string.Equals(context.Command, "apply-update", StringComparison.OrdinalIgnoreCase))
            {
                Logger.Info("Debug mode active — showing DevDebugWindow instead of normal launch flow.");
                var devDebugWindow = new DevDebugWindow();
                devDebugWindow.Show();
                base.OnFrameworkInitializationCompleted();
                return;
            }

            if (string.Equals(context.Command, "apply-update", StringComparison.OrdinalIgnoreCase))
            {
                var updateWindow = new UpdateWindow();
                updateWindow.Show();
                _ = RunApplyUpdateWithWindowAsync(desktop, context, updateWindow);
            }
            else
            {
                var splashWindow = CreateSplashWindow();
                splashWindow.Show();
                _ = RunCoordinatorWithSplashAsync(desktop, context, splashWindow);
            }
        }

        base.OnFrameworkInitializationCompleted();
    }

    private static async Task RunAirAppBrokerAsync(
        IClassicDesktopStyleApplicationLifetime desktop,
        CommandContext context)
    {
        var appRoot = Commands.ResolveAppRoot(context);
        var requesterPid = context.GetIntOption("requester-pid", 0);
        var dataLocationResolver = new DataLocationResolver(appRoot);
        Logger.Info($"Air APP broker starting. AppRoot='{appRoot}'; RequesterPid={requesterPid}.");

        using var airAppIpcHost = new LauncherAirAppLifecycleIpcHost(
            new LauncherAirAppLifecycleService(
                new AirAppProcessStarter(
                    new AirAppHostLocator(),
                    () => appRoot,
                    () => null,
                    () => dataLocationResolver.ResolveDataRoot())));
        airAppIpcHost.Start();

        await WaitForAirAppBrokerExitAsync(requesterPid, airAppIpcHost.LifecycleService).ConfigureAwait(false);

        Logger.Info("Air APP broker exiting.");
        await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0), DispatcherPriority.Background);
    }

    internal static async Task WaitForAirAppBrokerExitAsync(
        int requesterPid,
        LauncherAirAppLifecycleService airAppLifecycleService)
    {
        while (ShouldKeepAirAppBrokerAlive(requesterPid, airAppLifecycleService))
        {
            await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
        }
    }

    internal static bool ShouldKeepAirAppBrokerAlive(
        int requesterPid,
        LauncherAirAppLifecycleService airAppLifecycleService)
    {
        return TryGetLiveProcess(requesterPid) || airAppLifecycleService.HasLiveAirApps();
    }

    private bool HandlePreviewCommand(CommandContext context, IClassicDesktopStyleApplicationLifetime desktop)
    {
        switch (context.Command.ToLowerInvariant())
        {
            case "preview-splash":
            {
                Logger.Info("Preview command: splash.");
                var splashWindow = CreateSplashWindow();
                splashWindow.SetDebugMode(true);
                splashWindow.Show();
                _ = SimulateSplashPreviewAsync(desktop, splashWindow);
                return true;
            }
            case "preview-error":
            {
                Logger.Info("Preview command: error.");
                var errorWindow = new ErrorWindow();
                errorWindow.SetErrorMessage(Strings.Preview_ErrorMessage);
                errorWindow.Show();
                _ = WaitForWindowCloseAsync(desktop, errorWindow);
                return true;
            }
            case "preview-multi-instance":
            {
                Logger.Info("Preview command: multi-instance prompt.");
                var promptWindow = new MultiInstancePromptWindow();
                promptWindow.SetDetails(Environment.ProcessId, "ForegroundDesktop");
                promptWindow.Show();
                _ = WaitForWindowCloseAsync(desktop, promptWindow);
                return true;
            }
            case "preview-update":
            {
                Logger.Info("Preview command: update.");
                var updateWindow = new UpdateWindow();
                updateWindow.SetDebugMode(true);
                updateWindow.Show();
                _ = SimulateUpdatePreviewAsync(desktop, updateWindow);
                return true;
            }
            case "preview-oobe":
            {
                Logger.Info("Preview command: oobe.");
                var oobeWindow = new OobeWindow();
                oobeWindow.Show();
                _ = SimulateOobePreviewAsync(desktop, oobeWindow);
                return true;
            }
            case "preview-debug":
            {
                Logger.Info("Preview command: debug window.");
                var devDebugWindow = new DevDebugWindow();
                devDebugWindow.Show();
                return true;
            }
            default:
                return false;
        }
    }

    private static SplashWindow CreateSplashWindow()
    {
        var window = new SplashWindow();
        TrySetSplashVersionInfo(window, LauncherRuntimeContext.Current);
        return window;
    }

    private static void TrySetSplashVersionInfo(SplashWindow window, CommandContext context)
    {
        try
        {
            var appRoot = Commands.ResolveAppRoot(context);
            var versionInfo = new DeploymentLocator(appRoot).GetVersionInfo();
            window.SetVersionInfo(versionInfo.Version, versionInfo.Codename);
        }
        catch (Exception ex)
        {
            Logger.Warn($"Failed to set splash version info before coordinator start: {ex.Message}");
        }
    }

    private async Task SimulateSplashPreviewAsync(IClassicDesktopStyleApplicationLifetime desktop, SplashWindow window)
    {
        var stages = new[] { "initializing", "update", "plugins", "launch", "ready" };
        var messages = new[] { Strings.Preview_SplashInitializing, Strings.Preview_SplashCheckingUpdates, Strings.Preview_SplashCheckingPlugins, Strings.Preview_SplashLaunchingHost, Strings.Preview_SplashReady };
        var reporter = (ISplashStageReporter)window;

        for (var i = 0; i < stages.Length; i++)
        {
            reporter.Report(stages[i], messages[i]);
            await Task.Delay(800).ConfigureAwait(false);
        }

        await Task.Delay(5000).ConfigureAwait(false);
        await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
    }

    private async Task SimulateUpdatePreviewAsync(IClassicDesktopStyleApplicationLifetime desktop, UpdateWindow window)
    {
        var stages = new[] { "verify", "extract", "apply", "plugins", "cleanup" };

        for (var i = 0; i < stages.Length; i++)
        {
            window.Report(stages[i], string.Format(Strings.Preview_UpdateProcessing, stages[i]), (i + 1) * 20);
            await Task.Delay(600).ConfigureAwait(false);
        }

        window.ReportComplete(true, null);
        await Task.Delay(3000).ConfigureAwait(false);
        await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
    }

    private async Task SimulateOobePreviewAsync(IClassicDesktopStyleApplicationLifetime desktop, OobeWindow window)
    {
        try
        {
            await window.WaitForEnterAsync().ConfigureAwait(false);
            Logger.Info("OOBE preview completed by user.");
        }
        catch (Exception ex)
        {
            Logger.Error("OOBE preview failed.", ex);
        }

        await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
    }

    private async Task WaitForWindowCloseAsync(IClassicDesktopStyleApplicationLifetime desktop, Window window)
    {
        var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        window.Closed += (_, _) => tcs.TrySetResult();
        await tcs.Task.ConfigureAwait(false);
        await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(0));
    }

    private static async Task RunCoordinatorWithSplashAsync(
        IClassicDesktopStyleApplicationLifetime desktop,
        CommandContext context,
        SplashWindow splashWindow)
    {
        LauncherResult result;
        SplashWindow? currentSplashWindow = splashWindow;
        var appRoot = Commands.ResolveAppRoot(context);
        var dataLocationResolver = new DataLocationResolver(appRoot);
        var startupAttemptRegistry = new StartupAttemptRegistry();
        var coordinatorPipeName = LauncherCoordinatorIpcServer.CreatePipeName();
        var successPolicy = LauncherFlowCoordinator.ResolveSuccessPolicyKey(context);
        if (!startupAttemptRegistry.TryReserveCoordinator(
                context.LaunchSource,
                successPolicy,
                coordinatorPipeName,
                out var reservedAttempt,
                out var activeCoordinatorAttempt))
        {
            result = await AttachToExistingCoordinatorAsync(
                context,
                currentSplashWindow,
                activeCoordinatorAttempt).ConfigureAwait(false);

            Logger.Info($"Secondary launcher completed. Success={result.Success}; Code='{result.Code}'.");
            await WriteLauncherResultAsync(context, result).ConfigureAwait(false);

            Environment.ExitCode = result.Success ? 0 : 1;
            await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(Environment.ExitCode), DispatcherPriority.Background);
            return;
        }

        using var airAppIpcHost = new LauncherAirAppLifecycleIpcHost(
            new LauncherAirAppLifecycleService(
                new AirAppProcessStarter(
                    new AirAppHostLocator(),
                    () => appRoot,
                    () => null,
                    () => dataLocationResolver.ResolveDataRoot())));
        airAppIpcHost.Start();

        using var coordinatorServer = new LauncherCoordinatorIpcServer(
            coordinatorPipeName,
            BuildCoordinatorStatusFromAttempt(reservedAttempt),
            HandleCoordinatorRequestAsync,
            startupAttemptRegistry.UpdateOwnedCoordinatorHeartbeat);
        coordinatorServer.Start();

        while (true)
        {
            try
            {
                Logger.Info(
                    $"Coordinator start. Command='{context.Command}'; AppRoot='{appRoot}'; " +
                    $"IsDebugMode={context.IsDebugMode}; LaunchSource='{context.LaunchSource}'; " +
                    $"ResultPath='{context.GetOption("result") ?? "<none>"}'.");

                var deploymentLocator = new DeploymentLocator(appRoot);
                var coordinator = new LauncherFlowCoordinator(
                    context,
                    deploymentLocator,
                    new OobeStateService(appRoot),
                    new UpdateEngineService(deploymentLocator),
                    new PluginInstallerService(),
                    startupAttemptRegistry,
                    coordinatorServer);

                result = await coordinator.RunAsync(currentSplashWindow).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Logger.Error("Coordinator threw an unhandled exception.", ex);
                result = new LauncherResult
                {
                    Success = false,
                    Stage = "launch",
                    Code = "exception",
                    Message = $"Launcher failed: {ex.Message}",
                    ErrorMessage = ex.ToString()
                };
            }

            if (result.Success ||
                result.Code == "host_not_found" ||
                (!string.Equals(result.Stage, "launch", StringComparison.OrdinalIgnoreCase) &&
                 !string.Equals(result.Stage, "launchHost", StringComparison.OrdinalIgnoreCase)))
            {
                break;
            }

            var failureAction = await ShowFailureWindowAsync(result).ConfigureAwait(false);
            if (failureAction == ErrorWindowResult.Exit)
            {
                break;
            }

            if (failureAction == ErrorWindowResult.ActivateExisting &&
                await TryActivateExistingInstanceAsync().ConfigureAwait(false))
            {
                result = new LauncherResult
                {
                    Success = true,
                    Stage = "launch",
                    Code = "activation_requested",
                    Message = "Launcher activated the existing desktop instance.",
                    Details = result.Details
                };
                break;
            }

            currentSplashWindow = CreateSplashWindow();
            currentSplashWindow.Show();
        }

        Logger.Info($"Coordinator completed. Success={result.Success}; Stage='{result.Stage}'; Code='{result.Code}'.");
        await WriteLauncherResultAsync(context, result).ConfigureAwait(false);

        Environment.ExitCode = result.Success ? 0 : 1;
        if (result.Success)
        {
            var hostPid = ResolveManagedHostPid(result, startupAttemptRegistry.GetOwnedAttempt()?.HostPid ?? 0);
            await WaitForManagedProcessesToExitAsync(hostPid, airAppIpcHost.LifecycleService).ConfigureAwait(false);
        }

        await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(Environment.ExitCode), DispatcherPriority.Background);
    }

    private static int ResolveManagedHostPid(LauncherResult result, int fallbackHostPid)
    {
        if (result.Details.TryGetValue("hostPid", out var hostPidText) &&
            int.TryParse(hostPidText, out var hostPid))
        {
            return hostPid;
        }

        if (result.Details.TryGetValue("existingHostPid", out var existingHostPidText) &&
            int.TryParse(existingHostPidText, out var existingHostPid))
        {
            return existingHostPid;
        }

        return fallbackHostPid;
    }

    private static async Task WaitForManagedProcessesToExitAsync(
        int hostPid,
        LauncherAirAppLifecycleService airAppLifecycleService)
    {
        Logger.Info($"Launcher entering managed background lifetime. HostPid={hostPid}.");
        while (TryGetLiveProcess(hostPid) || airAppLifecycleService.HasLiveAirApps())
        {
            await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
        }

        Logger.Info("Launcher managed background lifetime completed; no host or Air APP process remains.");
    }

    private static async Task<LauncherResult> AttachToExistingCoordinatorAsync(
        CommandContext context,
        SplashWindow? splashWindow,
        StartupAttemptRecord? activeCoordinatorAttempt)
    {
        var reporter = splashWindow as ISplashStageReporter;
        reporter?.Report("activation", Strings.Preview_ActivationConnecting);

        if (activeCoordinatorAttempt is not null &&
            !string.IsNullOrWhiteSpace(activeCoordinatorAttempt.CoordinatorPipeName))
        {
            var command = string.Equals(context.LaunchSource, "restart", StringComparison.OrdinalIgnoreCase)
                ? LauncherCoordinatorCommands.Attach
                : LauncherCoordinatorCommands.ActivateDesktop;
            var request = new LauncherCoordinatorRequest
            {
                Command = command,
                LaunchSource = context.LaunchSource,
                SuccessPolicy = LauncherFlowCoordinator.ResolveSuccessPolicyKey(context)
            };

            var response = await new LauncherCoordinatorIpcClient()
                .SendAsync(activeCoordinatorAttempt.CoordinatorPipeName, request, TimeSpan.FromSeconds(2))
                .ConfigureAwait(false);

            if (response is not null)
            {
                reporter?.Report("activation", response.Message);
                await DismissSplashIfNeededAsync(splashWindow).ConfigureAwait(false);
                var success = response.Accepted ||
                              IsRecoverableActivationFailure(response.ActivationResult, response.Status);
                return new LauncherResult
                {
                    Success = success,
                    Stage = "launch",
                    Code = success && !response.Accepted ? "attached_to_launcher_coordinator" : response.Code,
                    Message = success && !response.Accepted
                        ? "Attached to the active Launcher coordinator; desktop startup is still in progress."
                        : response.Message,
                    Details = BuildCoordinatorResultDetails(response.Status, response.ActivationResult)
                };
            }
        }

        var activation = await TryActivateExistingInstanceWithStatusAsync(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
        if (activation is not null)
        {
            reporter?.Report("activation", activation.Message);
            await DismissSplashIfNeededAsync(splashWindow).ConfigureAwait(false);
            var success = activation.Accepted || IsRecoverableActivationFailure(activation, null);
            return new LauncherResult
            {
                Success = success,
                Stage = "launch",
                Code = activation.Accepted
                    ? "existing_host_activated"
                    : success
                        ? "existing_host_startup_pending"
                        : "existing_host_activation_failed",
                Message = success && !activation.Accepted
                    ? "Existing desktop process is still starting; Launcher attached without starting another process."
                    : activation.Message,
                Details = BuildCoordinatorResultDetails(null, activation)
            };
        }

        await DismissSplashIfNeededAsync(splashWindow).ConfigureAwait(false);
        return new LauncherResult
        {
            Success = false,
            Stage = "launch",
            Code = "launcher_coordinator_unavailable",
            Message = "Another Launcher is coordinating startup, but it did not respond in time.",
            Details = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
            {
                ["activeCoordinatorPid"] = activeCoordinatorAttempt?.CoordinatorPid.ToString() ?? string.Empty,
                ["activeCoordinatorPipeName"] = activeCoordinatorAttempt?.CoordinatorPipeName ?? string.Empty,
                ["activeAttemptId"] = activeCoordinatorAttempt?.AttemptId ?? string.Empty,
                ["activeHostPid"] = activeCoordinatorAttempt?.HostPid.ToString() ?? string.Empty
            }
        };
    }

    private static async Task<LauncherCoordinatorResponse> HandleCoordinatorRequestAsync(
        LauncherCoordinatorRequest request,
        LauncherCoordinatorStatus status)
    {
        if (string.Equals(request.Command, LauncherCoordinatorCommands.ActivateDesktop, StringComparison.OrdinalIgnoreCase))
        {
            var activation = await TryActivateExistingInstanceWithStatusAsync(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
            if (activation is not null)
            {
                if (!activation.Accepted && IsRecoverableActivationFailure(activation, status))
                {
                    return new LauncherCoordinatorResponse
                    {
                        Accepted = true,
                        Code = "attached_to_launcher_coordinator",
                        Message = "Attached to the active Launcher coordinator; desktop startup is still in progress.",
                        Status = status,
                        ActivationResult = activation
                    };
                }

                return new LauncherCoordinatorResponse
                {
                    Accepted = activation.Accepted,
                    Code = activation.Accepted ? "existing_host_activated" : "existing_host_activation_failed",
                    Message = activation.Message,
                    Status = status,
                    ActivationResult = activation
                };
            }

            return new LauncherCoordinatorResponse
            {
                Accepted = true,
                Code = "attached_to_launcher_coordinator",
                Message = "Attached to the active Launcher coordinator; desktop startup is still in progress.",
                Status = status
            };
        }

        return new LauncherCoordinatorResponse
        {
            Accepted = true,
            Code = "attached_to_launcher_coordinator",
            Message = "Attached to the active Launcher coordinator.",
            Status = status
        };
    }

    private static LauncherCoordinatorStatus BuildCoordinatorStatusFromAttempt(StartupAttemptRecord attempt)
    {
        return new LauncherCoordinatorStatus
        {
            AttemptId = attempt.AttemptId,
            CoordinatorPid = Environment.ProcessId,
            HostPid = attempt.HostPid,
            HostProcessAlive = TryGetLiveProcess(attempt.HostPid),
            LaunchSource = attempt.LaunchSource,
            SuccessPolicy = attempt.SuccessPolicy,
            LastObservedStage = attempt.LastObservedStage,
            LastObservedMessage = attempt.LastObservedMessage,
            PublicIpcConnected = attempt.PublicIpcConnected || attempt.IpcConnected,
            State = attempt.State.ToString(),
            SoftTimeoutShown = attempt.State is StartupAttemptState.SoftTimeout or StartupAttemptState.DetachedWaiting,
            Completed = attempt.State is StartupAttemptState.Succeeded or StartupAttemptState.Failed,
            Succeeded = attempt.State == StartupAttemptState.Succeeded,
            UpdatedAtUtc = attempt.UpdatedAtUtc
        };
    }

    private static bool IsRecoverableActivationFailure(
        PublicShellActivationResult? activation,
        LauncherCoordinatorStatus? status)
    {
        if (activation is { Accepted: true })
        {
            return false;
        }

        if (status is { Completed: false, HostProcessAlive: true })
        {
            return true;
        }

        var shellStatus = activation?.Status;
        if (shellStatus is null || !shellStatus.PublicIpcReady)
        {
            return false;
        }

        return !shellStatus.MainWindowOpened ||
               !shellStatus.DesktopVisible ||
               string.Equals(activation?.Code, "shell_not_ready", StringComparison.OrdinalIgnoreCase) ||
               string.Equals(activation?.Code, "startup_pending", StringComparison.OrdinalIgnoreCase);
    }

    private static Dictionary<string, string> BuildCoordinatorResultDetails(
        LauncherCoordinatorStatus? status,
        PublicShellActivationResult? activation)
    {
        var details = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            ["coordinatorPid"] = status?.CoordinatorPid.ToString() ?? string.Empty,
            ["coordinatorAttemptId"] = status?.AttemptId ?? string.Empty,
            ["hostPid"] = status?.HostPid.ToString() ?? activation?.Status.ProcessId.ToString() ?? string.Empty,
            ["hostProcessAlive"] = status?.HostProcessAlive.ToString() ?? string.Empty,
            ["publicIpcConnected"] = (status?.PublicIpcConnected ?? activation is not null).ToString(),
            ["startupStage"] = status?.LastObservedStage.ToString() ?? string.Empty,
            ["startupState"] = status?.State ?? string.Empty,
            ["activationAccepted"] = activation?.Accepted.ToString() ?? string.Empty,
            ["shellState"] = activation?.Status.ShellState ?? status?.ShellStatus?.ShellState ?? string.Empty,
            ["trayState"] = activation?.Status.Tray.State ?? status?.ShellStatus?.Tray.State ?? string.Empty,
            ["taskbarUsable"] = activation?.Status.Taskbar.IsUsable.ToString() ?? status?.ShellStatus?.Taskbar.IsUsable.ToString() ?? string.Empty
        };

        return details;
    }

    private static async Task DismissSplashIfNeededAsync(SplashWindow? splashWindow)
    {
        if (splashWindow is null)
        {
            return;
        }

        try
        {
            await splashWindow.DismissAsync().ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Logger.Warn($"Failed to dismiss splash after coordinator attach: {ex.Message}");
        }
    }

    private static async Task WriteLauncherResultAsync(CommandContext context, LauncherResult result)
    {
        var resultPath = context.GetOption("result");
        if (string.IsNullOrWhiteSpace(resultPath))
        {
            return;
        }

        try
        {
            await Commands.WriteResultIfNeededAsync(resultPath, result).ConfigureAwait(false);
            Logger.Info($"Launcher result written to '{Path.GetFullPath(resultPath)}'.");
        }
        catch (Exception ex)
        {
            Logger.Error($"Failed to write launcher result to '{resultPath}'.", ex);
        }
    }

    private static async Task<ErrorWindowResult> ShowFailureWindowAsync(LauncherResult result)
    {
        ErrorWindow? errorWindow = null;
        var hostProcessAlive = result.Details.TryGetValue("hostProcessAlive", out var hostProcessAliveText) &&
                               bool.TryParse(hostProcessAliveText, out var hostProcessAliveValue) &&
                               hostProcessAliveValue;
        var hostPid = result.Details.TryGetValue("hostPid", out var hostPidText) &&
                      int.TryParse(hostPidText, out var parsedPid)
            ? parsedPid
            : (int?)null;

        await Dispatcher.UIThread.InvokeAsync(() =>
        {
            try
            {
                errorWindow = new ErrorWindow();
                if (hostProcessAlive)
                {
                    errorWindow.ConfigureForRunningHostFailure(hostPid);
                }
                else
                {
                    errorWindow.ConfigureForGenericFailure(allowRetry: true);
                }

                errorWindow.SetErrorMessage(
                    $"Failed to start LanMountainDesktop.\n\nStage: {result.Stage}\nCode: {result.Code}\n\n{result.Message}");
                errorWindow.Show();
            }
            catch (Exception ex)
            {
                Logger.Error("Failed to show launcher failure window.", ex);
            }
        });

        if (errorWindow is null)
        {
            return ErrorWindowResult.Exit;
        }

        try
        {
            return await errorWindow.WaitForChoiceAsync().ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Logger.Error("Failure window closed unexpectedly.", ex);
            return ErrorWindowResult.Exit;
        }
    }

    private static async Task<bool> TryActivateExistingInstanceAsync()
    {
        var activation = await TryActivateExistingInstanceWithStatusAsync(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
        return activation?.Accepted == true;
    }

    private static async Task<PublicShellActivationResult?> TryActivateExistingInstanceWithStatusAsync(TimeSpan timeout)
    {
        try
        {
            using var ipcClient = new LanMountainDesktopIpcClient();
            var connectTask = ipcClient.ConnectAsync();
            var completedTask = await Task.WhenAny(connectTask, Task.Delay(timeout)).ConfigureAwait(false);
            if (completedTask != connectTask)
            {
                return null;
            }

            await connectTask.ConfigureAwait(false);
            if (!ipcClient.IsConnected)
            {
                return null;
            }

            var shellProxy = ipcClient.CreateProxy<IPublicShellControlService>();
            var activationTask = shellProxy.ActivateMainWindowWithStatusAsync();
            completedTask = await Task.WhenAny(activationTask, Task.Delay(timeout)).ConfigureAwait(false);
            if (completedTask != activationTask)
            {
                return null;
            }

            return await activationTask.ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Logger.Warn($"Failed to activate the existing desktop instance: {ex.Message}");
            return null;
        }
    }

    private static bool TryGetLiveProcess(int processId)
    {
        if (processId <= 0)
        {
            return false;
        }

        try
        {
            using var process = Process.GetProcessById(processId);
            return !process.HasExited;
        }
        catch
        {
            return false;
        }
    }

    private static async Task RunApplyUpdateWithWindowAsync(
        IClassicDesktopStyleApplicationLifetime desktop,
        CommandContext context,
        UpdateWindow window)
    {
        var appRoot = Commands.ResolveAppRoot(context);
        var deploymentLocator = new DeploymentLocator(appRoot);
        var updateEngine = new UpdateEngineService(deploymentLocator);
        var pluginInstaller = new PluginInstallerService();
        var pluginUpgrades = new PluginUpgradeQueueService(pluginInstaller);

        var success = true;
        string? errorMessage = null;

        try
        {
            await Dispatcher.UIThread.InvokeAsync(() => window.Report("verify", Strings.Update_Verifying, 10));
            var updateResult = await updateEngine.ApplyPendingUpdateAsync().ConfigureAwait(false);
            if (!updateResult.Success)
            {
                success = false;
                errorMessage = updateResult.Message;
            }

            if (success)
            {
                await Dispatcher.UIThread.InvokeAsync(() => window.Report("plugins", Strings.Update_ApplyingPlugins, 60));
                var pluginsDir = context.GetOption("plugins-dir") ?? Path.Combine(appRoot, "plugins");
                var queueResult = pluginUpgrades.ApplyPendingUpgrades(pluginsDir);
                if (!queueResult.Success && queueResult.Code != "noop")
                {
                    Logger.Error($"Plugin upgrade failed during apply-update: {queueResult.Message}");
                }
            }

            if (success)
            {
                await Dispatcher.UIThread.InvokeAsync(() => window.Report("cleanup", Strings.Update_CleaningUp, 90));
                deploymentLocator.CleanupOldDeployments(minVersionsToKeep: 3);
            }
        }
        catch (Exception ex)
        {
            success = false;
            errorMessage = ex.Message;
            Logger.Error("Apply-update flow failed.", ex);
        }

        await Dispatcher.UIThread.InvokeAsync(() => window.ReportComplete(success, errorMessage));
        await Task.Delay(success ? 1500 : 5000).ConfigureAwait(false);

        await Commands.WriteResultIfNeededAsync(context.GetOption("result"), new LauncherResult
        {
            Success = success,
            Stage = "apply-update",
            Code = success ? "ok" : "failed",
            Message = success ? "Update applied successfully." : (errorMessage ?? "Unknown error"),
            Details = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
            {
                ["command"] = context.Command,
                ["launchSource"] = context.LaunchSource
            }
        }).ConfigureAwait(false);

        Environment.ExitCode = success ? 0 : 1;
        await Dispatcher.UIThread.InvokeAsync(() => desktop.Shutdown(Environment.ExitCode), DispatcherPriority.Background);
    }
}
