<#
.SYNOPSIS
    SDA Glue for Windows Gateways Installer

.DESCRIPTION
    This script will automatically install SDA Glue on a Windows system.
    It must be run with Administrator privileges.
    Use the -Proxy and -ProxyCredential parameters to specify a proxy server and credentials if needed:
    $ .\install.ps1 -Proxy proxy.example.com:8080 -ProxyCredential username
    For more information: https://docs.sdaconsole.io/
#>

param (
    [string]$Proxy,
    [string]$ProxyCredential
)

$SDAGLUE_X86    = "https://repository.eu1.sdaconsole.io/glue/v2/windows/x86/stable/sdaglue.zip"
$SDAGLUE_AMD64  = "https://repository.eu1.sdaconsole.io/glue/v2/windows/amd64/stable/sdaglue.zip"
$SDAGLUE_ARM64  = "https://repository.eu1.sdaconsole.io/glue/v2/windows/arm64/stable/sdaglue.zip"
$OPENVPN_X86    = "https://repository.eu1.sdaconsole.io/glue/lib/OpenVPN-2.6.14-windows-x86.msi"
$OPENVPN_AMD64  = "https://repository.eu1.sdaconsole.io/glue/lib/OpenVPN-2.6.14-windows-amd64.msi"
$OPENVPN_ARM64  = "https://repository.eu1.sdaconsole.io/glue/lib/OpenVPN-2.6.14-windows-arm64.msi"

# Old directories
$OldProgramDir = "$Env:ProgramFiles\SDA Glue"
$OldBinDir     = "$Env:ProgramFiles\SDA Glue\bin"
$OldBinFile    = "$Env:ProgramFiles\SDA Glue\bin\sdaglue.exe"
$OldLink       = "$Env:SystemRoot\sdaglue.exe"

# Installation directories
$ProgramDir = "$Env:ProgramData\SDA Glue"
$LibDir     = "$Env:ProgramData\SDA Glue\lib"
$BinFile    = "$Env:ProgramData\SDA Glue\lib\sdaglue.exe"
$Link       = "$Env:SystemRoot\sdaglue.exe"
$ZipFile    = "$Env:ProgramData\SDA Glue\sdaglue.zip"

# Rollback function
function RollbackInstall {
    # ! Might accidentaly delete SDA Glue Session files
    if (Test-Path $ProgramDir) {
        Remove-Item -Path $ProgramDir -Recurse -Force -ErrorAction SilentlyContinue
    }
    if (Test-Path $Link) {
        Remove-Item -Path $Link -Force -ErrorAction SilentlyContinue
    }
}

function InstallOpenVpn {
    param (
        [string]$Url
    )

    Write-Host "> Installing OpenVPN" -ForegroundColor Green
    Write-Host "> Installer URL: $Url"
    $proc = Start-Process -FilePath "msiexec.exe" -ArgumentList @(
        "/i", "`"$Url`"",
        "ADDLOCAL=Drivers.OvpnDco,OpenVPN,Drivers,Drivers.TAPWindows6",
        "REBOOT=ReallySuppress",
        "/quiet", "/norestart"
    ) -Wait -NoNewWindow -PassThru

    if ($proc.ExitCode -eq 3010) {
        Write-Host "> OpenVPN installed successfully but requires reboot." -ForegroundColor Yellow
    } elseif ($proc.ExitCode -ne 0) {
        Write-Host "> OpenVPN installation failed with exit code: $($proc.ExitCode)" -ForegroundColor Red
        Write-Host "> Are you running PowerShell with Administrator privileges?" -ForegroundColor Yellow
        throw "Could not install OpenVPN."
    }

    Write-Host "> OpenVPN installation completed successfully." -ForegroundColor Green
}

function InstallOpenVpnProxy {
    param (
        [string]$Url,
        [string]$Proxy,
        [string]$ProxyCredential
    )

    Write-Host "> Installing OpenVPN" -ForegroundColor Green
    Write-Host "> Downloading from: $Url"

    # Download the installer to the temp directory
    $InstallerPath = Join-Path -Path $Env:TEMP -ChildPath "OpenVPNInstaller.msi"
    Write-Host "> Target: $InstallerPath"

    if (-not (Test-Path $InstallerPath)) {
        if ($ProxyCredential) {
            Start-BitsTransfer -Source $Url -Destination $InstallerPath -ProxyUsage Override -ProxyList $Proxy -ProxyAuthentication Basic -ProxyCredential $ProxyCredential
        } else {
            Start-BitsTransfer -Source $Url -Destination $InstallerPath -ProxyUsage Override -ProxyList $Proxy
        }
    } else {
        Write-Host "> Installer already exists, skipping download."
    }

    $proc = Start-Process -FilePath "msiexec.exe" -ArgumentList @(
        "/i", "`"$InstallerPath`"",
        "ADDLOCAL=Drivers.OvpnDco,OpenVPN,Drivers,Drivers.TAPWindows6",
        "REBOOT=ReallySuppress",
        "/quiet", "/norestart"
    ) -Wait -NoNewWindow -PassThru

    if ($proc.ExitCode -eq 3010) {
        Write-Host "> OpenVPN installed successfully but requires reboot." -ForegroundColor Yellow
    } elseif ($proc.ExitCode -ne 0) {
        Write-Host "> OpenVPN installation failed with exit code: $($proc.ExitCode)" -ForegroundColor Red
        Write-Host "> Are you running PowerShell with Administrator privileges?" -ForegroundColor Yellow
        throw "Could not install OpenVPN."
    }

    Write-Host "> OpenVPN installation completed successfully." -ForegroundColor Green
}

function InstallSdaGlue {
    param (
        [string]$Url,
        [string]$Proxy,
        [string]$ProxyCredential
    )

    Write-Host "> Installing SDA Glue" -ForegroundColor Green
    if (-not (Test-Path $LibDir)) {
        New-Item -ItemType Directory -Path $LibDir -Force | Out-Null
    }

    Write-Host "> Downloading SDA Glue from: $SdaGlueUrl"
    if ($Proxy) {
        if ($ProxyCredential) {
            Start-BitsTransfer -Source $Url -Destination $ZipFile -ProxyUsage Override -ProxyList $Proxy -ProxyAuthentication Basic -ProxyCredential $ProxyCredential
        } else {
            Start-BitsTransfer -Source $Url -Destination $ZipFile -ProxyUsage Override -ProxyList $Proxy
        }
    } else {
        Invoke-WebRequest -Uri $Url -OutFile $ZipFile -UseBasicParsing
    }

    Write-Host "> Downloaded to: $ZipFile" -ForegroundColor Green

    if (Test-Path $BinFile) {
        Write-Host "> Stopping the existing SDA Glue Service..."
        try {
            & "$BinFile" service stop
        } catch {
            # Ignore errors
        }
        try {
            & "$BinFile" service uninstall
        } catch {
            # Ignore errors
        }
    }

    Write-Host "> Unzipping SDA Glue" -ForegroundColor Green
    Expand-Archive -Path $ZipFile -DestinationPath $LibDir -Force
    Remove-Item $ZipFile -Force -ErrorAction SilentlyContinue

    # Make sure it's executable
    if (-not (Test-Path $BinFile)) {
        throw "SDA Glue binary was not downloaded properly."
    }

    Write-Host "> Installing SDA Glue as Windows Service..."
    & "$BinFile" service install

    Write-Host "> Creating symbolic link..."
    if (-not (Test-Path $Link)) {
        New-Item -ItemType SymbolicLink -Path $Link -Target $BinFile -Force
    }
}

function UninstallOldVersion {
    if (Test-Path $OldBinDir) {
        Write-Host "> Uninstalling old version of SDA Glue v0..."
        try {
            & "$OldBinFile" service stop
        } catch {
            Write-Host "> Stop service: $($_.Exception.Message)" -ForegroundColor Yellow
        }

        try {
            & "$OldBinFile" service uninstall
        } catch {
            Write-Host "> Uninstall service: $($_.Exception.Message)" -ForegroundColor Yellow
        }

        Remove-Item $OldProgramDir -Recurse -Force -ErrorAction SilentlyContinue
        Remove-Item $ProgramDir -Recurse -Force -ErrorAction SilentlyContinue
        Remove-Item $OldLink -Force -ErrorAction SilentlyContinue

        Write-Host "> Old version of SDA Glue has been successfully uninstalled." -ForegroundColor Green
        Write-Host "> You'll need to configure it again with 'sdaglue login' after installation." -ForegroundColor Yellow
    }
}

$script:RebootRequired = $false

function Enable-WinNAT {
    try {
        Write-Host "> Enabling WSL..." -ForegroundColor Yellow
        Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux -NoRestart
        Write-Host "> Enabling Virtual Machine Platform..." -ForegroundColor Yellow
        Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform -NoRestart
        $script:RebootRequired = $true
        Write-Host "> WinNAT enabled successfully." -ForegroundColor Green
    } catch {
        Write-Host "> Failed to enable WSL/Virtual Machine Platform" -ForegroundColor Red
        Write-Host "> Read more: https://learn.microsoft.com/en-us/windows/wsl/install#offline-install" -ForegroundColor Yellow
        Write-Host ""
        Write-Host "> When running: $($_.InvocationInfo.MyCommand.Name)"
        Write-Host "$($_.CategoryInfo.Category): $($_.Exception.Message)" -ForegroundColor Red
    }
}

function Test-WinNAT {
    Write-Host "> Checking if WinNAT is enabled on the system..." -ForegroundColor Yellow

    try {
        Get-NetNat -ErrorAction Stop | Out-Null
        Write-Host "> WinNAT is enabled!" -ForegroundColor Green
    }
    catch [System.Management.Automation.CommandNotFoundException], [Microsoft.Management.Infrastructure.CimException] {
        Write-Host "> WinNAT is not enabled." -ForegroundColor Red

        Write-Host "> Would you like it to try to automatically enable WinNAT? Requires reboot. (y/N)" -ForegroundColor Yellow
        $answer = Read-Host
        if ($answer -match '^(y|yes)$') {
            Enable-WinNAT
        }
    }
    catch {
        Write-Host "Get-NetNat: $($_.CategoryInfo.Category): $($_.Exception.Message)" -ForegroundColor Red
    }
}

try {
    switch ($Env:PROCESSOR_ARCHITECTURE) {
        "AMD64" {
            $SdaGlueUrl = $SDAGLUE_AMD64
            $OpenVpnUrl = $OPENVPN_AMD64
        }
        "ARM64" {
            $SdaGlueUrl = $SDAGLUE_ARM64
            $OpenVpnUrl = $OPENVPN_ARM64
        }
        "x86" {
            $SdaGlueUrl = $SDAGLUE_X86
            $OpenVpnUrl = $OPENVPN_X86
        }
        default {
            Write-Host "> Unsupported architecture: $Env:PROCESSOR_ARCHITECTURE" -ForegroundColor Red
            throw "Please contact the SDA support team for assistance."
        }
    }

    Test-WinNAT
    UninstallOldVersion

    if ($Proxy) {
        InstallOpenVpnProxy -Url $OpenVpnUrl -Proxy $Proxy -ProxyCredential $ProxyCredential
    } else {
        InstallOpenVpn -Url $OpenVpnUrl
    }

    InstallSdaGlue -Url $SdaGlueUrl -Proxy $Proxy -ProxyCredential $ProxyCredential


    if ($Proxy) {
        Write-Host "> Attention!" -ForegroundColor Red
        Write-Host "> If you are using a proxy, please configure it in SDA Glue" -ForegroundColor Yellow
        Write-Host "> using the commands:" -ForegroundColor Yellow
        Write-Host ">   $ `$Proxy = `"http://user:password@hostname:port`"" -ForegroundColor Yellow
        Write-Host ">   $ sdaglue.exe config set --proxy.http=`$Proxy --proxy.https=`$Proxy" -ForegroundColor Yellow
        Write-Host "> Then, restart the SDA Glue service with:" -ForegroundColor Yellow
        Write-Host ">   $ sdaglue.exe service restart" -ForegroundColor Yellow
    }

    Write-Host "> Starting the SDA Glue Service..."
    & "$BinFile" service start

    Write-Host "> SDA Glue installation completed successfully!" -ForegroundColor Green
    Write-Host "> Run 'sdaglue.exe --help' for usage instructions." -ForegroundColor Green

    if ($script:RebootRequired) {
        Write-Host ""
        Write-Host "> A reboot is required to finish enabling WinNAT." -ForegroundColor Yellow
        Write-Host "> Would you like to reboot now? (y/N)" -ForegroundColor Yellow
        $rebootAnswer = Read-Host
        if ($rebootAnswer -match '^(y|yes)$') {
            Restart-Computer -Force
        } else {
            Write-Host "> Please reboot manually before using SDA Glue." -ForegroundColor Yellow
        }
    }
}
catch {
    Write-Host "> Installation failed: $($_.Exception.Message)" -ForegroundColor Red
}
