using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Runtime.CompilerServices;

using System.Windows;

using System.Windows.Input;

using Microsoft.Win32;

using NexusInstaller.Models;
using NexusInstaller.Services;
using NexusInstaller.Views;



namespace NexusInstaller.ViewModels;



public sealed class NexusSetupViewModel : INotifyPropertyChanged

{

    private readonly InstallerOptions _options;

    private readonly PrerequisiteService _prerequisites = new();

    private readonly InstallerLogService _installerLog = InstallerLogService.Instance;
    private readonly InstallProgressCoordinator _installProgress;
    private readonly System.Windows.Threading.DispatcherTimer _elapsedTimer;

    private InstallerStep _currentStep = InstallerStep.Welcome;

    private string _installRoot;

    private OrganizationOption? _selectedOrganization;
    private RoleOption? _selectedRole;

    private string _userEmail = "user@nexus.local";

    private string _deviceName;

    private string _logText = string.Empty;

    private bool _isLogExpanded;

    private bool _isInstalling;

    private bool _isInstalled;

    private bool _startAfterInstall;

    private bool _freshPocResetConfirmed;
    private bool _installFailed;
    private string? _errorMessage;

    private string _generatedDeviceId = "—";
    private string _installPathValidationMessage = string.Empty;



    public NexusSetupViewModel(InstallerOptions options)

    {

        _options = options;

        _installRoot = string.IsNullOrWhiteSpace(options.InstallRoot)
            ? @"C:\NinjaNexusPOC"
            : options.InstallRoot;

        _deviceName = NexusIdentityBootstrapService.ResolveDefaultDeviceName();

        Organizations = new ObservableCollection<OrganizationOption>();
        Roles = new ObservableCollection<RoleOption>(RoleCatalog.All);
        SelectedRole = Roles.FirstOrDefault(r => r.Name == "User") ?? Roles.FirstOrDefault();

        ReadinessItems = new ObservableCollection<IdentityReadinessItem>(
        [
            new() { Key = "api", Label = "Identity API" },
            new() { Key = "sqlite", Label = "SQLite datamappe" },
            new() { Key = "orgs", Label = "Organisationer" },
            new() { Key = "roles", Label = "Roller" },
            new() { Key = "device", Label = "Enhedsnavn" },
            new() { Key = "enrollment", Label = "Enrollment gemt" },
        ]);

        foreach (var org in OrganizationCatalog.All)
        {
            Organizations.Add(org);
        }

        SelectedOrganization = Organizations.Count == 1
            ? Organizations[0]
            : Organizations.FirstOrDefault();

        Requirements = new ObservableCollection<RequirementCheck>();

        InstallPhases = new ObservableCollection<InstallPhaseItem>(
        [
            new() { Index = 0, Label = "Administrator-tjek" },
            new() { Index = 1, Label = "Stopper Nexus-processer" },
            new() { Index = 2, Label = "Fil-lås kontrol" },
            new() { Index = 3, Label = "Rydder gamle binærfiler" },
            new() { Index = 4, Label = "Data bevar/nulstil" },
            new() { Index = 5, Label = "Opretter mapper" },
            new() { Index = 6, Label = "Installerer forudsætninger" },
            new() { Index = 7, Label = "Udpakker Ninja Nexus" },
            new() { Index = 8, Label = "Verificerer SHA256" },
            new() { Index = 9, Label = "SQLite dat sti" },
            new() { Index = 10, Label = "Starter Identity API" },
            new() { Index = 11, Label = "Venter på API health" },
            new() { Index = 12, Label = "Database migrationer" },
            new() { Index = 13, Label = "Indlæser organisationer" },
            new() { Index = 14, Label = "Indlæser roller" },
            new() { Index = 15, Label = "Enhedsnavn detekteret" },
            new() { Index = 16, Label = "Gemmer enrollment" },
            new() { Index = 17, Label = "Validerer installation" },
            new() { Index = 18, Label = "Fuldført" },
        ]);

        _installProgress = new InstallProgressCoordinator();
        _installProgress.ProgressChanged += () =>
            Application.Current.Dispatcher.Invoke(NotifyInstallProgress);

        _elapsedTimer = new System.Windows.Threading.DispatcherTimer
        {
            Interval = TimeSpan.FromSeconds(1),
        };
        _elapsedTimer.Tick += (_, _) => NotifyRunningStepElapsed();



        NextCommand = new RelayCommand(_ => GoNext(), _ => CanGoNext && !IsInstalling);

        BackCommand = new RelayCommand(_ => GoBack(), _ => CanGoBack && !IsInstalling);

        BrowseCommand = new RelayCommand(_ => BrowseFolder());

        RefreshRequirementsCommand = new RelayCommand(async _ => await RefreshRequirementsAsync());

        DownloadDotNetCommand = new RelayCommand(_ => SystemRequirementsService.OpenDotNetDownload());

        InstallCommand = new RelayCommand(async _ => await InstallAsync(), _ => CanInstall && !IsInstalled);

        StartNexusCommand = new RelayCommand(async _ => await StartNexusAsync(), _ => IsInstalled && !IsInstalling);

        ToggleLogCommand = new RelayCommand(_ => IsLogExpanded = !IsLogExpanded);
        RelaunchAsAdminCommand = new RelayCommand(_ => RelaunchAsAdmin());
        RetryInstallCommand = new RelayCommand(_ => _ = InstallAsync(), _ => _installFailed && !IsInstalling);
        ClearStuckCommand = new RelayCommand(_ => ClearStuckState());
        OpenLogsCommand = new RelayCommand(_ => OpenLogsFolder());
        CancelInstallCommand = new RelayCommand(_ => CancelInstall(), _ => IsInstalling);
        ExitInstallerCommand = new RelayCommand(_ => Application.Current.Shutdown(), _ => IsInstalled && !IsInstalling);



        _installerLog.LineAdded += line => Application.Current.Dispatcher.Invoke(() =>

        {

            LogText += line + Environment.NewLine;

        });



        LogText = _installerLog.FullText;

        _installerLog.Info("Nexus Setup startet.");
        UpdateInstallPathValidation();



        _ = RefreshRequirementsAsync();

    }



    public event PropertyChangedEventHandler? PropertyChanged;



    public ObservableCollection<OrganizationOption> Organizations { get; }

    public ObservableCollection<RequirementCheck> Requirements { get; }

    public ObservableCollection<InstallPhaseItem> InstallPhases { get; }

    public ObservableCollection<InstallProgressEvent> RecentInstallEvents => _installProgress.RecentEvents;

    public double InstallProgressPercent => InstallStepStatusRules.ProgressPercent(InstallPhases);

    public string InstallProgressLabel
    {
        get
        {
            var completed = InstallStepStatusRules.CountCompleted(InstallPhases);
            var total = InstallPhases.Count;
            if (IsInstalled)
            {
                return $"Installation fuldført ({total} af {total})";
            }

            var current = InstallStepStatusRules.FindRunning(InstallPhases);
            var currentNumber = current is null ? completed + 1 : current.Index + 1;
            return $"Installerer {Math.Min(currentNumber, total)} af {total}";
        }
    }

    public string CurrentInstallAction =>
        InstallStepStatusRules.FindRunning(InstallPhases)?.Label
        ?? (IsInstalled ? "Nexus er klar" : "Afventer start");

    public string InstallStatusPill =>
        InstallStepStatusRules.GlobalStatusPill(IsInstalling, IsInstalled, _installProgress.IsHung, InstallPhases);

    public bool IsInstallHung => _installProgress.IsHung;

    public bool ShowInstallProgressHeader =>
        IsInstalling || IsInstalled || InstallPhases.Any(p => p.Status != InstallStepStatus.Pending);

    public bool IsRunningAsAdmin => AdminElevationHelper.IsRunningElevated();

    public string AdminStatusText => AdminElevationHelper.AdminStatusLabel;

    public bool ShowElevationPrompt => AdminElevationHelper.RequiresElevationForInstall;

    public bool FreshPocResetConfirmed
    {
        get => _freshPocResetConfirmed;
        set { _freshPocResetConfirmed = value; Notify(); }
    }

    public bool CanInstall => IsRunningAsAdmin && !IsInstalling && IsInstallRootValid();
    public bool ShowCompletionScreen => IsInstalled && !IsInstalling && !_installFailed;

    public bool ShowInstallFailedActions => _installFailed && !IsInstalling;

    public string LogsFolderPath => Path.Combine(InstallRoot, "logs");

    public ObservableCollection<RoleOption> Roles { get; }

    public ObservableCollection<IdentityReadinessItem> ReadinessItems { get; }

    public string BuildLabel => InstallerBuildInfo.DisplayLabel;

    public bool AreIdentityInputsEnabled => !IsInstalling;

    public string InstallPathValidationMessage
    {
        get => _installPathValidationMessage;
        private set { _installPathValidationMessage = value; Notify(); Notify(nameof(ShowInstallPathValidation)); }
    }

    public bool ShowInstallPathValidation => !string.IsNullOrWhiteSpace(InstallPathValidationMessage);

    public bool ShowOrganizationStatus => Organizations.Count == 0 || SelectedOrganization is null;

    public string OrganizationStatusMessage => Organizations.Count switch
    {
        0 => "Ingen organisationer tilgængelige. Genstart installeren eller kontakt support.",
        _ when SelectedOrganization is null => "Vælg organisation i listen ovenfor.",
        _ => string.Empty,
    };



    public int CurrentStepNumber => (int)_currentStep;



    public string StepTitle => _currentStep switch

    {

        InstallerStep.Welcome => "Velkommen til Ninja Nexus",

        InstallerStep.Requirements => "Systemkrav",

        InstallerStep.InstallPath => "Installationsmappe",

        InstallerStep.Identity => "Organisation og bruger",

        InstallerStep.Security => "Sikkerhed",

        InstallerStep.Install => IsInstalled ? "Installation fuldført" : "Installér og start",

        _ => string.Empty,

    };



    public string StepSubtitle => _currentStep switch

    {

        InstallerStep.Welcome => "Et system. Alt forbundet.",

        InstallerStep.Requirements => "Vi tjekker at denne PC er klar til Nexus. Manglende komponenter installeres automatisk.",

        InstallerStep.InstallPath => "Vælg hvor Ninja Nexus skal installeres.",

        InstallerStep.Identity => "Tildel enheden til organisation og bruger.",

        InstallerStep.Security => "Gennemgå hvad installationen aktiverer.",

        InstallerStep.Install => IsInstalled

            ? "Nexus er klar. Start systemet når du er klar."

            : "Installationen forbereder din Nexus-platform.",

        _ => string.Empty,

    };



    public bool IsWelcomeStep => _currentStep == InstallerStep.Welcome;

    public bool IsRequirementsStep => _currentStep == InstallerStep.Requirements;

    public bool IsPathStep => _currentStep == InstallerStep.InstallPath;

    public bool IsIdentityStep => _currentStep == InstallerStep.Identity;

    public bool IsSecurityStep => _currentStep == InstallerStep.Security;

    public bool IsInstallStep => _currentStep == InstallerStep.Install;



    public bool IsStep1Active => CurrentStepNumber >= 1;
    public bool IsStep2Active => CurrentStepNumber >= 2;
    public bool IsStep3Active => CurrentStepNumber >= 3;
    public bool IsStep4Active => CurrentStepNumber >= 4;
    public bool IsStep5Active => CurrentStepNumber >= 5;
    public bool IsStep6Active => CurrentStepNumber >= 6;



    public string InstallRoot

    {

        get => _installRoot;

        set
        {
            if (string.Equals(_installRoot, value, StringComparison.Ordinal))
            {
                return;
            }

            _installRoot = value;
            UpdateInstallPathValidation();
            Notify();
            NotifyNavigation();
        }

    }



    public OrganizationOption? SelectedOrganization

    {

        get => _selectedOrganization;

        set { _selectedOrganization = value; Notify(); NotifyNavigation(); Notify(nameof(ShowOrganizationStatus)); Notify(nameof(OrganizationStatusMessage)); }

    }

    public RoleOption? SelectedRole
    {
        get => _selectedRole;
        set { _selectedRole = value; Notify(); NotifyNavigation(); }
    }



    public string UserEmail

    {

        get => _userEmail;

        set
        {
            if (string.Equals(_userEmail, value, StringComparison.Ordinal))
            {
                return;
            }

            _userEmail = value;
            Notify();
            NotifyNavigation();
        }

    }



    public string Role => SelectedRole?.Name ?? "User";



    public string DeviceName

    {

        get => _deviceName;

        set
        {
            if (string.Equals(_deviceName, value, StringComparison.Ordinal))
            {
                return;
            }

            _deviceName = value;
            Notify();
            NotifyNavigation();
        }

    }



    public string GeneratedDeviceId

    {

        get => _generatedDeviceId;

        private set { _generatedDeviceId = value; Notify(); }

    }



    public string LogText

    {

        get => _logText;

        private set { _logText = value; Notify(); }

    }



    public bool IsLogExpanded

    {

        get => _isLogExpanded;

        set { _isLogExpanded = value; Notify(); Notify(nameof(LogToggleLabel)); }

    }



    public string LogToggleLabel => IsLogExpanded ? "Skjul installationslog" : "Vis installationslog";



    public bool IsInstalling

    {

        get => _isInstalling;

        private set
        {
            _isInstalling = value;
            Notify();
            Notify(nameof(ShowCompletionScreen));
            NotifyNavigation();
        }

    }



    public bool IsInstalled

    {

        get => _isInstalled;

        private set

        {

            _isInstalled = value;

            Notify();

            NotifyNavigation();

            Notify(nameof(StepTitle));

            Notify(nameof(StepSubtitle));

            Notify(nameof(ShowPrimaryOnInstallStep));
            Notify(nameof(ShowCompletionScreen));

            NotifyInstallProgress();

        }

    }



    public bool StartAfterInstall

    {

        get => _startAfterInstall;

        set { _startAfterInstall = value; Notify(); }

    }



    public bool HasAutoInstallPending => SystemRequirementsService.HasAutoInstallPending(Requirements);



    public string? ErrorMessage

    {

        get => _errorMessage;

        private set { _errorMessage = value; Notify(); }

    }



    public bool CanGoBack => _currentStep > InstallerStep.Welcome;



    public bool CanGoNext => _currentStep switch

    {

        InstallerStep.Welcome => true,

        InstallerStep.Requirements => !SystemRequirementsService.HasBlockingIssues(Requirements),

        InstallerStep.InstallPath => IsInstallRootValid(),

        InstallerStep.Identity => SelectedOrganization is not null
            && SelectedRole is not null
            && !string.IsNullOrWhiteSpace(UserEmail)
            && !string.IsNullOrWhiteSpace(DeviceName),

        InstallerStep.Security => true,

        InstallerStep.Install => false,

        _ => false,

    };



    public string PrimaryButtonLabel => _currentStep switch

    {

        InstallerStep.Welcome => "Start installation",

        InstallerStep.Install => "Næste",

        _ => "Næste",

    };



    public bool ShowPrimaryOnInstallStep => _currentStep == InstallerStep.Install && !IsInstalled;



    public ICommand NextCommand { get; }

    public ICommand BackCommand { get; }

    public ICommand BrowseCommand { get; }

    public ICommand RefreshRequirementsCommand { get; }

    public ICommand DownloadDotNetCommand { get; }

    public ICommand InstallCommand { get; }

    public ICommand StartNexusCommand { get; }

    public ICommand ToggleLogCommand { get; }
    public ICommand RelaunchAsAdminCommand { get; }
    public ICommand RetryInstallCommand { get; }
    public ICommand ClearStuckCommand { get; }
    public ICommand OpenLogsCommand { get; }
    public ICommand ExitInstallerCommand { get; }
    public ICommand CancelInstallCommand { get; }



    private void GoNext()

    {

        if (_currentStep == InstallerStep.Install)

        {

            return;

        }



        _currentStep = (InstallerStep)((int)_currentStep + 1);

        ErrorMessage = null;

        if (_currentStep == InstallerStep.Identity)
        {
            EnsureDefaultDeviceName();
            _ = RefreshIdentityCatalogAsync();
        }
        else if (_currentStep == InstallerStep.Security)
        {
            UpdateSecurityReadinessPreview();
        }

        NotifyStepChanged();

    }



    private void GoBack()

    {

        if (!CanGoBack)

        {

            return;

        }



        _currentStep = (InstallerStep)((int)_currentStep - 1);

        ErrorMessage = null;

        NotifyStepChanged();

    }



    private void BrowseFolder()

    {

        var dialog = new OpenFolderDialog

        {

            Title = "Vælg installationsmappe",

            InitialDirectory = Directory.Exists(InstallRoot) ? InstallRoot : @"C:\",

        };



        if (dialog.ShowDialog() == true && !string.IsNullOrWhiteSpace(dialog.FolderName))

        {

            InstallRoot = dialog.FolderName;
            _ = RefreshRequirementsAsync();

        }

    }



    private async Task RefreshRequirementsAsync()

    {

        Requirements.Clear();

        foreach (var check in await SystemRequirementsService.EvaluateAsync(InstallRoot))

        {

            Requirements.Add(check);

            _installerLog.Info($"Preflight: {check.Label} — {check.StatusDisplay} ({check.Detail})");

        }



        NotifyNavigation();

        Notify(nameof(HasAutoInstallPending));

    }



    private async Task InstallAsync()

    {

        if (SelectedOrganization is null)

        {

            ErrorMessage = "Vælg organisation.";

            return;

        }

        if (FreshPocResetConfirmed)
        {
            if (!NexusFreshPocResetDialog.Confirm(Application.Current.MainWindow))
            {
                return;
            }
        }

        IsInstalling = true;
        _installFailed = false;
        ErrorMessage = null;
        IsLogExpanded = true;
        ResetPhases();
        _installProgress.Reset();
        _elapsedTimer.Start();

        var progress = new Progress<string>(msg => _installerLog.Info(msg));

        try

        {

            await RunPhaseAsync(0, async () =>
            {
                await Task.Yield();
                if (!AdminElevationHelper.IsRunningElevated())
                {
                    throw new InvalidOperationException(
                        "Ninja Nexus Setup skal køre som administrator. Klik 'Genstart som administrator'.");
                }
                _installProgress.ReportInfo("Administrator-rettigheder bekræftet.");
            });

            await RunPhaseAsync(1, async () =>
            {
                var stop = await NexusProcessStopService.StopNexusProcessesAsync(InstallRoot, progress);
                _installProgress.ReportInfo(stop.Message);
                if (stop.Blocked)
                {
                    _installProgress.ReportStepBlocked(InstallPhases[1], stop.Message);
                    throw new InvalidOperationException(stop.Message);
                }
            });

            await RunPhaseAsync(2, async () =>
            {
                await Task.Yield();
                var locks = NexusInstallCleanupService.CheckFileLocks(InstallRoot);
                if (locks.IsLocked)
                {
                    var msg = $"Filer låst: {string.Join(", ", locks.LockedPaths)}";
                    _installProgress.ReportFileLock(msg);
                    _installProgress.ReportStepBlocked(InstallPhases[2], msg);
                    throw new InvalidOperationException(msg);
                }
                InstallPhases[2].Detail = "Ingen låste Nexus-filer";
            });

            await RunPhaseAsync(3, async () =>
            {
                await Task.Run(() =>
                {
                    if (FreshPocResetConfirmed)
                    {
                        var reset = NexusInstallCleanupService.FreshPocReset(InstallRoot, progress);
                        InstallPhases[3].Detail = reset.Message;
                    }
                    else
                    {
                        var repair = NexusInstallCleanupService.RepairBinaries(InstallRoot, progress);
                        InstallPhases[3].Detail = repair.Message;
                    }
                });
            });

            await RunPhaseAsync(4, async () =>
            {
                await Task.Yield();
                var dataDir = NexusIdentityDataPaths.ResolveDataDirectory(InstallRoot);
                var detail = FreshPocResetConfirmed
                    ? "Data nulstillet efter backup"
                    : $"Bevarer {dataDir}";
                InstallPhases[4].Detail = detail;
                _installProgress.ReportInfo(detail);
            });

            await RunPhaseAsync(5, async () =>
            {
                await Task.Run(() => NexusIdentityDataPaths.EnsureDataDirectories(InstallRoot));
                Directory.CreateDirectory(Path.Combine(InstallRoot, "logs"));
                InstallPhases[5].Detail = NexusIdentityDataPaths.ResolveDataDirectory(InstallRoot);
            });

            await RunPhaseAsync(6, async () =>
            {
                await RefreshRequirementsAsync();
                if (SystemRequirementsService.HasBlockingIssues(Requirements))
                {
                    throw new InvalidOperationException("Systemkrav blokerer installation.");
                }

                foreach (var kind in new[]
                {
                    PrerequisiteKind.DotNet8Desktop,
                    PrerequisiteKind.DotNet10Desktop,
                    PrerequisiteKind.WebView2,
                    PrerequisiteKind.VcRedist,
                })
                {
                    if (await _prerequisites.IsInstalledAsync(kind))
                    {
                        _installProgress.ReportInfo($"{kind} allerede installeret");
                        continue;
                    }

                    await _prerequisites.InstallIfMissingAsync(kind, progress);
                }

                await RefreshRequirementsAsync();
            });

            await RunPhaseAsync(7, async () =>
            {
                _installerLog.Info($"Udpakker til {InstallRoot}...");
                await Task.Run(() => PayloadExtractor.ExtractTo(InstallRoot));
                _installerLog.Info("Filer udpakket.");
            });

            await RunPhaseAsync(8, async () =>
            {
                await Task.Yield();
                var hash = NexusInstallCleanupService.ComputePayloadSha256(InstallRoot);
                var detail = $"SHA256 shell: {hash[..Math.Min(16, hash.Length)]}…";
                InstallPhases[8].Detail = detail;
                _installProgress.ReportInfo(detail);
            });

            await RunIdentityPhasesAsync();

            await RunPhaseAsync(17, async () =>
            {
                await Task.Run(() =>
                {
                    using var launcher = new NexusLaunchService(InstallRoot);
                    launcher.ValidateInstall();
                    launcher.CreateDesktopShortcut();
                });

                if (!await RuntimeChecker.HasDotNet8DesktopAsync())
                {
                    throw new InvalidOperationException(".NET 8 Desktop Runtime mangler efter installation.");
                }

                if (!PrerequisiteService.IsWebView2Installed())
                {
                    throw new InvalidOperationException("WebView2 Runtime mangler efter installation.");
                }

                var agentDir = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    "NinjaNexus", "agent");
                Directory.CreateDirectory(agentDir);
                _installerLog.Info("Validering OK.");
            });

            await RunPhaseAsync(18, async () =>
            {
                await Task.Yield();
                _installerLog.Info("Installation fuldført. Nexus er klar til manuel start.");
            });

            IsInstalled = true;
            _installerLog.Info("Ninja Nexus er installeret. Vælg Start Nexus, Åbn logs eller Afslut installer.");

            if (StartAfterInstall)
            {
                _ = await StartNexusAsync();
            }

        }

        catch (Exception ex)

        {

            _installFailed = true;
            ErrorMessage = ex.Message;

            _installerLog.Error(ex.Message);

            foreach (var phase in InstallPhases.Where(p => p.Status is InstallStepStatus.Running or InstallStepStatus.Retrying))
            {
                _installProgress.ReportStepFailed(phase, ex.Message);
            }

            NotifyInstallProgress();

            MessageBox.Show(

                ex.Message,

                "Ninja Nexus — installation fejlede",

                MessageBoxButton.OK,

                MessageBoxImage.Error);

        }

        finally

        {

            _elapsedTimer.Stop();
            IsInstalling = false;
            NotifyInstallProgress();
            Notify(nameof(ShowInstallFailedActions));
            Notify(nameof(CanInstall));
            Notify(nameof(ShowCompletionScreen));
            await RefreshRequirementsAsync();

        }

    }

    private async Task RunIdentityPhasesAsync()
    {
        var dbPath = NexusIdentityDataPaths.ResolveDatabasePath(InstallRoot);
        await RunPhaseAsync(9, async () =>
        {
            await Task.Yield();
            InstallPhases[9].Detail = dbPath;
            _installProgress.ReportInfo($"SQLite: {dbPath}");
        });

        await RunPhaseAsync(10, async () =>
        {
            using var launcher = new NexusLaunchService(InstallRoot);
            launcher.StopExisting();
            await launcher.StartIdentityAsync(new Progress<string>(msg =>
            {
                _installProgress.ReportInfo(msg);
                InstallPhases[10].Detail = msg;
            }));
        });

        await RunPhaseAsync(11, async () =>
        {
            using var api = new NexusIdentityApiClient();
            if (!await api.IsHealthyAsync())
            {
                throw new InvalidOperationException("Identity API health check fejlede.");
            }
            InstallPhases[11].Detail = "healthy";
        });

        await RunPhaseAsync(12, async () =>
        {
            await Task.Yield();
            const string detail = "Migrationer kørt ved API-opstart";
            InstallPhases[12].Detail = detail;
            _installProgress.ReportInfo(detail);
        });

        IReadOnlyList<OrganizationOption> orgs = OrganizationCatalog.All;
        await RunPhaseAsync(13, async () =>
        {
            using var api = new NexusIdentityApiClient();
            orgs = await api.GetOrganizationsAsync();
            if (orgs.Count == 0)
            {
                orgs = OrganizationCatalog.All;
            }

            Application.Current.Dispatcher.Invoke(() => ReplaceOrganizations(orgs));
            InstallPhases[13].Detail = $"{orgs.Count} organisation(er)";
        });

        IReadOnlyList<RoleOption> roles = RoleCatalog.All;
        await RunPhaseAsync(14, async () =>
        {
            using var api = new NexusIdentityApiClient();
            roles = await api.GetRolesAsync();
            if (roles.Count == 0)
            {
                roles = RoleCatalog.All;
            }

            Application.Current.Dispatcher.Invoke(() => ReplaceRoles(roles));
            InstallPhases[14].Detail = $"{roles.Count} rolle(r)";
        });

        var resolvedDevice = string.IsNullOrWhiteSpace(DeviceName)
            ? NexusIdentityBootstrapService.ResolveDefaultDeviceName()
            : DeviceName.Trim();
        DeviceName = resolvedDevice;

        await RunPhaseAsync(15, async () =>
        {
            await Task.Yield();
            InstallPhases[15].Detail = resolvedDevice;
        });

        await RunPhaseAsync(16, async () =>
        {
            await Task.Yield();
            var deviceId = NexusIdentitySeedService.Seed(
                SelectedOrganization!,
                UserEmail,
                resolvedDevice,
                SelectedRole?.Name ?? "User",
                ReadShellVersion(),
                "1.0.0");
            GeneratedDeviceId = deviceId;
            InstallPhases[16].Detail = deviceId;
            _installProgress.ReportInfo($"Enrollment gemt — {deviceId}");
        });
    }

    private void ApplyBootstrapInstallPhase(IdentityBootstrapStatus status)
    {
        var index = status.Phase switch
        {
            IdentityBootstrapPhase.CreateFolders => 9,
            IdentityBootstrapPhase.StartApi => 10,
            IdentityBootstrapPhase.WaitHealth => 11,
            IdentityBootstrapPhase.Migrations => 12,
            IdentityBootstrapPhase.LoadOrganizations => 13,
            IdentityBootstrapPhase.LoadRoles => 14,
            IdentityBootstrapPhase.DeviceName => 15,
            IdentityBootstrapPhase.Enrollment => 16,
            _ => (int?)null,
        };

        if (index is null || index >= InstallPhases.Count)
        {
            return;
        }

        var step = InstallPhases[index.Value];
        step.Detail = status.Detail;
        if (status.State == IdentityReadinessState.Failed)
        {
            step.Status = InstallStepStatus.Failed;
        }
    }

    private void RelaunchAsAdmin()
    {
        var args = Environment.GetCommandLineArgs().Skip(1).ToArray();
        if (AdminElevationHelper.TryRelaunchElevated(args))
        {
            Application.Current.Shutdown(0);
        }
        else
        {
            MessageBox.Show(
                "Kunne ikke genstarte som administrator. Højreklik NexusSetup.exe → Kør som administrator.",
                "Administrator påkrævet",
                MessageBoxButton.OK,
                MessageBoxImage.Warning);
        }
    }

    private void ClearStuckState()
    {
        _installProgress.Reset();
        foreach (var phase in InstallPhases)
        {
            phase.Status = InstallStepStatus.Pending;
            phase.Detail = null;
        }

        NexusInstallCleanupService.RepairBinaries(InstallRoot, new Progress<string>(_installerLog.Info));
        _installProgress.ReportInfo("Stuck state ryddet — repair cleanup kørt.");
        NotifyInstallProgress();
    }

    private void OpenLogsFolder()
    {
        Directory.CreateDirectory(LogsFolderPath);
        Process.Start(new ProcessStartInfo
        {
            FileName = LogsFolderPath,
            UseShellExecute = true,
        });
    }

    private void CancelInstall()
    {
        if (!IsInstalling)
        {
            return;
        }

        _installFailed = true;
        ErrorMessage = "Installation annulleret af bruger.";
        IsInstalling = false;
        _elapsedTimer.Stop();
        NotifyInstallProgress();
    }



    private async Task RunPrerequisitePhaseAsync(int index, PrerequisiteKind kind)

    {

        if (await _prerequisites.IsInstalledAsync(kind))

        {

            _installProgress.ReportStepSkipped(InstallPhases[index], $"{kind} allerede installeret");
            await RefreshRequirementsAsync();
            return;

        }



        SetRequirementInstalling(kind);
        var step = InstallPhases[index];
        _installProgress.ReportStepStarted(step);

        try

        {

            var progress = new Progress<string>(msg =>
            {
                _installerLog.Info(msg);
                if (msg.Contains("låst", StringComparison.OrdinalIgnoreCase) ||
                    msg.Contains("locked", StringComparison.OrdinalIgnoreCase))
                {
                    _installProgress.ReportFileLock(msg);
                }
            });

            await _prerequisites.InstallIfMissingAsync(kind, progress);
            _installProgress.ReportStepCompleted(step);

        }

        catch (Exception ex)

        {

            _installProgress.ReportStepFailed(step, ex.Message);
            throw;

        }

        finally

        {

            await RefreshRequirementsAsync();

        }

    }



    private void SetRequirementInstalling(PrerequisiteKind kind)

    {

        var check = Requirements.FirstOrDefault(r => r.Kind == kind);

        if (check is not null)

        {

            check.Status = RequirementStatus.Installing;

            Notify(nameof(Requirements));

        }

    }



    private async Task<bool> StartNexusAsync()

    {

        IsInstalling = true;

        try

        {

            if (!await RuntimeChecker.HasDotNet8DesktopAsync() || !PrerequisiteService.IsWebView2Installed())

            {

                throw new InvalidOperationException(

                    "Forudsætninger mangler. Kør installationen igen for at installere runtime-komponenter.");

            }



            using var launcher = new NexusLaunchService(InstallRoot);

            launcher.ValidateInstall();

            launcher.StopExisting();



            var progress = new Progress<string>(msg => _installerLog.Info(msg));

            await launcher.StartIdentityAsync(progress);

            _installerLog.Info("Starter Nexus Shell...");

            launcher.StartShell();

            _installerLog.Info("Nexus Shell startet. Log ind med user@nexus.local / NexusUser1!");
            return true;

        }

        catch (Exception ex)

        {

            ErrorMessage = ex.Message;

            _installerLog.Error(ex.Message);

            MessageBox.Show(ex.Message, "Kunne ikke starte Nexus", MessageBoxButton.OK, MessageBoxImage.Error);
            return false;

        }

        finally

        {

            IsInstalling = false;
            Notify(nameof(ShowCompletionScreen));

        }

    }



    private async Task RunPhaseAsync(int index, Func<Task> work)

    {

        var step = InstallPhases[index];
        _installProgress.ReportStepStarted(step);

        try

        {

            await work().WaitAsync(InstallProgressCoordinator.StepTimeout);
            _installProgress.ReportStepCompleted(step);

        }

        catch (TimeoutException)

        {

            _installProgress.ReportTimeout(step);
            throw new TimeoutException($"Installation trin '{step.Label}' overskred tidsgrænsen.");

        }

        catch (Exception ex)

        {

            _installProgress.ReportStepFailed(step, ex.Message);
            throw;

        }

    }



    private void ResetPhases()

    {

        foreach (var phase in InstallPhases)

        {

            phase.Status = InstallStepStatus.Pending;
            phase.Detail = null;
            phase.StartedAt = null;

        }

        NotifyInstallProgress();

    }



    private void NotifyInstallProgress()

    {

        Notify(nameof(InstallProgressPercent));
        Notify(nameof(InstallProgressLabel));
        Notify(nameof(CurrentInstallAction));
        Notify(nameof(InstallStatusPill));
        Notify(nameof(IsInstallHung));
        Notify(nameof(ShowInstallProgressHeader));
        Notify(nameof(RecentInstallEvents));
        Notify(nameof(InstallPhases));

    }



    private void NotifyRunningStepElapsed()

    {

        var running = InstallStepStatusRules.FindRunning(InstallPhases);
        running?.RefreshElapsedDisplay();
    }



    private string ReadShellVersion()

    {

        var versionFile = Path.Combine(InstallRoot, "VERSION.txt");

        if (File.Exists(versionFile))

        {

            return File.ReadAllText(versionFile).Trim();

        }



        return "0.1.0-identity2";

    }



    private void NotifyStepChanged()

    {

        Notify(nameof(CurrentStepNumber));

        Notify(nameof(StepTitle));

        Notify(nameof(StepSubtitle));

        Notify(nameof(IsWelcomeStep));

        Notify(nameof(IsRequirementsStep));

        Notify(nameof(IsPathStep));

        Notify(nameof(IsIdentityStep));

        Notify(nameof(IsSecurityStep));

        Notify(nameof(IsInstallStep));

        Notify(nameof(IsStep1Active));
        Notify(nameof(IsStep2Active));
        Notify(nameof(IsStep3Active));
        Notify(nameof(IsStep4Active));
        Notify(nameof(IsStep5Active));
        Notify(nameof(IsStep6Active));

        NotifyNavigation();

    }



    private void NotifyNavigation()

    {

        Notify(nameof(CanGoBack));

        Notify(nameof(CanGoNext));

        Notify(nameof(PrimaryButtonLabel));

        Notify(nameof(HasAutoInstallPending));

        Notify(nameof(ShowPrimaryOnInstallStep));
        Notify(nameof(ShowOrganizationStatus));
        Notify(nameof(OrganizationStatusMessage));
        Notify(nameof(AreIdentityInputsEnabled));
        Notify(nameof(InstallPathValidationMessage));
        Notify(nameof(ShowInstallPathValidation));
        Notify(nameof(CanInstall));

        CommandManager.InvalidateRequerySuggested();

    }

    private void UpdateInstallPathValidation()
    {
        InstallPathValidationMessage = IsInstallRootValid(out var message)
            ? string.Empty
            : message;
    }

    private bool IsInstallRootValid() => IsInstallRootValid(out _);

    private bool IsInstallRootValid(out string message)
    {
        message = string.Empty;

        if (string.IsNullOrWhiteSpace(InstallRoot))
        {
            message = "Installationssti må ikke være tom.";
            return false;
        }

        string fullPath;
        try
        {
            fullPath = Path.GetFullPath(InstallRoot.Trim());
        }
        catch (Exception ex)
        {
            message = $"Installationssti er ugyldig: {ex.Message}";
            return false;
        }

        var normalized = fullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
        var driveRoot = (Path.GetPathRoot(fullPath) ?? string.Empty)
            .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

        if (string.Equals(normalized, driveRoot, StringComparison.OrdinalIgnoreCase))
        {
            message = "Vælg en mappe under drevet, ikke selve drevroden.";
            return false;
        }

        var windows = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
        var system = Path.Combine(windows, "System32");
        var programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
        var programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);

        if (IsSameOrInside(fullPath, windows)
            || IsSameOrInside(fullPath, system)
            || IsSameOrInside(fullPath, programFiles)
            || IsSameOrInside(fullPath, programFilesX86))
        {
            message = "Vælg en Nexus POC-mappe uden for Windows/System/Program Files.";
            return false;
        }

        try
        {
            Directory.CreateDirectory(fullPath);
            var probe = Path.Combine(fullPath, $".nexus-write-{Guid.NewGuid():N}.tmp");
            File.WriteAllText(probe, "ok");
            File.Delete(probe);
            return true;
        }
        catch (Exception ex)
        {
            message = $"Installationsmappen kan ikke skrives til: {ex.Message}";
            return false;
        }
    }

    private static bool IsSameOrInside(string candidate, string root)
    {
        if (string.IsNullOrWhiteSpace(root))
        {
            return false;
        }

        var fullCandidate = Path.GetFullPath(candidate)
            .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
        var fullRoot = Path.GetFullPath(root)
            .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

        return string.Equals(fullCandidate, fullRoot, StringComparison.OrdinalIgnoreCase)
            || fullCandidate.StartsWith(fullRoot + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase);
    }

    private void EnsureDefaultDeviceName()
    {
        if (string.IsNullOrWhiteSpace(DeviceName))
        {
            DeviceName = NexusIdentityBootstrapService.ResolveDefaultDeviceName();
        }
    }

    private void UpdateSecurityReadinessPreview()
    {
        SetReadiness("device", IdentityReadinessState.Ready, DeviceName);
        SetReadiness("orgs", IdentityReadinessState.Ready,
            SelectedOrganization is null ? "Afventer valg" : SelectedOrganization.Name);
        SetReadiness("roles", IdentityReadinessState.Ready, SelectedRole?.Name ?? "User");
        SetReadiness("sqlite", IdentityReadinessState.Pending,
            NexusIdentityDataPaths.ResolveDataDirectory(InstallRoot));
        SetReadiness("api", IdentityReadinessState.Pending, "Startes ved installation");
        SetReadiness("enrollment", IdentityReadinessState.Pending, "Gemmes ved installation");
    }

    private async Task RefreshIdentityCatalogAsync()
    {
        EnsureDefaultDeviceName();
        SetReadiness("device", IdentityReadinessState.Ready, DeviceName);

        var identityExe = Path.Combine(InstallRoot, "identity", "NexusIdentity.Api.exe");
        if (!File.Exists(identityExe))
        {
            SetReadiness("api", IdentityReadinessState.Pending, "Filer udpakkes ved installation");
            SetReadiness("sqlite", IdentityReadinessState.Pending, "Oprettes ved installation");
            SetReadiness("orgs", IdentityReadinessState.Ready, $"{Organizations.Count} fra katalog");
            SetReadiness("roles", IdentityReadinessState.Ready, $"{Roles.Count} fra katalog");
            return;
        }

        Notify(nameof(AreIdentityInputsEnabled));

        try
        {
            using var bootstrap = new NexusIdentityBootstrapService();
            using var launcher = new NexusLaunchService(InstallRoot);
            launcher.StopExisting();
            await launcher.StartIdentityAsync(new Progress<string>(msg => _installerLog.Info(msg)));

            using var api = new NexusIdentityApiClient();
            var orgs = await api.GetOrganizationsAsync();
            if (orgs.Count > 0)
            {
                ReplaceOrganizations(orgs);
            }

            var roles = await api.GetRolesAsync();
            if (roles.Count > 0)
            {
                ReplaceRoles(roles);
            }

            SetReadiness("api", IdentityReadinessState.Ready);
            SetReadiness("sqlite", IdentityReadinessState.Ready,
                NexusIdentityDataPaths.ResolveDatabasePath(InstallRoot));
            SetReadiness("orgs", IdentityReadinessState.Ready, $"{Organizations.Count} indlæst");
            SetReadiness("roles", IdentityReadinessState.Ready, $"{Roles.Count} indlæst");
        }
        catch (Exception ex)
        {
            _installerLog.Error($"Identity preflight: {ex.Message}");
            SetReadiness("api", IdentityReadinessState.Failed, ex.Message);
            SetReadiness("orgs", IdentityReadinessState.Ready, $"{Organizations.Count} fra katalog");
            SetReadiness("roles", IdentityReadinessState.Ready, $"{Roles.Count} fra katalog");
        }
        finally
        {
            Notify(nameof(AreIdentityInputsEnabled));
        }
    }

    private void ReplaceOrganizations(IReadOnlyList<OrganizationOption> orgs)
    {
        var selectedId = SelectedOrganization?.Id;
        Organizations.Clear();
        foreach (var org in orgs)
        {
            Organizations.Add(org);
        }

        SelectedOrganization = selectedId is null
            ? (Organizations.Count == 1 ? Organizations[0] : Organizations.FirstOrDefault())
            : Organizations.FirstOrDefault(o => o.Id == selectedId) ?? Organizations.FirstOrDefault();
    }

    private void ReplaceRoles(IReadOnlyList<RoleOption> roles)
    {
        var selectedName = SelectedRole?.Name;
        Roles.Clear();
        foreach (var role in roles)
        {
            Roles.Add(role);
        }

        SelectedRole = selectedName is null
            ? (Roles.Count == 1 ? Roles[0] : Roles.FirstOrDefault(r => r.Name == "User") ?? Roles.FirstOrDefault())
            : Roles.FirstOrDefault(r => r.Name == selectedName) ?? Roles.FirstOrDefault();
    }

    private void ApplyBootstrapStatus(IdentityBootstrapStatus status)
    {
        var key = status.Phase switch
        {
            IdentityBootstrapPhase.CreateFolders or IdentityBootstrapPhase.Migrations => "sqlite",
            IdentityBootstrapPhase.ValidateConfig => "sqlite",
            IdentityBootstrapPhase.StartApi or IdentityBootstrapPhase.WaitHealth => "api",
            IdentityBootstrapPhase.LoadOrganizations => "orgs",
            IdentityBootstrapPhase.LoadRoles => "roles",
            IdentityBootstrapPhase.DeviceName => "device",
            IdentityBootstrapPhase.Enrollment => "enrollment",
            _ => null,
        };

        if (key is null)
        {
            return;
        }

        SetReadiness(key, status.State, status.Detail);
    }

    private void SetReadiness(string key, IdentityReadinessState state, string? detail = null)
    {
        var item = ReadinessItems.FirstOrDefault(i => i.Key == key);
        if (item is null)
        {
            return;
        }

        item.State = state;
        item.Detail = detail;
    }



    private void Notify([CallerMemberName] string? name = null) =>

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));



    public async Task PrepareEvidenceStepAsync(InstallerStep step)

    {

        _currentStep = step;

        if (step == InstallerStep.Requirements)

        {

            await RefreshRequirementsAsync();

        }

        NotifyStepChanged();

    }



    public void PrepareEvidenceInstalling()

    {

        _currentStep = InstallerStep.Install;

        _isInstalling = false;

        for (var i = 0; i < InstallPhases.Count; i++)

        {

            InstallPhases[i].Status = i switch
            {
                < 7 => InstallStepStatus.Completed,
                7 => InstallStepStatus.Running,
                _ => InstallStepStatus.Pending,
            };
        }

        IsLogExpanded = true;
        LogText += $"[{DateTime.Now:HH:mm:ss}] Udpakker til {InstallRoot}...{Environment.NewLine}";
        Notify(nameof(IsInstalling));
        Notify(nameof(IsLogExpanded));
        NotifyInstallProgress();
        NotifyStepChanged();
    }

    public void PrepareEvidenceComplete()
    {
        _currentStep = InstallerStep.Install;
        _isInstalled = true;
        _isInstalling = false;

        foreach (var phase in InstallPhases)
        {
            phase.Status = InstallStepStatus.Completed;
        }

        GeneratedDeviceId = "DEV-POC-EVIDENCE";
        IsLogExpanded = true;
        LogText += $"[{DateTime.Now:HH:mm:ss}] Installation fuldført.{Environment.NewLine}";
        Notify(nameof(IsInstalled));
        Notify(nameof(IsInstalling));
        Notify(nameof(IsLogExpanded));
        NotifyInstallProgress();
        Notify(nameof(GeneratedDeviceId));
        NotifyStepChanged();
    }

}



internal sealed class RelayCommand : ICommand

{

    private readonly Action<object?> _execute;

    private readonly Predicate<object?>? _canExecute;



    public RelayCommand(Action<object?> execute, Predicate<object?>? canExecute = null)

    {

        _execute = execute;

        _canExecute = canExecute;

    }



    public event EventHandler? CanExecuteChanged

    {

        add => CommandManager.RequerySuggested += value;

        remove => CommandManager.RequerySuggested -= value;

    }



    public bool CanExecute(object? parameter) => _canExecute?.Invoke(parameter) ?? true;

    public void Execute(object? parameter) => _execute(parameter);

}


