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

. "$PSScriptRoot/../lib/Security.ps1";
. "$PSScriptRoot/../lib/WSL.ps1";

$null = New-Module {
    . "$PSScriptRoot/../lib/Constants.ps1";
    . "$PSScriptRoot/../lib/Deployment.ps1";
    . "$PSScriptRoot/../lib/PowerManagement.ps1";
    . "$PSScriptRoot/../lib/Registry.ps1";
    . "$PSScriptRoot/../lib/Security.ps1";
    . "$PSScriptRoot/../lib/SoftwareManagement.ps1";
    . "$PSScriptRoot/../lib/System.ps1";
    . "$PSScriptRoot/../lib/Update.ps1";
    . "$PSScriptRoot/../lib/Users.ps1";
    . "$PSScriptRoot/../Types/WindowsInstallerAction.ps1";
    . "$PSScriptRoot/../../lib/Operations.ps1";
    . "$PSScriptRoot/../../lib/Settings.ps1";
    . "$PSScriptRoot/../../lib/Scripting.ps1";
    . "$PSScriptRoot/../../lib/SoftwareManagement.ps1";
    . "$PSScriptRoot/../../Common/Types/InstallerAction.ps1";

    <#
        .SYNOPSIS
        Finishes the installation of a running Windows machine.
    #>
    function Start-WindowsInstallation {
        Start-Operation -NoImplicitCleanup {
            Start-InstallationLoop ([WindowsInstallerAction]::Install);
        };
    }

    <#
        .SYNOPSIS
        Creates a backup of the current Windows machine.
    #>
    function Start-WindowsBackup {
        Start-Operation -NoImplicitCleanup {
            Start-InstallationLoop ([WindowsInstallerAction]::Backup);
        };
    }

    <#
        .SYNOPSIS
        Starts the installation loop.
    #>
    function Start-InstallationLoop {
        param(
            [WindowsInstallerAction] $Action
        )

        while ((Get-Stage) -ne ([WindowsInstallerStage]::Completed)) {
            switch (Get-Stage) {
                ($null) {
                    Set-Stage ([WindowsInstallerStage]::Initialize);
                    break;
                }
                ([WindowsInstallerStage]::Initialize) {
                    $env:BACKUP_ARCHIVE = pwsh -Command Write-Host (
                        Read-Host (
                            & {
                                switch ($Action) {
                                    ([WindowsInstallerAction]::Backup) {
                                        "Please select the path you wish to store your backup at";
                                    }
                                    ([WindowsInstallerAction]::Install) {
                                        "Please select an archive you wish to restore from, if you wish to restore from a backup";
                                    }
                                }
                            }));

                    Set-Stage ([WindowsInstallerStage]::Run);
                    break;
                }
                ([WindowsInstallerStage]::Run) {
                    switch ($Action) {
                        ([WindowsInstallerAction]::Install) {
                            while (-not (Get-IsFinished)) {
                                if (Test-Admin) {
                                    $null = Import-Module PSWindowsUpdate;
                                    Update-WindowsInstallation;

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

                                switch (Get-SetupStage) {
                                    ($null) {
                                        Set-SetupStage ([SetupStage]::Initialize);
                                        break;
                                    }
                                    ([SetupStage]::Initialize) {
                                        Set-SetupStage ([SetupStage]::Configure);
                                    }
                                    ([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-SetupStage ([SetupStage]::Install);
                                    }
                                    ([SetupStage]::Install) {
                                        Write-Host "Entering install phase";
                                        Deploy-SoftwareAction;
                                        Set-SetupStage ([SetupStage]::CreateUser);
                                        Restart-Intermediate;
                                        return;
                                    }
                                    ([SetupStage]::CreateUser) {
                                        Install-ValhallaUsers;
                                        Set-IsFinished $true;
                                    }
                                }
                            }
                        }
                        ([WindowsInstallerAction]::Backup) {
                            $finished = $false;
                            $setupUser = Get-SetupUser;

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

                            while (-not $finished) {
                                switch (Get-BackupStage) {
                                    $null {
                                        Set-BackupStage ([BackupStage]::Initialize);
                                    }
                                    ([BackupStage]::Initialize) {
                                        $null = New-LocalUser $setupUser -NoPassword;
                                        Set-LocalUser $setupUser -PasswordNeverExpires $true;
                                        Set-LocalUser $setupUser -PasswordNeverExpires $false;
                                        Add-LocalGroupMember -Member $setupUser @adminGroup;
                                        Set-AutologinUser $setupUser;
                                        Disable-UAC;
                                        Set-BackupStage ([BackupStage]::Backup);
                                        Restart-Intermediate;
                                        return;
                                    }
                                    ([BackupStage]::Backup) {
                                        Deploy-SoftwareAction ([InstallerAction]::Backup);
                                        Set-BackupStage ([BackupStage]::BackupUsers);
                                    }
                                    ([BackupStage]::BackupUsers) {
                                        $users = @(Get-Users);
                                        $i = Get-CurrentUser;
                                        Disable-LocalUser $setupUser;

                                        for (; $i -lt $users.Count; $i++) {
                                            Set-CurrentUser $i;
                                            $user = $users[$i];

                                            if ($env:UserName -ne $user) {
                                                Set-BootMessage "Please Log In" "Please log in with the user ``$user``";
                                                Add-LocalGroupMember -Member "$user" @adminGroup -ErrorAction SilentlyContinue;
                                                Disable-Autologin;
                                                Restart-Intermediate;
                                                return;
                                            }
                                            else {
                                                Deploy-SoftwareAction -Action ([InstallerAction]::BackupUser);
                                                Remove-LocalGroupMember -Member "$user" @adminGroup;

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

                                        Disable-BootMessage;
                                        Write-Host "Never forget to store the backup somewhere safe!";
                                        Write-Host "I mean… what kind of a dumbass would ever forget to do so, right?";
                                        Read-Host "Press enter once you're done";
                                        $finished = $true;
                                    }
                                }
                            }
                        }
                    }

                    Set-Stage ([WindowsInstallerStage]::Cleanup);
                    break;
                }
                ([WindowsInstallerStage]::Cleanup) {
                    $taskName = "PortValhalla Cleaner";
                    $setupUser = Get-SetupUser;
                    Clear-OperationResources;
                    Remove-Item -Recurse -Force "C:\ProgramData\PortValhalla";
                    Get-SetupConfigKey | Remove-Item -Recurse -Force;
                    Disable-Autologin;
                    Disable-LocalUser $setupUser;

                    $script = {
                        param(
                            $TaskName,
                            $UserName,
                            $ProjectRoot,
                            $ArtifactRoot
                        )

                        $user = Get-LocalUser $UserName;
                        [string] $sid = $user.SID;
                        Remove-LocalUser $user;
                        Get-CimInstance Win32_UserProfile | Where-Object { $_.SID -eq $sid } | Remove-CimInstance;
                        Unregister-ScheduledTask -Confirm:$false $TaskName;
                        Remove-Item -Recurse -Force "$ArtifactRoot";

                        if ($ProjectRoot) {
                            Remove-Item -Recurse -Force "$ProjectRoot";
                        }
                    };

                    $trigger = New-ScheduledTaskTrigger -AtStartup;

                    $task = New-ScheduledTaskAction -Execute "pwsh" `
                        -Argument (@(
                            "-Command & { $script }",
                            (ConvertTo-Injection $taskName),
                            (ConvertTo-Injection $setupUser),
                            (ConvertTo-Injection "$env:VALHALLA_ROOT"),
                            (ConvertTo-Injection (Get-ArtifactRoot))
                        ) -join " ");

                    $null = Register-ScheduledTask -Force $taskName -Action $task -Trigger $trigger -RunLevel Highest -User "SYSTEM";
                    Set-Stage ([WindowsInstallerStage]::Completed);
                    Enable-UAC;
                    Restart-Intermediate -NoRegister;
                    break;
                }
            }
        }
    }
};