# Single-line progress helper for Run.bat
param(
    [Parameter(Mandatory = $true)]
    [ValidateSet('WaitProcessEnd', 'WaitProcessStart', 'WaitService', 'WaitServiceStopped', 'ProgressDelay', 'WaitMsiInstall')]
    [string]$Action,

    [string]$ProcessName,
    [string]$ServiceName,
    [string]$Label = 'Please wait',
    [int]$MaxSeconds = 60,
    [int]$EstimatedSeconds = 45,
    [int]$SleepSeconds = 1,
    [string]$InstallPathsList = ''
)

$ErrorActionPreference = 'SilentlyContinue'

if (-not [string]::IsNullOrWhiteSpace($InstallPathsList)) {
    $InstallPaths = @($InstallPathsList -split ';' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
} else {
    $InstallPaths = @()
}

function Write-ProgressLine {
    param([string]$Text)
    Write-Host -NoNewline ("`r$Text")
}

function Show-PercentLine {
    param(
        [string]$Message,
        [int]$Percent
    )

    if ($Percent -lt 0) { $Percent = 0 }
    if ($Percent -gt 100) { $Percent = 100 }
    Write-ProgressLine ("       $Message ... $Percent%          ")
}

function Show-SpinnerTick {
    param(
        [string]$Message,
        [int]$Tick,
        [int]$Max
    )

    $percent = [int](($Tick / [Math]::Max($Max, 1)) * 100)
    Show-PercentLine -Message $Message -Percent $percent
}

function Complete-Spinner {
    Write-Host ''
}

function Test-InstallComplete {
    param([string[]]$Paths)

    foreach ($path in $Paths) {
        if ([string]::IsNullOrWhiteSpace($path)) { continue }
        if (-not (Test-Path $path)) { continue }

        $markers = @('iWinNAC.exe', 'iKCS.exe')
        foreach ($marker in $markers) {
            if (Test-Path (Join-Path $path $marker)) {
                return $true
            }
        }
    }

    return $false
}

function Test-MsiProcessFinished {
    param([bool]$MsiSeen)

    if (-not $MsiSeen) { return $false }
    return -not (Get-Process -Name 'msiexec' -ErrorAction SilentlyContinue)
}

function Test-ServiceRegistered {
    param([string]$Name)

    if ([string]::IsNullOrWhiteSpace($Name)) { return $true }

    sc.exe query $Name 2>$null | Out-Null
    return ($LASTEXITCODE -eq 0)
}

function Test-MsiInstallReady {
    param(
        [string[]]$Paths,
        [bool]$MsiSeen,
        [string]$ServiceName
    )

    if (-not $MsiSeen) { return $false }
    if (-not (Test-InstallComplete -Paths $Paths)) { return $false }
    if (-not (Test-MsiProcessFinished -MsiSeen $MsiSeen)) { return $false }
    if (-not (Test-ServiceRegistered -Name $ServiceName)) { return $false }

    return $true
}

function Get-MsiInstallPercent {
    param(
        [datetime]$StartTime,
        [int]$EstimatedSeconds,
        [string[]]$Paths,
        [int]$ReadyStreak,
        [bool]$MsiSeen = $false,
        [string]$ServiceName = ''
    )

    if ($MsiSeen -and (Test-InstallComplete -Paths $Paths)) {
        if (Test-MsiProcessFinished -MsiSeen $MsiSeen) {
            if ([string]::IsNullOrWhiteSpace($ServiceName) -or (Test-ServiceRegistered -Name $ServiceName)) {
                if ($ReadyStreak -ge 1) { return 100 }
                return 98
            }
            return 97
        }

        if ($ReadyStreak -ge 1) { return 95 }
        return 90
    }

    $elapsed = ((Get-Date) - $StartTime).TotalSeconds
    if ($EstimatedSeconds -lt 1) { $EstimatedSeconds = 1 }

    $percent = [int](($elapsed / $EstimatedSeconds) * 90)
    if ($percent -lt 5) { $percent = 5 }
    if ($percent -gt 90) { $percent = 90 }

    return $percent
}

function Complete-MsiInstallSuccess {
    param([string]$Label)

    Show-PercentLine -Message $Label -Percent 100
    Complete-Spinner
    exit 0
}

try {
    switch ($Action) {
        'WaitMsiInstall' {
            $startTime   = Get-Date
            $tick        = 0
            $readyStreak = 0
            $msiSeen     = $false

            while ($true) {
                if (Get-Process -Name 'msiexec' -ErrorAction SilentlyContinue) {
                    $msiSeen = $true
                }

                if (Test-MsiInstallReady -Paths $InstallPaths -MsiSeen $msiSeen -ServiceName $ServiceName) {
                    $readyStreak++
                    $percent = Get-MsiInstallPercent -StartTime $startTime -EstimatedSeconds $EstimatedSeconds -Paths $InstallPaths -ReadyStreak $readyStreak -MsiSeen $msiSeen -ServiceName $ServiceName
                    Show-PercentLine -Message $Label -Percent $percent

                    if ($readyStreak -ge 2) {
                        Complete-MsiInstallSuccess -Label $Label
                    }
                }
                else {
                    $readyStreak = 0
                    $percent = Get-MsiInstallPercent -StartTime $startTime -EstimatedSeconds $EstimatedSeconds -Paths $InstallPaths -ReadyStreak 0 -MsiSeen $msiSeen -ServiceName $ServiceName
                    Show-PercentLine -Message $Label -Percent $percent

                    if ($msiSeen -and (Test-MsiProcessFinished -MsiSeen $true)) {
                        if (Test-InstallComplete -Paths $InstallPaths) {
                            for ($grace = 1; $grace -le 45; $grace++) {
                                if (Test-ServiceRegistered -Name $ServiceName) {
                                    Complete-MsiInstallSuccess -Label $Label
                                }

                                $gracePercent = [Math]::Min(99, 90 + [Math]::Floor($grace / 2))
                                Show-PercentLine -Message $Label -Percent $gracePercent
                                Start-Sleep -Seconds 1
                            }
                        }

                        Complete-Spinner
                        exit 1
                    }
                }

                $tick++
                if ($tick -gt $MaxSeconds) {
                    if (Test-MsiInstallReady -Paths $InstallPaths -MsiSeen $msiSeen -ServiceName $ServiceName) {
                        Complete-MsiInstallSuccess -Label $Label
                    }

                    Complete-Spinner
                    exit 1
                }

                Start-Sleep -Seconds $SleepSeconds
            }
        }

        'WaitProcessEnd' {
            if ([string]::IsNullOrWhiteSpace($ProcessName)) { exit 1 }

            $tick = 0
            while (Get-Process -Name ($ProcessName -replace '\.exe$','') -ErrorAction SilentlyContinue) {
                $tick++
                if ($tick -gt $MaxSeconds) {
                    Complete-Spinner
                    exit 1
                }

                Show-SpinnerTick -Message $Label -Tick $tick -Max $MaxSeconds
                Start-Sleep -Seconds $SleepSeconds
            }

            Complete-Spinner
            exit 0
        }

        'WaitProcessStart' {
            if ([string]::IsNullOrWhiteSpace($ProcessName)) { exit 1 }

            $tick = 0
            while (-not (Get-Process -Name ($ProcessName -replace '\.exe$','') -ErrorAction SilentlyContinue)) {
                $tick++
                if ($tick -gt $MaxSeconds) {
                    Complete-Spinner
                    exit 1
                }

                Show-SpinnerTick -Message $Label -Tick $tick -Max $MaxSeconds
                Start-Sleep -Seconds $SleepSeconds
            }

            Complete-Spinner
            exit 0
        }

        'WaitService' {
            if ([string]::IsNullOrWhiteSpace($ServiceName)) { exit 1 }

            $tick = 0
            while ($true) {
                $query = sc.exe query $ServiceName 2>$null
                if ($LASTEXITCODE -eq 0 -and ($query -match 'RUNNING')) {
                    Show-PercentLine -Message $Label -Percent 100
                    Complete-Spinner
                    exit 0
                }

                $tick++
                if ($tick -gt $MaxSeconds) {
                    if ($query -match 'RUNNING') {
                        Show-PercentLine -Message $Label -Percent 100
                        Complete-Spinner
                        exit 0
                    }

                    Complete-Spinner
                    exit 1
                }

                $percent = [int](($tick / [Math]::Max($MaxSeconds, 1)) * 100)
                if ($percent -lt 1) { $percent = 1 }
                if ($percent -gt 99) { $percent = 99 }
                Show-PercentLine -Message $Label -Percent $percent
                Start-Sleep -Seconds $SleepSeconds
            }
        }

        'WaitServiceStopped' {
            if ([string]::IsNullOrWhiteSpace($ServiceName)) { exit 1 }

            $tick = 0
            while ($true) {
                $query = sc.exe query $ServiceName 2>$null
                if ($LASTEXITCODE -ne 0) {
                    Complete-Spinner
                    exit 0
                }

                if ($query -match 'STOPPED') {
                    Show-PercentLine -Message $Label -Percent 100
                    Complete-Spinner
                    exit 0
                }

                $tick++
                if ($tick -gt $MaxSeconds) {
                    if ($query -match 'STOPPED') {
                        Show-PercentLine -Message $Label -Percent 100
                        Complete-Spinner
                        exit 0
                    }

                    Complete-Spinner
                    exit 1
                }

                $percent = [int](($tick / [Math]::Max($MaxSeconds, 1)) * 100)
                if ($percent -lt 1) { $percent = 1 }
                if ($percent -gt 99) { $percent = 99 }
                Show-PercentLine -Message $Label -Percent $percent
                Start-Sleep -Seconds $SleepSeconds
            }
        }

        'ProgressDelay' {
            for ($tick = 1; $tick -le $MaxSeconds; $tick++) {
                Show-SpinnerTick -Message $Label -Tick $tick -Max $MaxSeconds
                Start-Sleep -Seconds $SleepSeconds
            }

            Complete-Spinner
            exit 0
        }
    }

    exit 1
}
catch {
    Complete-Spinner
    exit 1
}
