. "$PSScriptRoot/Constants.ps1";
. "$PSScriptRoot/../lib/Security.ps1";
. "$PSScriptRoot/../../lib/SoftwareManagement.ps1";

<#
    .SYNOPSIS
    Gets the name of the WSL distribution used for managing the configuration.
#>
function Get-WslDistributionName {
    return "ValhallaUbuntu";
}

<#
    .SYNOPSIS
    Gets the path to the directory containing the WSL distribution.
#>
function Get-WslDistributionPath {
    Join-Path (Get-ArtifactRoot) (Get-WslDistributionName);
}

<#
    .SYNOPSIS
    Gets the path to the virtual hard disk of the WSL distribution.
#>
function Get-WslDistributionDisk {
    return "$(Get-WslDistributionPath)/ext4.vhdx";
}

<#
    .SYNOPSIS
    Checks whether `wsl` is installed properly.
#>
function Test-Wsl {
    & { $null = wsl --status; $?; };
}

<#
    .SYNOPSIS
    Checks whether any WSL distributions are installed for the current user.
#>
function Test-WslDistributions {
    & { $null = wsl -l; $?; };
}

<#
    .SYNOPSIS
    Checks whether the managed distribution is installed.
#>
function Test-WslDistribution {
    & { $null = wsl -d (Get-WslDistributionName) -e true; $?; };
}

<#
    .SYNOPSIS
    Installs `wsl` on the system.
#>
function Install-Wsl {
    wsl --install --no-launch;
    # Microsoft broke WSL - Quelle surprise!
    # ToDo: Remove this workaround once it's unbroken
    Install-SetupPackage "https://github.com/microsoft/WSL/releases/download/2.3.17/wsl.2.3.17.0.x64.msi" -ArgumentList "/Quiet";
}

<#
    .SYNOPSIS
    Installs a Ubuntu distribution to a shared directory.
#>
function Install-WslDistribution {
    $dir = Get-WslDistributionPath;
    $root = Split-Path -Parent $dir;
    $ubuntuPattern = "*Ubuntu*";
    $registryPath = "HKCU:/Software/Microsoft/Windows/CurrentVersion/Lxss";
    $key = Get-Item $registryPath;

    if (-not (Get-AppxPackage $ubuntuPattern)) {
        Install-Wsl;
    }

    wsl --shutdown;

    if ($key) {
        $key = $key | Rename-Item -NewName "$(Split-Path -Leaf $key)_" -PassThru;
    }

    if (-not (Test-Path $root)) {
        $null = New-Item -ItemType Directory $root;
    }

    Copy-Item -Recurse -Force (Get-AppxPackage $ubuntuPattern).InstallLocation $dir;
    Set-UserPermissions $dir;
    & "$dir\ubuntu.exe" install --root;
    wsl --shutdown;
    Remove-Item -Recurse -Force $registryPath;

    if ($key) {
        Move-Item $key.PSPath $registryPath;
    }
}

<#
    .SYNOPSIS
    Uninstalls the managed WSL distribution.
#>
function Uninstall-WslDistribution {
    wsl --unregister (Get-WslDistributionName);
}

<#
    .SYNOPSIS
    Registers the managed WSL distribution.
#>
function Register-WslDistribution {
    wsl --import-in-place (Get-WslDistributionName) (Get-WslDistributionDisk);
    wsl --set-default (Get-WslDistributionName);
}

<#
    .SYNOPSIS
    Unregisters the managed WSL distribution.
#>
function Unregister-WslDistribution {
    $wslDisk = Get-WslDistributionDisk;
    wsl --shutdown;
    $tempDisk = Rename-Item -Force $wslDisk "ext4_.vhdx" -PassThru;
    Uninstall-WslDistribution;
    Move-Item $tempDisk $wslDisk;
}

<#
    .SYNOPSIS
    Checks whether `nix` is installed in WSL.
#>
function Test-Nix {
    wsl --shell-type login type -t nix;
}

<#
    .SYNOPSIS
    Installs `nix` in WSL.
#>
function Install-Nix {
    wsl -- sh `<`(curl -L https://nixos.org/nix/install`) --daemon --yes;
    wsl --shutdown;
}

<#
    .SYNOPSIS
    Execute a `nix` command in WSL.
#>
function Invoke-Nix {
    wsl --shell-type login nix --extra-experimental-features "nix-command flakes" @args;
}

<#
    .SYNOPSIS
    Execute a `fish` command in WSL.
#>
function Invoke-Fish {
    Invoke-Nix run nixpkgs`#fish `-- @args;
}

<#
    .SYNOPSIS
    Converts the specified path to linux and escapes it for the use in a script.

    .PARAMETER Path
    The path to convert.
#>
function ConvertTo-LinuxPath {
    param(
        [string] $Path
    )

    & {
        $ErrorActionPreference = 'Continue';
        $completed = $false;

        while (-not $completed) {
            $job = Start-Job {
                $env:Value = Resolve-Path $Using:Path;
                $env:WSLENV = "Value/p";
                $result = wsl -- bash -c 'echo "$Value"';
                wsl -e printf "%q" "$result";
            };

            $result = Receive-Job -Wait $job;


            if (-not ($result.StartsWith("/"))) {
                Write-Error "The result of the path conversion of ``$Path`` was unexpected: ``$result``";
                continue;
            }

            if ($job.State -ne ([System.Management.Automation.JobState]::Completed)) {
                Write-Error "An error occurred while converting ``$Path`` to a Linux path.`nOutput: ``$result``";
                continue;
            }

            $completed = $true;
        }

        $result;
    };
}