using namespace Microsoft.Win32;

$null = New-Module {
    . "$PSScriptRoot/../lib/Registry.ps1";
    . "$PSScriptRoot/../../lib/Scripting.ps1";
    [RegistryKey] $key = $null;
    $runOncePath = "Software\Microsoft\Windows\CurrentVersion\RunOnce";
    $systemRunOncePath = "HKLM:\$runOncePath";
    $logonPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
    $runOnceName = "PortValhalla";
    $powerSchemeOption = "PowerScheme";
    $autologinOption = "AutoAdminLogon";
    $domainOption = "DefaultDomainName";
    $userOption = "DefaultUserName";
    $passwordOption = "DefaultPassword";

    <#
        .SYNOPSIS
        Gets the reghistry key containing the `RunOnce` commands.

        .PARAMETER UserKey
        The key of the user to get the `RunOnce` key for.
    #>
    function Get-RunOnceKey {
        param(
            [RegistryKey] $UserKey
        )

        [string] $path = $null;

        if ($UserKey) {
            $path = Join-Path ($UserKey.PSPath) $runOncePath;
        }
        else {
            $path = $systemRunOncePath;
        }

        if (-not (Test-Path $path)) {
            New-Item $path;
        }
        else {
            Get-Item $path;
        }
    }

    <#
        .SYNOPSIS
        Generates a script for executing the installer.
    #>
    function Get-StartupScript {
        "pwsh -Command " + (Get-StartupCommand);
    }

    <#
        .SYNOPSIS
        Generates a command for running the installer using `pwsh`.
    #>
    function Get-StartupCommand {
        ($env:DEBUG ? "`$env:DEBUG = $([int]$env:DEBUG);" : "") +
        ($env:VALHALLA_ROOT ? "`$env:VALHALLA_ROOT = $(ConvertTo-Injection $env:VALHALLA_ROOT);" : "") +
        ($env:PWSH_PATH ? "`$env:PWSH_PATH = $(ConvertTo-Injection $env:PWSH_PATH);" : "") +
        ($env:BACKUP_ARCHIVE ? "`$env:BACKUP_ARCHIVE = $(ConvertTo-Injection ([System.IO.Path]::GetFullPath($env:BACKUP_ARCHIVE)));" : "") +
        "`$env:INSTALLER_SCRIPT = $(ConvertTo-Injection (Resolve-Path $env:INSTALLER_SCRIPT));" +
        "`$env:CONFIG_NAME = $(ConvertTo-Injection $env:CONFIG_NAME);" +
        "& `$env:INSTALLER_SCRIPT;";
    }

    <#
        .SYNOPSIS
        Gets the GUID of the current power scheme.
    #>
    function Get-PowerScheme {
        [regex]::Match((powercfg /GETACTIVESCHEME), "Power Scheme GUID: ([0-9a-f-]+) ").Groups[1].Value;
    }

    <#
        .SYNOPSIS
        Sets the current power scheme.

        .PARAMETER SchemeID
        The ID of the scheme to set.
    #>
    function Set-PowerScheme {
        param(
            [string] $SchemeID
        )

        powercfg /S $SchemeID;
    }

    <#
        .SYNOPSIS
        Backs up and overrides the current power scheme if it might cause automatic standby.
    #>
    function Backup-PowerScheme {
        $performanceScheme = "8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c";
        $currentScheme = Get-PowerScheme;

        if ($currentScheme -ne $performanceScheme) {
            Write-Host "Disabling Power Save mode";
            Set-SetupOption $powerSchemeOption $currentScheme;
            powercfg /S $performanceScheme;
        }
    }

    <#
        .SYNOPSIS
        Restores the backed up power scheme.
    #>
    function Restore-PowerScheme {
        $scheme = Get-SetupOption $powerSchemeOption;

        if ($scheme) {
            powercfg /S $scheme;
        }
    }

    <#
        .SYNOPSIS
        Registers a task to run the setup once after the next reboot.

        .PARAMETER UserKey
        The regtistry key of the user to register the task for.
    #>
    function Register-Setup {
        param(
            [Parameter(ParameterSetName = "System")]
            [switch] $System,
            [Parameter(ParameterSetName = "DefaultUser", Mandatory)]
            [switch] $DefaultUser,
            [Parameter(ParameterSetName = "User", Mandatory)]
            [switch] $User,
            [Parameter(ParameterSetName = "User")]
            [Parameter(ParameterSetName = "SpecificUser", Mandatory)]
            [RegistryKey] $UserKey
        )

        if ($DefaultUser.IsPresent) {
            Edit-DefaultUserKey {
                param(
                    [RegistryKey] $Key
                )

                Register-Setup -UserKey $Key;
            }

            return;
        }

        if ($User.IsPresent -or $UserKey) {
            if (-not $UserKey) {
                $UserKey = Get-Item "HKCU:\";
            }

            $key = Get-RunOnceKey $UserKey;
        }
        else {
            $key = Get-RunOnceKey;
        }

        Set-ItemProperty -Path $key.PSPath -Name $runOnceName -Type "ExpandString" -Value (Get-StartupScript);
        $key.Handle.Close();
    }

    <#
        .SYNOPSIS
        Clears leftovers from past registrations.
    #>
    function Clear-SetupRegistration {
        Edit-DefaultUserKey {
            param(
                [RegistryKey] $Key
            )

            $runOnceKey = Get-RunOnceKey $Key;
            Remove-Item $runOnceKey.PSPath;
        }
    }

    <#
        .SYNOPSIS
        Sets the user to login automatically on boot.

        .PARAMETER Name
        The name of the user to login automatically.
    #>
    function Set-AutologinUser {
        param(
            [string] $Name
        )

        Set-ItemProperty $logonPath -Name $autologinOption "1";

        if (-not $Name) {
            $Name = Get-SetupUser;
        }

        $options = @{
            $domainOption   = "";
            $userOption     = $Name;
            $passwordOption = "";
        };

        foreach ($key in $options.Keys) {
            Set-ItemProperty $logonPath -Name $key -Value $options[$key];
        }
    }

    <#
        .SYNOPSIS
        Disables the automatic login.
    #>
    function Disable-Autologin {
        Set-ItemProperty $logonPath -Name $autologinOption "0";

        foreach ($key in @($domainOption, $userOption, $passwordOption)) {
            Remove-ItemProperty $logonPath -Name $key -ErrorAction SilentlyContinue;
        }
    }

    <#
        .SYNOPSIS
        Reboots the machine intermediately and restarts the setup after the next login.
    #>
    function Restart-Intermediate {
        param(
            [Parameter(ParameterSetName = "None")]
            [switch] $NoRegister,
            [Parameter(ParameterSetName = "Default", Mandatory)]
            [switch] $DefaultUser,
            [Parameter(ParameterSetName = "Current", Mandatory)]
            [switch] $CurrentUser
        )

        if (-not $NoRegister.IsPresent) {
            if ($DefaultUser.IsPresent) {
                Register-Setup -DefaultUser;
            }
            elseif ($CurrentUser.IsPresent) {
                Register-Setup -User;
            }
            else {
                Register-Setup;
            }
        }

        if ($env:DEBUG -and (-not $Global:NonInteractive)) {
            Read-Host "Press enter to reboot";
        }

        Restart-Computer -Force;
    }
}