using namespace System.Management.Automation.Host;

$null = New-Module {
    . "$PSScriptRoot/Scripting.ps1";
    . "$PSScriptRoot/SoftwareManagement.ps1";
    . "$PSScriptRoot/../Windows/lib/Constants.ps1";
    . "$PSScriptRoot/../Windows/lib/PowerManagement.ps1";
    . "$PSScriptRoot/../Windows/lib/Registry.ps1";
    . "$PSScriptRoot/../Windows/lib/Security.ps1";
    . "$PSScriptRoot/../Windows/lib/SoftwareManagement.ps1";
    . "$PSScriptRoot/../Windows/lib/System.ps1";
    . "$PSScriptRoot/../Windows/lib/Tasks.ps1";
    . "$PSScriptRoot/../Windows/lib/WSL.ps1";

    <#
        .SYNOPSIS
        Gets the PowerShell modules required for operating.
    #>
    function Get-RequiredModules {
        $modules = @(
            @("PSScriptAnalyzer")
        ) + (& {
                if (-not $IsWindows) {
                    @();
                }
                else {
                    @(
                        @("KnownFolders"),
                        @("PSWindowsUpdate"),
                        @("LocalAccounts", $true),
                        @("NuGet")
                    );
                }
            });

        for ($i = 0; $i -lt $modules.Count; $i++) {
            if ($modules[$i] -is [string]) {
                $modules[$i] = @($modules[$i]);
            }
        }

        return $modules;
    }

    function Start-Operation {
        param(
            [switch] $NonInteractive,
            [switch] $NoImplicitCleanup,
            [scriptblock] $Action
        )

        $cleanup = { };
        $taskPending = $false;

        if (-not $Global:InOperation) {
            if ($env:DEBUG) {
                Set-PSDebug -Trace 1;
            }

            if ($IsWindows -and ($null -ne (Get-OneShotTask))) {
                $taskPending = $true;
                [switch] $NonInteractive = $true;
            }

            $Global:InOperation = $true;
            $Global:NonInteractive = $NonInteractive;
            $Global:ErrorActionPreference = $NonInteractive.IsPresent ? 'Continue' : 'Inquire';

            if ($IsWindows) {
                $env:WSLENV = "CONFIG_NAME:VALHALLA_FLAKE_ROOT";
                Backup-PowerScheme;
            }

            if (-not $NoImplicitCleanup.IsPresent) {
                $cleanup = {
                    Remove-Variable -Scope Global -Name "InOperation";
                    Clear-OperationResources;
                };
            }

            & {
                $initialized = $false;

                # Ping digitalcourage DNS server
                # https://digitalcourage.de/
                Write-Host "Waiting for internet connection…";

                while ((Test-Connection -Count 1 5.9.164.112).Status -ne 'Success') {
                    Start-Sleep 0.1;
                }

                while (-not $initialized) {
                    if ($IsWindows) {
                        if (-not ((Test-Command "choco") -and (Test-Command "refreshenv"))) {
                            # Install chocolatey
                            Write-Host "Installing 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")) {
                            Write-Host "Installing PowerShell Core…";
                            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) {
                            $liveScriptOption = "LiveScripts";

                            if (($null -eq (Get-SetupOption $liveScriptOption)) -and (Test-Qemu)) {
                                $result = $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);

                                Set-SetupOption $liveScriptOption $result;

                                if ($result -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 @("INSTALLER_SCRIPT")) {
                                        $variable = Get-Item "Env:\$name";

                                        $path = Join-Path `
                                            "Z:\" `
                                            ([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.exe")) {
                            Install-ChocoPackage gsudo;
                            refreshenv;
                            continue;
                        }

                        function global:Invoke-Sudo {
                            param(
                                $u,
                                [Parameter(ValueFromRemainingArguments, Position = 0)]
                                [string[]] $ArgumentList
                            )
                        
                            $params = @();
                        
                            if ($u) {
                                $params = @("-u", $u);
                            }
                            elseif ($ArgumentList[0] -eq "-u") {
                                $user = $ArgumentList[1];
                                $ArgumentList = $ArgumentList | Select-Object -Skip 2;
                                $params = @("-u", $user);
                            }
                        
                            gsudo @params {
                                $command = $args[0];
                                $flags = ($args | Select-Object -Skip 1);
                                & $command @flags;
                            } -args $ArgumentList;
                        }

                        New-Alias -Force "sudo" -Scope Global Invoke-Sudo;

                        if ($env:DEBUG) {
                            & {
                                $sys32 = "$env:WINDIR/System32";
                                $osk = (Get-Item "$sys32/osk.exe").FullName;
                                $cmd = (Get-Item "$sys32/cmd.exe").FullName;

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

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

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

                        if (-not (Test-Command "7z")) {
                            Install-ChocoPackage 7zip.portable;
                            refreshenv;
                            continue;
                        }

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

                        if (-not (Test-Wsl)) {
                            Install-Wsl;
                            Restart-Intermediate;
                            return;
                        }

                        if (-not (Test-WslDistribution)) {
                            if (-not (Test-Path (Get-WslDistributionDisk))) {
                                Install-WslDistribution;
                            }

                            Register-WslDistribution;
                            continue;
                        }

                        if (-not (Test-Nix)) {
                            Install-Nix;
                            continue;
                        }

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

                        Install-ChocoPackage selenium-gecko-driver firefox;
                        Install-WingetPackage AutoHotkey.AutoHotkey;
                        . "$PSScriptRoot/../Windows/Software/pinned-item/Main.ps1";
                    }

                    Write-Host "Installing PowerShell Modules…";

                    foreach ($module in (Get-RequiredModules)) {
                        $parameters = @{ };

                        if ($module -is [string]) {
                            $module = @($module);
                        }

                        if ($module[1]) {
                            $parameters = @("-AllowPrerelease");
                        }

                        if (-not (Test-PSModule $module[0])) {
                            sudo pwsh -Command Install-Module -Scope AllUsers -AcceptLicense -Force -AllowClobber $module[0] @parameters;
                            Import-Module $module[0];
                        }
                    }

                    if (-not $env:CONFIG_NAME) {
                        Show-ProfileNamePrompt;
                    }

                    $initialized = $true;
                }
            };
        }

        if ($taskPending) {
            Start-OneShot;
        }
        else {
            & $Action;
        }

        & $cleanup;
    }

    <#
        .SYNOPSIS
        Clears resources allocated during the operation.
    #>
    function Clear-OperationResources {
        if ($IsWindows) {
            Uninstall-WslDistribution;
            $null = Uninstall-Package Selenium.WebDriver -ErrorAction Continue;
            Uninstall-ChocoPackage 7zip.portable gsudo selenium-gecko-driver yq;
            Uninstall-WingetPackage AutoHotkey.AutoHotkey;
            Restore-PowerScheme;
        }

        foreach ($module in (Get-RequiredModules)) {
            Remove-Module -Force $module[0] -ErrorAction SilentlyContinue;
            Uninstall-Module -Force -Name $module[0] -ErrorAction SilentlyContinue;
        }
    }
};