name: 'Cleanup Processes (Windows)'
description: 'Kill all lemonade, llama-server, and FLM processes on Windows'
runs:
  using: "composite"
  steps:
    - name: Kill zombie processes
      shell: PowerShell
      run: |
        Write-Host "========================================" -ForegroundColor Cyan
        Write-Host "Cleaning up processes on Windows..." -ForegroundColor Cyan
        Write-Host "========================================" -ForegroundColor Cyan

        # Helper function to kill a process and its children recursively
        function Stop-ProcessAndChildren {
            param(
                [int]$ParentPid
            )

            $children = Get-CimInstance Win32_Process -Filter "ParentProcessId = $ParentPid" -ErrorAction SilentlyContinue
            foreach ($child in $children) {
                Stop-ProcessAndChildren -ParentPid $child.ProcessId
            }

            if (Get-Process -Id $ParentPid -ErrorAction SilentlyContinue) {
                Write-Host "  Stopping PID $ParentPid..." -ForegroundColor Yellow
                Stop-Process -Id $ParentPid -Force -ErrorAction SilentlyContinue
            }
        }

        # Targeted PID-file based process cleanup
        $tmpRoot = $env:RUNNER_TEMP
        if (-not $tmpRoot) { $tmpRoot = $PWD }
        Write-Host "Checking for PID files in $tmpRoot..." -ForegroundColor Yellow
        $pidFiles = Get-ChildItem (Join-Path $tmpRoot "lemond*.pid"), (Join-Path $tmpRoot "lemonade*.pid") -ErrorAction SilentlyContinue
        foreach ($file in $pidFiles) {
            $pidText = Get-Content $file.FullName -Raw -ErrorAction SilentlyContinue
            $targetPid = 0
            if ($pidText -and [int]::TryParse($pidText.Trim(), [ref]$targetPid)) {
                Write-Host "Found PID $targetPid in $($file.Name)" -ForegroundColor Yellow
                $proc = Get-Process -Id $targetPid -ErrorAction SilentlyContinue
                if ($proc) {
                    if ($proc.ProcessName -match "lemond" -or $proc.ProcessName -match "lemonade" -or $proc.ProcessName -match "llama-server") {
                        Write-Host "Terminating PID $targetPid ($($proc.ProcessName)) and its descendants..." -ForegroundColor Yellow
                        Stop-ProcessAndChildren -ParentPid $targetPid
                    } else {
                        Write-Host "Process $targetPid ($($proc.ProcessName)) is not a lemonade process (PID reused or stale). Skipping." -ForegroundColor Gray
                    }
                } else {
                    Write-Host "Process $targetPid is not running." -ForegroundColor Gray
                }
                Remove-Item $file.FullName -Force -ErrorAction SilentlyContinue
            }
        }

        # Function to safely kill processes by name pattern
        function Stop-ProcessByPattern {
            param(
                [string]$Pattern,
                [string]$Description
            )

            Write-Host "`nSearching for $Description processes..." -ForegroundColor Yellow
            $processes = Get-Process | Where-Object { $_.ProcessName -like "*$Pattern*" }

            if ($processes) {
                Write-Host "Found $($processes.Count) $Description process(es):" -ForegroundColor Yellow
                $processes | ForEach-Object {
                    Write-Host "  - $($_.ProcessName) (PID: $($_.Id))" -ForegroundColor Gray
                }

                $processes | ForEach-Object {
                    try {
                        Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
                        Write-Host "  Killed: $($_.ProcessName) (PID: $($_.Id))" -ForegroundColor Green
                    } catch {
                        Write-Host "  Failed to kill: $($_.ProcessName) (PID: $($_.Id)) - $_" -ForegroundColor Red
                    }
                }
            } else {
                Write-Host "No $Description processes found." -ForegroundColor Gray
            }
        }

        # Determine if we are on a self-hosted (shared) runner to prevent concurrent run collisions
        $isSelfHosted = $false
        if ($env:RUNNER_ENVIRONMENT -eq "self-hosted") {
            $isSelfHosted = $true
        } elseif ($env:RUNNER_NAME -match "(stx|rai|selfhost)") {
            $isSelfHosted = $true
        }

        if ($isSelfHosted) {
            Write-Host "Running on self-hosted runner ($($env:RUNNER_NAME)). Global cleanup bypassed to prevent concurrency collisions." -ForegroundColor Cyan
        } else {
            Write-Host "Running on GitHub-hosted runner. Proceeding with global cleanup fallback..." -ForegroundColor Cyan

            # Kill llama-server processes
            Stop-ProcessByPattern -Pattern "llama-server" -Description "llama-server"
            Stop-ProcessByPattern -Pattern "llama" -Description "llama"

            # Kill FLM processes (Windows only)
            Stop-ProcessByPattern -Pattern "flm" -Description "FLM"

            # Kill backend subprocesses that outlive a force-killed lemond
            Stop-ProcessByPattern -Pattern "ort-server" -Description "ort-server"
            Stop-ProcessByPattern -Pattern "moonshine-server" -Description "moonshine-server"

            # Kill lemonade processes (lemond, lemonade, lemonade-tray, etc.)
            Stop-ProcessByPattern -Pattern "lemonade" -Description "lemonade"
            Stop-ProcessByPattern -Pattern "lemond" -Description "lemond"

            # Kill wscript processes (VBScript launcher used by lemonade)
            Stop-ProcessByPattern -Pattern "wscript" -Description "wscript"
        }

        # Wait a moment for processes to fully terminate
        Start-Sleep -Seconds 2

        # Attempt to uninstall legacy C++ NSIS-based Lemonade Server, if present
        Write-Host "`nChecking for legacy Lemonade Server NSIS installer..." -ForegroundColor Yellow
        $nsisKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Lemonade Server'
        if (Test-Path $nsisKey) {
            try {
                $nsisProps = Get-ItemProperty -Path $nsisKey -ErrorAction Stop
                $uninstallExe = $nsisProps.UninstallString
                if ($uninstallExe) {
                    Write-Host "  Found NSIS uninstall registry entry. UninstallString: $uninstallExe" -ForegroundColor Gray
                    if (Test-Path $uninstallExe) {
                        Write-Host "  Running NSIS uninstaller silently..." -ForegroundColor Yellow
                        try {
                            & $uninstallExe /S | Out-Null
                            Write-Host "  NSIS uninstaller process invoked." -ForegroundColor Green
                        } catch {
                            Write-Host "  NSIS uninstaller execution failed: $_" -ForegroundColor Red
                        }
                    } else {
                        Write-Host "  NSIS uninstall executable path is stale (file not found). Treating as already removed." -ForegroundColor Gray
                    }
                } else {
                    Write-Host "  NSIS uninstall string missing in registry. Treating as stale entry." -ForegroundColor Gray
                }

                # After running (or skipping) the uninstaller, ensure the ARP key is gone so MSI is not blocked
                if (Test-Path $nsisKey) {
                    Write-Host "  Removing stale NSIS ARP registry key to unblock MSI install..." -ForegroundColor Yellow
                    try {
                        Remove-Item -Path $nsisKey -Recurse -Force -ErrorAction Stop
                        Write-Host "  NSIS ARP registry key removed." -ForegroundColor Green
                    } catch {
                        Write-Host "  Failed to remove NSIS ARP registry key: $_" -ForegroundColor Red
                    }
                } else {
                    Write-Host "  NSIS ARP registry key already removed by uninstaller." -ForegroundColor Green
                }
            } catch {
                Write-Host "  Failed to query NSIS uninstall key: $_" -ForegroundColor Red
            }
        } else {
            Write-Host "  No legacy C++ NSIS installer registry key detected in HKCU (already clean)" -ForegroundColor Gray
        }

        # Clean up Python user directory that can cause issues
        Write-Host "`nCleaning up system Python directory..." -ForegroundColor Yellow
        $systemPythonPath = "C:\windows\system32\config\systemprofile\AppData\Roaming\Python"
        if (Test-Path $systemPythonPath) {
            try {
                Remove-Item -Path $systemPythonPath -Recurse -Force -ErrorAction SilentlyContinue
                Write-Host "  Cleaned: $systemPythonPath" -ForegroundColor Green
            } catch {
                Write-Host "  Failed to clean: $systemPythonPath - $_" -ForegroundColor Red
            }
        } else {
            Write-Host "  System Python directory not found (already clean)" -ForegroundColor Gray
        }

        # Clean up log files to avoid confusion between test runs
        Write-Host "`nCleaning up log files..." -ForegroundColor Yellow
        $logPatterns = @("lemonade*.log", "lemond*.log", "lemonade-server*.log")
        foreach ($pattern in $logPatterns) {
            $logFiles = Get-ChildItem "$env:TEMP\$pattern" -ErrorAction SilentlyContinue
            if ($logFiles) {
                Write-Host "  Found $($logFiles.Count) log file(s) matching '$pattern'" -ForegroundColor Gray
                $logFiles | ForEach-Object {
                    try {
                        Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue
                        Write-Host "    Removed: $($_.Name)" -ForegroundColor Green
                    } catch {
                        Write-Host "    Failed to remove: $($_.Name)" -ForegroundColor Red
                    }
                }
            }
        }

        Write-Host "`n========================================" -ForegroundColor Cyan
        Write-Host "Process cleanup complete!" -ForegroundColor Cyan
        Write-Host "========================================" -ForegroundColor Cyan

    - name: Uninstall Lemonade Server (MSI)
      shell: PowerShell
      run: |
        Write-Host "`n========================================" -ForegroundColor Magenta
        Write-Host "Uninstalling Lemonade Server (MSI)..." -ForegroundColor Magenta
        Write-Host "========================================" -ForegroundColor Magenta

        # Uninstall by Product Code - Attempt to find it first using fast registry search
        $productName = "Lemonade Server"
        Write-Host "Searching for product via registry: $productName" -ForegroundColor Cyan

        $uninstallPaths = @(
            "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
            "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
            "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*"
        )

        # A single stale ARP entry (uninstallable, exit code 1605) must not shadow
        # a real registration of the same DisplayName, so collect every GUID-keyed
        # match across all hives instead of stopping at the first one.
        $foundProducts = @()

        foreach ($path in $uninstallPaths) {
            $subkeys = Get-ItemProperty $path -ErrorAction SilentlyContinue
            foreach ($key in $subkeys) {
                if ($key.DisplayName -eq $productName -and $key.PSChildName -like "{*}") {
                    $foundProducts += $key
                }
            }
        }

        if ($foundProducts.Count -gt 0) {
            foreach ($foundProduct in $foundProducts) {
                $productCode = $foundProduct.PSChildName
                Write-Host "Found installed product: $($foundProduct.DisplayName) (Version: $($foundProduct.DisplayVersion))" -ForegroundColor Green
                Write-Host "Product Code: $productCode" -ForegroundColor Gray

                Write-Host "Uninstalling..." -ForegroundColor Yellow
                $proc = Start-Process -FilePath "msiexec.exe" -ArgumentList "/x $productCode /qn /norestart" -Wait -PassThru

                if ($proc.ExitCode -eq 0) {
                    Write-Host "Uninstall successful!" -ForegroundColor Green
                } elseif ($proc.ExitCode -eq 3010) {
                    Write-Host "Uninstall successful (3010: reboot pending, not performed on CI runners)." -ForegroundColor Yellow
                } elseif ($proc.ExitCode -eq 1605) {
                    Write-Host "Windows Installer reports product not installed (1605). Removing stale ARP registry key..." -ForegroundColor Yellow
                    try {
                        Remove-Item -Path $foundProduct.PSPath -Recurse -Force -ErrorAction Stop
                    } catch {
                        Write-Host "  Remove-Item failed: $_" -ForegroundColor Red
                    }

                    if (Test-Path $foundProduct.PSPath) {
                        Write-Host "WARNING: stale ARP registry key still present: $productCode" -ForegroundColor Red
                    } else {
                        Write-Host "Stale ARP registry key removed: $productCode" -ForegroundColor Green
                    }
                } else {
                    Write-Host "Uninstall failed with exit code $($proc.ExitCode)" -ForegroundColor Red
                }
            }
        } else {
            Write-Host "Product '$productName' not found installed via MSI in registry." -ForegroundColor Gray

            # Fallback: Check if we can uninstall via the MSI file if it exists in current directory
            if (Test-Path "lemonade-server-minimal.msi") {
                Write-Host "Found lemonade-server-minimal.msi in current directory. Attempting uninstall via file..." -ForegroundColor Yellow
                $proc = Start-Process -FilePath "msiexec.exe" -ArgumentList "/x lemonade-server-minimal.msi /qn /norestart" -Wait -PassThru

                if ($proc.ExitCode -eq 0) {
                    Write-Host "Uninstall via MSI file successful!" -ForegroundColor Green
                } elseif ($proc.ExitCode -eq 3010) {
                    Write-Host "Uninstall via MSI file successful (3010: reboot pending, not performed on CI runners)." -ForegroundColor Yellow
                } else {
                    Write-Host "Uninstall via MSI file failed with exit code $($proc.ExitCode)" -ForegroundColor Red
                }
            }
        }
