using namespace System.Management.Automation.Host;
using namespace System.Security.Principal;

$null = New-Module {
    . "$PSScriptRoot/Deployment.ps1";
    . "$PSScriptRoot/../../Common/Scripts/Config.ps1";
    . "$PSScriptRoot/../../Common/Scripts/Operations.ps1";
    . "$PSScriptRoot/../../Common/Types/OneShotTask.ps1";
    $loggedInUserOption = "LoggedInUser";

    <#
        .SYNOPSIS
        Installs all pending users to the system.
    #>
    function Install-ValhallaUsers {
        $users = @(Get-Users);
        $i = Get-CurrentUser;

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

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

            while ((Get-UserStage) -ne ([UserStage]::Completed)) {
                switch (Get-UserStage) {
                    ($null) {
                        Set-UserStage ([UserStage]::Create);
                        break;
                    }
                    ([UserStage]::Create) {
                        if ($env:UserName -ne $name) {
                            $userInfo = @{
                                name = $name;
                                msAccount = $msAccount;
                            };

                            New-ValhallaUser @userInfo;

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

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

                                Clear-SetupRegistration;
                                Disable-OneShotListener;
                            }

                            Set-UserStage ([UserStage]::Configure);
                        }
                    }
                    ([UserStage]::Configure) {
                        $displayName = Get-UserConfig -UserName $name "displayName";

                        $userInfo = @{
                            name = $name;
                        };

                        if ($displayName) {
                            $userInfo.fullName = $displayName;
                        }

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

                        Set-LocalUser @userInfo;
                        Deploy-SoftwareAction -Action 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]::Cleanup);
                    }
                    ([UserStage]::Cleanup) {
                        $user = Get-SetupUser;
                        Disable-LocalUser $name;
                        Enable-LocalUser $user;
                        Set-AutologinUser $user;
                        Unregister-WslDistribution;
                        Set-UserStage ([UserStage]::Completed);
                        Restart-Intermediate;
                        exit;
                    }
                }
            }
        }

        foreach ($user in $users) {
            Enable-LocalUser $user;
        }
    }

    <#
        .SYNOPSIS
        Creates a new user for the PortValhalla setup.

        .PARAMETER Name
        The name of the user to create.

        .PARAMETER MSAccount
        A value indicating whether the user should be created as a Microsoft Account.
    #>
    function New-ValhallaUser {
        param(
            [string] $Name,
            [switch] $MSAccount
        )

        function Add-MicrosoftAccount {
            param(
                [string] $Name
            )

            $newUser = & {
                while ($true) {
                    $currentUsers = Get-LocalUser | ForEach-Object { $_.Name };

                    Write-Host (
                        @(
                            "So… Windows is too dumb to create users which are bound to a Microsoft Account.",
                            "Thus, you have to do it by yourself.",
                            "So sorry…") -join "`n");

                    Write-Host "Create a user for ``$Name`` manually… (because Windows is too stupid)";
                    $null = Read-Host "Hit enter once you're done";

                    $newUsers = @(Get-LocalUser | Where-Object { -not ($currentUsers -contains $_.Name) });

                    if ($newUsers.Count) {
                        if ($newUsers.Count -eq 1) {
                            $newUser = $newUsers[0];

                            Write-Host "Found new user ``$newUser``";

                            if (
                                $Host.UI.PromptForChoice(
                                    "Confirm",
                                    "Is ``$newUser`` your user?",
                                    [ChoiceDescription[]]@(
                                        [ChoiceDescription]::new("&No", "``$newUser`` is not your user"),
                                        [ChoiceDescription]::new("&Yes", "``$newUser`` is your user")),
                                    0) -eq 1) {
                                return $newUser;
                            }
                        } else {
                            $result = $Host.UI.PromptForChoice(
                                "Select your User",
                                "Which one is your user?",
                                [ChoiceDescription[]](
                                    & {
                                        [ChoiceDescription]::new("&None", "None of these users is yours");

                                        for ($i = 0; $i -lt $newUsers.Count; $i++) {
                                            $name = "$($newUsers[$i])";
                                            [ChoiceDescription]::new("&$($i + 1) - ``$name``", "Your user is ``$name``");
                                        }
                                    }), 0);

                            if ($result -gt 0) {
                                return $newUsers[$result - 1];
                            }
                        }
                    } else {
                        Write-Host "";
                        Write-Host "Unable to determine the new user";
                        Write-Host "Retrying…";
                    }
                }
            };

            Set-MSAccountName ([string]$newUser);
        }

        if ($MSAccount) {
            if (Test-Admin) {
                Write-Host "Preparing environment for creating MS Account";
                Register-Setup -DefaultUser;
                Enable-OneShotListener;
                Enable-UAC;

                # Reset Windows activation status
                # Otherwise the login won't work - Windows is fricking frustrating.
                slmgr /upk;
                slmgr /cpky;
                slmgr /rearm;
                Restart-Intermediate -CurrentUser;
                exit;
            }
        }

        Write-Host "Creating personal user ``$Name``…";

        if ($MSAccount) {
            Add-MicrosoftAccount $Name;
            Set-SetupOption $loggedInUserOption $env:UserName;
            Invoke-OneShot ([OneShotTask]::InitializeMSAccount);
        } else {
            New-LocalUser -NoPassword $Name;
            Set-LocalUser $Name -PasswordNeverExpires $true;
            Set-LocalUser $Name -PasswordNeverExpires $false;
            Initialize-UserCreation;
        }
    }

    <#
        .SYNOPSIS
        Prepares the first login for initializing the current user under configuration.
    #>
    function Initialize-UserCreation {
        $name = (@(Get-Users))[(Get-CurrentUser)];
        $msAccount = Get-UserConfig -UserName $name "microsoftAccount";

        Write-Host "Initializing user ``$name``…";

        $userArguments = @{
            name = $name;
        };

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

        if ($msAccount) {
            $accountName = Get-MSAccountName;
            Write-Host "Renaming ``$accountName`` to ``$name``…"
            Rename-LocalUser $accountName $name;
        }

        Set-LocalUser @userArguments;

        if ($msAccount) {
            Disable-LocalUser (Get-SetupOption $loggedInUserOption);
        } else {
            Disable-LocalUser $env:UserName;
        }

        Add-LocalGroupMember `
            @adminGroup `
            $name `
            -ErrorAction SilentlyContinue;

        if ($msAccount) {
            Disable-Autologin;
            Set-BootMessage -Caption "Please Log In" -Message "Please log in using your new Microsoft Account ``$name``.";
        } else {
            Set-AutologinUser "$name";
        }
    }
};