#!/bin/pwsh
using namespace System.Management.Automation.Host;
using namespace System.Security.Principal;

. "$PSScriptRoot/../Scripts/Prerequisites.ps1";

. "$PSScriptRoot/Manage.ps1";
. "$PSScriptRoot/User/Install.ps1";
. "$PSScriptRoot/../Scripts/Security.ps1";
. "$PSScriptRoot/../Software/Firefox/Install.ps1";
. "$PSScriptRoot/../../Common/Scripts/Context.ps1";
. "$PSScriptRoot/../../Common/Types/OneShotTask.ps1";

$null = New-Module {
    . "$PSScriptRoot/../Scripts/Hooks.ps1";
    . "$PSScriptRoot/../Scripts/PowerManagement.ps1";
    . "$PSScriptRoot/../Scripts/Registry.ps1";
    . "$PSScriptRoot/../Scripts/Security.ps1";
    . "$PSScriptRoot/../Scripts/Update.ps1";
    . "$PSScriptRoot/../Scripts/Users.ps1";
    . "$PSScriptRoot/../../Common/Scripts/Config.ps1";
    . "$PSScriptRoot/../../Common/Scripts/Operations.ps1";
    . "$PSScriptRoot/../../Common/Scripts/Software.ps1";
    . "$PSScriptRoot/../../Common/Scripts/SoftwareManagement.ps1";
    . "$PSScriptRoot/../../Common/Types/InstallerAction.ps1";
    . "$PSScriptRoot/../../Common/Types/OneShotTask.ps1";

    <#
        .SYNOPSIS
        Finishes the installation of a running Windows machine.
    #>
    function Start-WindowsInstallation {
        Start-Operation {
            Start-InstallationLoop;
        };
    }

    <#
        .SYNOPSIS
        Starts the installation loop.
    #>
    function Start-InstallationLoop {
        $wslLocation = "$env:ProgramData\PortValhalla\Ubuntu";

        while (-not (Get-IsFinished)) {
            switch (Get-Stage) {
                ($null) {
                    Set-Stage ([SetupStage]::Initialize);
                    break;
                }
                ([SetupStage]::Initialize) {
                    if (-not ((Test-Command "choco") -and (Test-Command "refreshenv"))) {
                        Invoke-Hook "Install-Chocolatey" -Fallback {
                            # Install chocolatey
                            New-Item -Force $PROFILE;
                            [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
                            Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'));
                            Import-Module $env:ChocolateyInstall/helpers/chocolateyProfile.psm1;
                            refreshenv;
                        };

                        continue;
                    }


                    if (-not (Test-ChocoPackage "powershell-core")) {
                        Invoke-Hook "Install-PowerShellCore" -Fallback {
                            choco install -y powershell-core --install-arguments='"ADD_FILE_CONTEXT_MENU_RUNPOWERSHELL=1 ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL=1 REGISTER_MANIFEST=1 USER_MU=1 ENABLE_MU=1"';
                        };

                        Restart-Intermediate;
                        return;
                    }

                    if ($env:PWSH_PATH -and (Test-Path $env:PWSH_PATH)) {
                        attrib "-R" "$env:PWSH_PATH\*" /S /D;
                        Remove-Item -Recurse -Force $env:PWSH_PATH;
                        continue;
                    }

                    if ($env:DEBUG) {
                        if (
                            (Get-Item $env:INSTALLER_SCRIPT).IsReadOnly -and
                            (Test-Qemu) -and
                            ($Host.UI.PromptForChoice(
                                    "Confirm",
                                    "Do you wish to swap to live scripts?",
                                    [ChoiceDescription[]]@(
                                        [ChoiceDescription]::new("&No", "Use scripts stored in the virtual machine"),
                                        [ChoiceDescription]::new("&Yes", "Use live scripts stored on the host")),
                                    0) -eq 1)) {
                            Install-ChocoPackage winfsp qemu-guest-agent;
                            Get-Service VirtioFsSvc | Start-Service -PassThru | Set-Service -StartupType Automatic;

                            while (-not (Test-Path Z:\)) {
                                Start-Sleep 0.1;
                            }

                            foreach ($name in @("CONFIG_MODULE", "INSTALLER_SCRIPT")) {
                                $variable = Get-Item "Env:\$name";

                                $path = Join-Path `
                                    "Z:\Repositories\PortValhalla" `
                                    ([System.IO.Path]::GetRelativePath("$PSScriptRoot/../../..", $variable.Value));

                                Set-Item "Env:\$name" $path;
                                Write-Host "The new value of ``$name`` is ``$path``";
                            }

                            Restart-Intermediate;
                            exit;
                        }
                    }

                    if (-not (Test-Command "gsudo")) {
                        Install-ChocoPackage gsudo;
                        refreshenv;
                        continue;
                    }

                    if ($env:DEBUG) {
                        & {
                            $sys32 = "$env:WINDIR/System32";
                            $osk = (Get-Item "$sys32/osk.exe").FullName;
                            $cmd = (Get-Item "$sys32/cmd.exe").FullName;
                            $tmpOsk = New-TemporaryFile;
                            $tmpCmd = New-TemporaryFile;
                            gsudo -d copy "$osk" "$tmpOsk";
                            gsudo -d copy "$cmd" "$tmpCmd";

                            if ((Get-FileHash $tmpOsk).Hash -ne (Get-FileHash $tmpCmd).Hash) {
                                Set-MpPreference -ExclusionPath $osk;
                                gsudo -d --ti move $osk "${osk}_";
                                gsudo -d -s copy $cmd $osk;
                                continue;
                            }
                        };
                    }

                    if (-not (Test-Winget)) {
                        . "$PSScriptRoot/../Software/winget/Manage.ps1";
                        continue;
                    }

                    if (-not (Test-Command "git")) {
                        Install-WingetPackage Git.Git;
                        refreshenv;
                        continue;
                    }

                    if (-not (& { $null = wsl --status; $?; })) {
                        wsl --install --no-launch;
                        Restart-Intermediate;
                        return;
                    }

                    if (-not (& { $null = wsl -l; $?; })) {
                        $wslRoot = Split-Path -Parent $wslLocation;

                        if (-not (Test-Path $wslRoot)) {
                            $null = New-Item -ItemType Directory $wslRoot;
                        }

                        Copy-Item -Recurse -Force (Get-AppxPackage "*Ubuntu*").InstallLocation $wslLocation;
                        Set-UserPermissions $wslLocation;
                        & "$wslLocation\ubuntu.exe" install --root;
                        continue;
                    }

                    if (-not (wsl --shell-type login type -t nix)) {
                        wsl -- sh `<`(curl -L https://nixos.org/nix/install`) --daemon --yes;
                        wsl --shutdown;
                        continue;
                    }

                    if (-not (Test-PSPackage Selenium.WebDriver)) {
                        Write-Host "Installing browser automation tools…";
                        Install-Module -AcceptLicense -Force NuGet;
                        Import-Module NuGet;
                        $null = Install-Package -Force Selenium.WebDriver -RequiredVersion 4.10.0 -SkipDependencies;
                        continue;
                    }

                    Install-ChocoPackage selenium-gecko-driver firefox;

                    Invoke-Hook "Install-PSModules" -Fallback {
                        Install-Module -Scope AllUsers -AcceptLicense -Force KnownFolders;
                        Install-Module -Scope AllUsers -AcceptLicense -Force PSWindowsUpdate;
                        Install-Module -Scope AllUsers -AcceptLicense -Force PSScriptAnalyzer;
                        Install-Module -Scope AllUsers -AcceptLicense -AllowPrerelease -AllowClobber -Force LocalAccounts;
                        Import-Module LocalAccounts;
                        . "$PSScriptRoot/../Software/PinnedItem/Manage.ps1";
                    };

                    Install-WingetPackage AutoHotkey.AutoHotkey;
                    Set-Stage ([SetupStage]::Configure);
                    break;
                }
                default {
                    if (-not (& { $null = wsl -l; $? })) {
                        wsl --import-in-place "PortValhalla" "$wslLocation/ext4.vhdx";
                        wsl --set-default "PortValhalla";
                    }

                    switch ($_) {
                        ([SetupStage]::OneShot) {
                            Write-Host "Running OneShot task ``$(Get-OneShotTask)``";

                            Start-OneShot {
                                switch (Get-OneShotTask) {
                                    ([OneShotTask]::InitializeMSAccount) {
                                        Initialize-UserCreation;
                                        Register-Setup -DefaultUser;
                                    }
                                    ([OneShotTask]::DisableUAC) {
                                        Disable-UAC;
                                        Register-Setup;
                                    }
                                }
                            };

                            break;
                        }
                        default {
                            if (Test-Admin) {
                                $null = Import-Module PSWindowsUpdate;

                                Invoke-Hook "Invoke-WindowsUpdate" -Fallback {
                                    Update-WindowsInstallation;
                                };

                                if ((Get-WURebootStatus -Silent)) {
                                    Restart-Intermediate;
                                    return;
                                }
                            }

                            <#
                                .SYNOPSIS
                                Deploys an action for each software.

                                .PARAMETER Action
                                The action to execute by default.
                            #>
                            function Deploy-SoftwareAction {
                                param(
                                    [Nullable[InstallerAction]] $Action = $null
                                )

                                [bool] $install = $false;
                                $arguments = [hashtable]@{ };

                                if ($null -ne $Action) {
                                    $install = ($Action -eq ([InstallerAction]::Install));
                                    $null = $arguments.Add("action", $Action);
                                } else {
                                    $install = $true;
                                }

                                # Drivers
                                & {
                                    $driverPath = "$PSScriptRoot/../Drivers";
                                    $mbPath = "$driverPath/ROG Zenith Extreme Alpha";

                                    if ($install) {
                                        if (Get-Config "valhalla.hardware.elgatoWave") {
                                            if (-not (Test-ChocoPackage wavelink)) {
                                                Install-ChocoPackage wavelink -ArgumentList '--install-arguments="/norestart"';
                                                Remove-DesktopIcon "*Wave Link*";
                                                Restart-Intermediate;
                                                exit;
                                            }
                                        }
                                    }

                                    foreach ($component in (Get-Config "valhalla.hardware.components")) {
                                        switch ($component) {
                                            ("ROG Zenith Extreme Alpha") {
                                                & "$mbPath/MarvellEthernet/Manage.ps1" @arguments;
                                                & "$mbPath/IntelWiFi/Manage.ps1" @arguments;
                                                & "$mbPath/IntelBluetooth/Manage.ps1" @arguments;
                                                & "$mbPath/AMDChipsetX399/Manage.ps1" @arguments;
                                                & "$driverPath/AMDChipsetX399/Manage.ps1" @arguments;
                                            }
                                            ("Predator Z301C") {
                                                & "$driverPath/Predator Z301C/Manage.ps1" @arguments;
                                            }
                                        }
                                    }

                                    if ($install) {
                                        if (Get-Config "valhalla.hardware.amdCPU") {
                                            Install-ChocoPackage amd-ryzen-master;
                                            # ToDo: backup Ryzen energy saving plan
                                        }

                                        if (Get-Config "valhalla.hardware.nvidiaGPU") {
                                            Install-ChocoPackage geforce-game-ready-driver;
                                            Remove-DesktopIcon "*Geforce*";
                                        }

                                        if (Get-Config "valhalla.hardware.corsairDevice") {
                                            Install-ChocoPackage icue;
                                        }
                                    }

                                    if (Get-Config "valhalla.hardware.eyeX") {
                                        & "$driverPath/Tobii EyeX/Manage.ps1" @arguments;
                                    }
                                };

                                & {
                                    # Windows Config
                                    $softwarePath = "$PSScriptRoot/../Software";
                                    $commonSoftware = "$PSScriptRoot/../../Common/Software";
                                    & "$softwarePath/Windows/Manage.ps1" @arguments;

                                    if (Get-Config "valhalla.hardware.logitechG") {
                                        & "$softwarePath/LGHub/Manage.ps1" @arguments;
                                    }

                                    if (Test-Collection "essential") {
                                        # Essentials
                                        & "$softwarePath/aliae/Manage.ps1" @arguments;
                                        & "$softwarePath/git/Manage.ps1" @arguments;
                                        & "$softwarePath/OpenSSH/Manage.ps1" @arguments;
                                        & "$softwarePath/PowerShell/Manage.ps1" @arguments;
                                        & "$softwarePath/chocolatey/Manage.ps1" @arguments;
                                        & "$softwarePath/zoxide/Manage.ps1" @arguments;
                                        & "$commonSoftware/posh-git/Manage.ps1" @arguments;
                                        & "$commonSoftware/Terminal-Icons/Manage.ps1" @arguments;
                                        & "$softwarePath/Oh My Posh/Manage.ps1" @arguments;

                                        if (Get-Config "valhalla.windows.dualboot") {
                                            & "$softwarePath/Ext4Fsd/Main.ps1" @arguments;
                                        }

                                        if ($install) {
                                            Install-ChocoPackage `
                                                procexp `
                                                procmon `
                                                ;

                                            Install-WingetPackage `
                                                KDE.KDEConnect `
                                                ;
                                        }
                                    }

                                    if (Test-Collection "common") {
                                        # Common Software
                                        & "$softwarePath/WinSCP/Manage.ps1" @arguments;
                                        & "$softwarePath/Thunderbird/Manage.ps1" @arguments;
                                        & "$softwarePath/PuTTY/Manage.ps1" @arguments;

                                        if ($install) {
                                            Install-ChocoPackage `
                                                7zip `
                                                chocolateygui `
                                                DefaultProgramsEditor `
                                                bitwarden `
                                                keepass `
                                                ;

                                            Install-WingetPackage `
                                                SomePythonThings.WingetUIStore `
                                                ;

                                            Remove-DesktopIcon "UniGetUI*";
                                        }
                                    }

                                    if (Test-Collection "desktopExperience") {
                                        if ($install) {
                                            # Fonts
                                            Install-ChocoPackage nerd-fonts-CascadiaCode;

                                            # Internet Access
                                            Install-WingetPackage Brave.Brave kamranahmedse.pennywise;
                                            Remove-DesktopIcon "*Brave*";
                                            Remove-TaskbarItem "*Brave*";
                                            Remove-DesktopIcon "Pennywise*";

                                            # Tools
                                            Install-SetupPackage -Source "https://github.com/mRemoteNG/mRemoteNG/releases/download/2023.03.03-v1.77.3-nb/mRemoteNG-Installer-1.77.3.nb-1784.msi" -ArgumentList "/Quiet";
                                            Remove-DesktopIcon "mRemoteNG*";

                                            Install-ChocoPackage `
                                                gimp `
                                                gpu-z `
                                                windirstat `
                                                winmerge `
                                                handbrake `
                                                hwmonitor `
                                                qbittorrent `
                                                imgburn `
                                                inkscape `
                                                krita `
                                                MetaX `
                                                obs-studio `
                                                ;

                                            Remove-DesktopIcon "GIMP*";
                                            Remove-DesktopIcon "GPU-Z*";
                                            Remove-DesktopIcon "WinDirStat*";
                                            Remove-DesktopIcon "*HWMonitor*";
                                            Remove-DesktopIcon "ImgBurn*";
                                            Remove-DesktopIcon "InkScape*";
                                            Remove-DesktopIcon "Krita*";
                                            Remove-DesktopIcon "MetaX*";
                                            Remove-DesktopIcon "OBS Studio*";

                                            Install-WingetPackage `
                                                AntSoftware.AntRenamer `
                                                AppWork.JDownloader;

                                            Remove-DesktopIcon "JDownloader*";
                                        }

                                        # ToDo: Consider hiding behind own config?
                                        & "$softwarePath/Ubiquiti UniFi Controller/Manage.ps1" @arguments;

                                        # Internet Access
                                        & "$softwarePath/Firefox/Manage.ps1" @arguments;
                                        & "$softwarePath/MSEdgeRedirect/Manage.ps1" @arguments;

                                        if (Test-Collection "fileSync") {
                                            & "$softwarePath/Nextcloud/Main.ps1" @arguments;
                                        }
                                    }

                                    if (Test-Collection "socialMedia") {
                                        if ($install) {
                                            Install-ChocoPackage `
                                                signal `
                                                threema-desktop `
                                                element-desktop `
                                                teamspeak `
                                                ;

                                            Remove-DesktopIcon "*Element*";
                                            Remove-DesktopIcon "*TeamSpeak*";

                                            Install-WingetPackage Discord.Discord;
                                            Remove-DesktopIcon "*Discord*";
                                        }
                                    }

                                    if (Test-Collection "media") {
                                        if ($install) {
                                            Install-ChocoPackage `
                                                k-litecodecpackmega `
                                                vlc `
                                                ;

                                            Remove-DesktopIcon "VLC*";
                                            Install-ChocoPackage jellyfin-media-player -ArgumentList "--install-args","/norestart";
                                            Remove-DesktopIcon "Jellyfin Media Player*";
                                            Install-WingetPackage Ytmdesktop.Ytmdesktop;
                                            Remove-DesktopIcon "Youtube Music*";
                                        }
                                    }

                                    if (Test-Collection "coding") {
                                        if ($install) {
                                            Install-ChocoPackage `
                                                gh `
                                                github-desktop `
                                                ida-free `
                                                HxD `
                                                docker-desktop `
                                                imhex `
                                                dotpeek `
                                                ;

                                            Remove-DesktopIcon "IDA *";
                                            Remove-DesktopIcon "GitHub*";
                                            Remove-DesktopIcon "Docker*";
                                        }

                                        & "$softwarePath/vscode/Main.ps1" @arguments;
                                        & "$softwarePath/VisualStudio/Manage.ps1" @arguments;

                                        # Node.js
                                        & "$softwarePath/NVS/Manage.ps1" @arguments;
                                    }

                                    if (Test-Collection "gaming") {
                                        # Gaming
                                        if ($install) {
                                            Install-ChocoPackage `
                                                goggalaxy `
                                                epicgameslauncher `
                                                rayman-controlpanel `
                                                ppsspp `
                                                ;

                                            Remove-DesktopIcon "*Epic Games*";
                                            Remove-DesktopIcon "*PPSSPP *-Bit*";

                                            Install-ChocoPackage `
                                                steam `
                                                ubisoft-connect `
                                                -ArgumentList "--ignore-checksums" `
                                                ;

                                            Remove-DesktopIcon "*Steam*";
                                            Remove-DesktopIcon "*Ubisoft Connect*";

                                            Install-WingetPackage ElectronicArts.EADesktop;
                                            Remove-DesktopIcon "EA.*";
                                        }

                                        & "$softwarePath/TrackMania Nations Forever/Manage.ps1" @arguments;
                                        & "$softwarePath/TrackMania United Forever/Manage.ps1" @arguments;
                                        & "$softwarePath/ManiaPlanet/Manage.ps1" @arguments;
                                        & "$softwarePath/osu!/Manage.ps1" @arguments;
                                        & "$softwarePath/osu!lazer/Manage.ps1" @arguments;
                                        & "$softwarePath/RetroArch/Manage.ps1" @arguments;
                                        & "$softwarePath/reWASD/Manage.ps1" @arguments;
                                    }
                                };
                            }

                            switch ($_) {
                                ([SetupStage]::Configure) {
                                    if (Get-Config "valhalla.windows.dualboot.enable") {
                                        if (-not (Test-Qemu)) {
                                            # Fix synchronization between Linux and Windows clocks.
                                            Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" -Name "RealTimeIsUniversal" -Value 1 -Type "DWord";
                                        }

                                        # Force time resynchronization
                                        $timeZoneOption = "Start";
                                        $timeZoneKey = "HKLM:\SYSTEM\CurrentControlSet\Services\tzautoupdate";
                                        $service = Get-Service W32Time;
                                        $autoUpdate = (Get-Item $timeZoneKey).GetValue($timeZoneOption);
                                        $stopped = ($service.Status -eq "Stopped");

                                        $setUpdate = { param([int] $Value) Set-ItemProperty $timeZoneKey -Name $timeZoneOption $Value };
                                        & $setUpdate 3;
                                        Start-Service $service;
                                        w32tm /resync /force;
                                        & $setUpdate $autoUpdate;

                                        if ($stopped) {
                                            Stop-Service $service;
                                        }
                                    }

                                    Set-Stage ([SetupStage]::Install);
                                }
                                ([SetupStage]::Install) {
                                    Write-Host "Entering install phase";
                                    Deploy-SoftwareAction;
                                    Set-Stage ([SetupStage]::CreateUser);
                                }
                                ([SetupStage]::CreateUser) {
                                    $users = @(Get-Users);
                                    $i = Get-CurrentUser;

                                    for (; $i -lt $users.Count; $i++) {
                                        $name = $users[$i];
                                        $msAccount = Get-UserConfig -UserName $name "microsoftAccount";
                                        Set-CurrentUser $i;

                                        if (Test-Admin) {
                                            Disable-BootMessage;
                                        }

                                        while ((Get-UserStage) -ne ([UserStage]::Completed)) {
                                            switch (Get-UserStage) {
                                                ($null) {
                                                    Set-UserStage ([UserStage]::Create);
                                                    continue;
                                                }
                                                ([UserStage]::Create) {

                                                    if ($env:UserName -ne $name) {
                                                        New-ValhallaUser $name;

                                                        if ($msAccount) {
                                                            logoff;
                                                        } else {
                                                            Restart-Intermediate;
                                                        }

                                                        exit;
                                                    } else {
                                                        if ($msAccount) {
                                                            if (-not (Test-Admin)) {
                                                                Invoke-OneShot DisableUAC;
                                                                Restart-Computer;
                                                                return;
                                                            }

                                                            Clear-SetupRegistration;
                                                            Disable-OneShotListener;
                                                        }

                                                        Set-UserStage ([UserStage]::Configure);
                                                    }
                                                }
                                                (([UserStage]::Configure)) {
                                                    $adminGroup = @{
                                                        SID = [SecurityIdentifier]::new([WellKnownSidType]::BuiltinAdministratorsSid, $null);
                                                    };

                                                    Deploy-SoftwareAction -Action ([InstallerAction]::ConfigureUser);
                                                    Remove-LocalGroupMember -Member $name @adminGroup -ErrorAction SilentlyContinue;

                                                    foreach ($group in Get-UserConfig -UserName $name "groups") {
                                                        Add-LocalGroupMember -Member $name -Name "$group";
                                                    }

                                                    if (-not $msAccount) {
                                                        net user $name /logonpasswordchg:yes;
                                                    }

                                                    Set-UserStage ([UserStage]::Completed);
                                                }
                                            }
                                        }
                                    }

                                    Set-IsFinished $true;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    function Invoke-WindowsInstallation([Context] $context) {
        $Global:InformationPreference = "Continue";
        $Global:ErrorActionPreference = "Inquire";
        $context.UserNames ??= @("Manuel");
        Start-OldWindowsInstallationScript $context;
    }

    function Start-OldWindowsInstallationScript([Context] $context) {
        . "$PSScriptRoot/Upgrade.ps1";
        $finished = $false;
        Remove-Item Env:\POSH_THEME -ErrorAction SilentlyContinue;
        $configPath = "$PSScriptRoot/../Config";
        $softwarePath = "$PSScriptRoot/../Software";
        $initialConfigStage = "InitialConfiguration";
        $prerequisitesStage = "InstallationPrerequisites";
        $driverStage = "DriverInstallation";
        $softwareStage = "MachineWideSoftwareInstallation";
        $userStage = "CreatingUser";
        $restorationStage = "Restoring";


        while (-not $finished) {
            switch ($context.GetStage()) {
                { (-not $_) -or ($_ -eq $initialConfigStage) } {
                    $context.SetStage($initialConfigStage);

                    if ((Get-Command Initialize-Configuration -ErrorAction SilentlyContinue)) {
                        Write-Information "Configuration initialization function was found. Running...";
                        Initialize-Configuration $context;
                    }

                    $null = Enable-WindowsOptionalFeature -Online -All -FeatureName "NetFx3";

                    . "$configPath/Windows/Install.ps1" $context;
                    . "$configPath/Explorer/Install.ps1" $context;
                    . "$configPath/OpenSSH/Install.ps1" $context;
                    . "$configPath/chocolatey/Install.ps1";

                    $context.RemoveDesktopIcon("*Microsoft Edge*");
                    $context.SetStage($prerequisitesStage);
                    break;
                }
                # Always install updates
                default {
                    Write-Host "Starting Installation and Restoration of Windows";
                    Update-WindowsInstallation $context;
                }
                $prerequisitesStage {
                    Write-Host "Installing prerequisites for installing software";

                    if (-not $(Get-Command winget)) {
                        choco install -y winget;
                    }

                    Install-Module -AcceptLicense -Force "NuGet";
                    Import-Module NuGet;

                    Install-Firefox $context;
                    choco install -y selenium-gecko-driver;
                    $null = Install-Package -Force Selenium.WebDriver -RequiredVersion 4.10.0 -SkipDependencies;

                    winget install --accept-source-agreements --accept-package-agreements -e --id AutoHotkey.AutoHotkey;

                    $context.SetStage($driverStage);
                    break;
                }
                $driverStage {
                    Write-Host "Installing drivers";

                    if ((Get-Command Install-PortValhallaDrivers -ErrorAction SilentlyContinue)) {
                        Write-Information "Driver installation function was found. Starting installation";
                        Install-PortValhallaDrivers $context;
                    }

                    Write-Information "Finished installing drivers";
                    $context.SetStage($softwareStage);
                    $context.Reboot();
                    exit;
                }
                $softwareStage {
                    Write-Host "Setting up software with default app associations";
                    . "$softwarePath/WinSCP/Install.ps1" $context;
                    . "$softwarePath/Thunderbird/Install.ps1" $context;

                    Write-Host "Installing default settings for new users";
                    . "$softwarePath/aliae/Install.ps1" $context;
                    . "$softwarePath/posh-git/Install.ps1";
                    . "$softwarePath/Terminal-Icons/Install.ps1";
                    . "$softwarePath/Oh My Posh/Install.ps1" $context;
                    $context.SetStage($userStage);
                    break;
                }
                $userStage {
                    Install-PersonalUsers $context;
                    $context.SetStage($restorationStage);
                    break;
                }
                $restorationStage {
                    Restore-WindowsInstallation $context;
                    $finished = $true;
                    break;
                }
            }
        }
    }
};