#!/bin/pwsh
. "$PSScriptRoot/../Scripts/Prerequisites.ps1";

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

$null = New-Module {
    . "$PSScriptRoot/../Scripts/Hooks.ps1";
    . "$PSScriptRoot/../Scripts/PowerManagement.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";

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

    <#
        .SYNOPSIS
        Starts the installation loop.
    #>
    function Start-InstallationLoop {
        while (-not (Get-IsFinished)) {
            if ($null -eq (Get-Stage)) {
                Set-Stage ([SetupStage]::Initialize);
            } elseif ((Get-Stage) -eq ([SetupStage]::Initialize)) {
                if (-not ((Test-Command "choco") -and (Test-Command "refreshenv"))) {
                    Invoke-Hook "Install-Chocolatey" -Fallback {
                        # Install chocolatey
                        [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-Command "gsudo")) {
                    Install-ChocoPackage gsudo;
                    refreshenv;
                    continue;
                }

                if (-not (Test-Command "git")) {
                    Install-ChocoPackage git;
                    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 (-not (Test-Winget)) {
                    . "$PSScriptRoot/../Software/winget/Manage.ps1";
                    continue;
                }

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

                if (-not (& { $null = wsl -l; $?; })) {
                    ubuntu 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 -AcceptLicense -Force PSWindowsUpdate;
                    Install-Module -AcceptLicense -Force PSScriptAnalyzer;
                    . "$PSScriptRoot/../Software/PinnedItem/Manage.ps1";
                };

                Set-Stage ([SetupStage]::Configure);
            } else {
                $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(
                        [InstallerAction] $Action
                    )

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

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

                    # Drivers
                    $null = New-Module {
                        $driverPath = "$PSScriptRoot/../Drivers";
                        $mbPath = "$driverPath/ROG Zenith Extreme Alpha";

                        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.elgatoWave") {
                                if (-not (Test-ChocoPackage wavelink)) {
                                    Install-ChocoPackage wavelink -ArgumentList '--install-arguments="/norestart"';
                                    Remove-DesktopIcon "*Wave Link*";
                                    Restart-Intermediate;
                                    exit;
                                }
                            }
                        }

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

                    $null = New-Module {
                        # 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/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;
                            . "$commonSoftware/Oh My Posh/Manage.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 `
                                    ;
                            }
                        }

                        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 "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/Manage.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 `
                                    jellyfin-media-player `
                                    vlc `
                                    ;

                                Remove-DesktopIcon "VLC*";
                                Install-WingetPackage Ytmdesktop.Ytmdesktop;
                                Remove-DesktopIcon "Youtube Music*";
                            }
                        }

                        if (Test-Collection "coding") {
                            if ($install) {
                                Install-ChocoPackage vscode -ArgumentList "--params","/NoDesktopIcon";
                                Install-ChocoPackage vscodium -ArgumentList "--params","/NoDesktopIcon /AssociateWithFiles";

                                Install-ChocoPackage `
                                    gh `
                                    github-desktop `
                                    ida-free `
                                    HxD `
                                    docker-desktop `
                                    imhex `
                                    dotpeek `
                                    ;

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

                            . "$softwarePath/VisualStudio/Manage.ps1" @arguments;

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

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

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

                                Install-ChocoPackage ubisoft-connect -ArgumentList "--ignore-checksums";
                                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 (Get-Stage) {
                    ([SetupStage]::Configure) {
                        if (Get-Config "valhalla.windows.dualboot.enable") {
                            # Fix synchronization between Linux and Windows clocks.
                            Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" -Name "RealTimeIsUniversal" -Value 1 -Type "DWord";

                            # Force time resynchronization
                            $service = Get-Service W32Time;
                            $stopped = ($service.Status -eq "Stopped");
                            Start-Service $service;
                            w32tm /resync /force;

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

                        Set-Stage ([SetupStage]::Install);
                    }
                    ([SetupStage]::Install) {
                        Write-Host "Entering install phase";
                        Deploy-SoftwareAction;
                        Set-Stage ([SetupStage]::CreateUser);
                    }
                    ([SetupStage]::CreateUser) {
                        Start-ValhallaUserSetup;
                        Set-Stage ([SetupStage]::ConfigureUser);
                    }
                    ([SetupStage]::ConfigureUser) {
                        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;
                }
            }
        }
    }
};