Public/Start-SACInteractive.ps1

function Start-SACInteractive {
<#
.SYNOPSIS
    Interactive CLI main menu for the Surgical Autodesk Cleaner toolkit.
.DESCRIPTION
    Presents a top-level action menu that surfaces all SAC tools:
    Surgical Cleanup, Master Purge, User Profile Reset, Licensing Reset,
    Pre-Flight Scan, and Profile Backup Restore. Supports Out-ConsoleGridView
    on PowerShell 7+ with automatic fallback to a native console menu.
 
    Includes a conditional "View Attention Items" menu option that surfaces
    critical failure logs from the last run for immediate review.
#>

    [CmdletBinding()]
    param(
        [switch]$ScanOnly
    )

    # Always reset the target state on start to ensure a fresh session.
    # This prevents the target from 'sticking' between TUI entries in the same shell session.
    $script:SACTarget = [PSCustomObject]@{
        ComputerName = "localhost"
        Credential   = $null
        IsRemote     = $false
    }
    
    # Reset last run status for the fresh session
    $script:SACLastRunStatus = $null

    # Shared helpers (Test-SACRemoteSession and Invoke-SACPause) are imported from the Private folder.

    # -------------------------------------------------------------------------
    # Shared helper: multi-select list (GridView on PS7+, text fallback)
    # -------------------------------------------------------------------------
    function Invoke-SACSelection {
        param (
            [Parameter(Mandatory=$true)][array]$Items,
            [string]$Title,
            [switch]$ViewerOnly
        )
        if ($Items.Count -eq 0) { return @() }

        if ($PSVersionTable.PSVersion.Major -ge 7 -and -not (Test-SACRemoteSession)) {
            if (-not (Get-Command Out-ConsoleGridView -ErrorAction SilentlyContinue)) {
                try { Import-Module Microsoft.PowerShell.ConsoleGuiTools -ErrorAction Stop }
                catch {
                    try {
                        Write-Host "Installing Microsoft.PowerShell.ConsoleGuiTools for enhanced UI..." -ForegroundColor Cyan
                        Install-Module Microsoft.PowerShell.ConsoleGuiTools -Force -Scope CurrentUser -ErrorAction Stop
                        Import-Module Microsoft.PowerShell.ConsoleGuiTools -ErrorAction Stop
                    } catch {}
                }
            }
            if (Get-Command Out-ConsoleGridView -ErrorAction SilentlyContinue) {
                try {
                    $hint = if ($ViewerOnly) { "(Select items to copy to clipboard | ENTER = Close)" } else { "(CTRL-A = Select All | SPACE = Toggle | ENTER = Confirm)" }
                    $selected = $Items | Out-ConsoleGridView -Title "$Title $hint" -OutputMode Multiple -ErrorAction Stop
                    if ($selected) { return @($selected) }
                    return @()
                } catch {
                    Write-Host "`n[!] UI rendering unavailable. Falling back to text menu..." -ForegroundColor Yellow
                }
            }
        }

        Write-Host "`n--- $Title ---" -ForegroundColor Cyan
        if (-not $ViewerOnly) {
            Write-Host "0. ALL ITEMS" -ForegroundColor Yellow
        }
        for ($i = 0; $i -lt $Items.Count; $i++) {
            Write-Host "$($i+1). $($Items[$i])"
        }
        
        $inputStr = ""
        if ($ViewerOnly) {
            $inputStr = Read-Host "`n(Optional) Enter numbers to copy to clipboard (or press Enter to return)"
        } else {
            $inputStr = Read-Host "`nEnter numbers to select (comma-separated, or 0 for all)"
        }

        if ([string]::IsNullOrWhiteSpace($inputStr)) { return @() }
        if ($inputStr -match '0') { return $Items }
        $selected = @()
        $indices = $inputStr -split ',' | ForEach-Object { $_.Trim() }
        foreach ($idx in $indices) {
            if ($idx -match '^\d+$' -and [int]$idx -gt 0 -and [int]$idx -le $Items.Count) {
                $selected += $Items[[int]$idx - 1]
            }
        }
        return $selected
    }

    # -------------------------------------------------------------------------
    # Discover installed Autodesk products for header context
    # -------------------------------------------------------------------------
    function Get-InstalledAutodeskSummary {
        # Known primary/parent product keywords - these float to the top
        $PrimaryProducts = @(
            'AutoCAD','Revit','Civil 3D','Inventor','Navisworks','3ds Max','Maya',
            'Fusion','Vault','ReCap','Advance Steel','BIM 360','InfraWorks'
        )

        $registryBlock = {
            $regPaths = @(
                'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall',
                'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
            )
            $results = @()
            foreach ($path in $regPaths) {
                if (Test-Path $path) {
                    Get-ChildItem -Path $path -ErrorAction SilentlyContinue | ForEach-Object {
                        $dn = $_.GetValue("DisplayName")
                        $pb = $_.GetValue("Publisher")
                        if (($null -ne $dn -and $dn -match 'Autodesk') -or ($null -ne $pb -and $pb -match 'Autodesk')) {
                            if ($dn -match '\b20\d{2}\b') {
                                $results += $dn
                            }
                        }
                    }
                }
            }
            return $results | Select-Object -Unique
        }

        $all = if ($script:SACTarget.IsRemote) {
            $cmdParams = @{
                ComputerName = $script:SACTarget.ComputerName
                ScriptBlock  = $registryBlock
                ErrorAction  = "SilentlyContinue"
            }
            if ($script:SACTarget.Credential) { $cmdParams["Credential"] = $script:SACTarget.Credential }
            Invoke-Command @cmdParams
        } else {
            & $registryBlock
        }

        if (-not $all) { return @() }

        # Score: 0 = primary product, 1 = everything else
        $sorted = $all | Sort-Object {
            $name = $_
            $isPrimary = ($PrimaryProducts | Where-Object { $name -match [regex]::Escape($_) }).Count -gt 0
            if ($isPrimary) { 0 } else { 1 }
        }, { $_ }   # secondary sort: alphabetical within each group

        return $sorted
    }


    function Invoke-SurgicalCleanupFlow {
        param ([bool]$ScanOnly = $false, [switch]$BuildScript)

        Write-Host "`nScanning registry for installed Autodesk products..." -ForegroundColor Cyan

        $discoveryBlock = {
            $regPaths = @(
                'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall',
                'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
            )
            $results = @()
            foreach ($path in $regPaths) {
                if (Test-Path $path) {
                    Get-ChildItem -Path $path -ErrorAction SilentlyContinue | ForEach-Object {
                        $dn = $_.GetValue("DisplayName")
                        $pb = $_.GetValue("Publisher")
                        if (($null -ne $dn -and $dn -match 'Autodesk') -or ($null -ne $pb -and $pb -match 'Autodesk')) {
                            $results += [PSCustomObject]@{
                                DisplayName          = $dn
                                Publisher            = $pb
                                UninstallString      = $_.GetValue("UninstallString")
                                QuietUninstallString = $_.GetValue("QuietUninstallString")
                                InstallLocation      = $_.GetValue("InstallLocation")
                                PSChildName          = $_.PSChildName
                                PSPath               = $_.PSPath
                            }
                        }
                    }
                }
            }
            return $results
        }

        $UninstallKeys = if ($script:SACTarget.IsRemote) {
            $cmdParams = @{
                ComputerName = $script:SACTarget.ComputerName
                ScriptBlock  = $discoveryBlock
                ErrorAction  = "SilentlyContinue"
            }
            if ($script:SACTarget.Credential) { $cmdParams["Credential"] = $script:SACTarget.Credential }
            Invoke-Command @cmdParams
        } else {
            & $discoveryBlock
        }

        if (-not $UninstallKeys) {
            Write-Host "No Autodesk products found on this system." -ForegroundColor Yellow
            return
        }

        $FoundProducts = @()
        foreach ($app in $UninstallKeys) {
            if ($app.DisplayName -match '\b(20\d{2})\b') {
                $year = $matches[1]
                $FoundProducts += [PSCustomObject]@{
                    DisplayName = $app.DisplayName
                    Year        = [int]$year
                }
            }
        }

        if ($FoundProducts.Count -eq 0) {
            Write-Host "No versioned Autodesk products found." -ForegroundColor Yellow
            return
        }

        $AvailableYears = $FoundProducts.Year | Select-Object -Unique | Sort-Object
        $AllAppNames = @()
        foreach ($fp in $FoundProducts) {
            $name = $fp.DisplayName -replace '\s*\b20\d{2}\b.*', '' -replace '^Autodesk\s+', ''
            $name = $name.Trim()
            if ([string]::IsNullOrWhiteSpace($name)) { $name = "Autodesk Core" }
            if ($AllAppNames -notcontains $name) { $AllAppNames += $name }
        }
        $AllAppNames = $AllAppNames | Sort-Object

        Write-Host "`nHow would you like to target?" -ForegroundColor Cyan
        Write-Host " [1] By Product"
        Write-Host " [2] By Year"
        Write-Host " [3] By Product + Year (recommended)"
        $modeInput = Read-Host "Select [1-3]"
        if ([string]::IsNullOrWhiteSpace($modeInput) -or $modeInput -notin @("1","2","3")) { $modeInput = "3" }

        $SelectedProducts = @()
        $SelectedYears    = @()

        if ($modeInput -eq "1") {
            $SelectedProducts = Invoke-SACSelection -Items $AllAppNames -Title "Select Products"
            if (-not $SelectedProducts) { Write-Host "No products selected." -ForegroundColor Red; return }
            $SelectedYears = $AvailableYears
        } elseif ($modeInput -eq "2") {
            $SelectedYears = Invoke-SACSelection -Items $AvailableYears -Title "Select Years"
            if (-not $SelectedYears) { Write-Host "No years selected." -ForegroundColor Red; return }
            $SelectedProducts = $AllAppNames
        } else {
            $SelectedYears = Invoke-SACSelection -Items $AvailableYears -Title "Step 1 of 2 - Select Target Years"
            if (-not $SelectedYears) { Write-Host "No years selected." -ForegroundColor Red; return }

            $FilteredProducts = @()
            foreach ($fp in ($FoundProducts | Where-Object { $_.Year -in $SelectedYears })) {
                $name = $fp.DisplayName -replace '\s*\b20\d{2}\b.*', '' -replace '^Autodesk\s+', ''
                $name = $name.Trim()
                if ([string]::IsNullOrWhiteSpace($name)) { $name = "Autodesk Core" }
                if ($FilteredProducts -notcontains $name) { $FilteredProducts += $name }
            }
            $FilteredProducts = $FilteredProducts | Sort-Object

            $SelectedProducts = Invoke-SACSelection -Items $FilteredProducts -Title "Step 2 of 2 - Select Products for $($SelectedYears -join ', ')"
            if (-not $SelectedProducts) { Write-Host "No products selected." -ForegroundColor Red; return }
        }

        if ($ScanOnly) {
            Write-Host "`nBuilding pre-flight scan report..." -ForegroundColor Cyan

            $Report = @()

            foreach ($product in $SelectedProducts) {
                foreach ($year in $SelectedYears) {
                    $PackageName = "*$product*$year*"
                    $keys = $UninstallKeys | Where-Object { $_.DisplayName -like $PackageName }

                    foreach ($app in $keys) {
                        $actionType = if ($app.PSChildName -match '^{.*}$' -and $app.UninstallString -match '^MsiExec\.exe') { "MSI Uninstall" } else { "Custom Uninstall" }
                        $Report += [PSCustomObject]@{
                            Action="$actionType"; ComponentType="Application"
                            TargetProduct=$product; TargetYear=$year
                            DisplayName=$app.DisplayName; Detail=$app.UninstallString
                        }
                    }
                }
            }

            # Process discovery (remote-aware)
            $processDiscoveryBlock = {
                $ProcessesToKill = @("acad*","AcEventSync*","AcQMod*","revit*","3dsmax*","maya*","inventor*","roamer*","navisworks*","recap*","dwgviewr*")
                $results = @()
                foreach ($procPattern in $ProcessesToKill) {
                    Get-Process -Name $procPattern -ErrorAction SilentlyContinue | ForEach-Object {
                        $results += [PSCustomObject]@{ Name = $_.ProcessName; Id = $_.Id }
                    }
                }
                return $results
            }

            $FoundProcesses = if ($script:SACTarget.IsRemote) {
                $cmdParams = @{
                    ComputerName = $script:SACTarget.ComputerName
                    ScriptBlock  = $processDiscoveryBlock
                    ErrorAction  = "SilentlyContinue"
                }
                if ($script:SACTarget.Credential) { $cmdParams["Credential"] = $script:SACTarget.Credential }
                Invoke-Command @cmdParams
            } else {
                & $processDiscoveryBlock
            }

            foreach ($p in $FoundProcesses) {
                $Report += [PSCustomObject]@{
                    Action="Terminate Process"; ComponentType="Active Process"
                    TargetProduct="Global"; TargetYear="Global"
                    DisplayName="$($p.Name).exe"; Detail="PID: $($p.Id)"
                }
            }

            # File system discovery (remote-aware)
            $fsDiscoveryBlock = {
                param($prods, $years)
                $SafePathsToSearch = @(
                    "$($env:ProgramFiles)\Autodesk","$(${env:ProgramFiles(x86)})\Autodesk",
                    "$($env:ProgramData)\Autodesk","$($env:PUBLIC)\Documents\Autodesk",
                    "C:\Users\*\AppData\Local\Autodesk","C:\Users\*\AppData\Roaming\Autodesk"
                )
                $results = @()
                foreach ($product in $prods) {
                    foreach ($year in $years) {
                        foreach ($basePath in $SafePathsToSearch) {
                            Get-ChildItem -Path $basePath -Filter "*$product*$year*" -Directory -ErrorAction SilentlyContinue | ForEach-Object {
                                $results += [PSCustomObject]@{ Name = $_.Name; FullName = $_.FullName; Product = $product; Year = $year }
                            }
                        }
                    }
                }
                return $results
            }

            $FoundDirs = if ($script:SACTarget.IsRemote) {
                $cmdParams = @{
                    ComputerName = $script:SACTarget.ComputerName
                    ScriptBlock  = $fsDiscoveryBlock
                    ArgumentList = @($SelectedProducts, $SelectedYears)
                    ErrorAction  = "SilentlyContinue"
                }
                if ($script:SACTarget.Credential) { $cmdParams["Credential"] = $script:SACTarget.Credential }
                Invoke-Command @cmdParams
            } else {
                & $fsDiscoveryBlock $SelectedProducts $SelectedYears
            }

            foreach ($d in $FoundDirs) {
                $Report += [PSCustomObject]@{
                    Action="Purge Directory"; ComponentType="Orphaned Folder"
                    TargetProduct=$d.Product; TargetYear=$d.Year
                    DisplayName=$d.Name; Detail=$d.FullName
                }
            }

            if ($Report.Count -gt 0) {
                $Report = $Report | Select-Object -Unique *
                $OutPath = "$([Environment]::GetFolderPath('Desktop'))\Autodesk_ScanReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
                $Report | Export-Csv -Path $OutPath -NoTypeInformation -Force
                Write-Host "Scan Complete! $($Report.Count) actions identified." -ForegroundColor Green
                Write-Host "Report saved to: $OutPath`n" -ForegroundColor Green
            } else {
                Write-Host "Scan Complete! No matching components would be removed.`n" -ForegroundColor Yellow
            }
        } elseif ($BuildScript) {
            # ... (BuildScript logic stays largely the same for now, as it generates a local script)
            # ... (omitted for brevity in this replace call, will keep it if it fits)
            Write-Host "`nGenerating Deployment Script..." -ForegroundColor Cyan
            $OutPath = "$([Environment]::GetFolderPath('Desktop'))\SAC_Deployment_$(Get-Date -Format 'yyyyMMdd_HHmmss').ps1"
            $prodString = ($SelectedProducts | ForEach-Object { "`"$_`"" }) -join ", "
            $yearString = $SelectedYears -join ", "
            $command = "Start-SACCleanup -TargetProducts $prodString -TargetYears $yearString -Silent"
            
            $scriptContent = @"
<#
.SYNOPSIS
    Automated Surgical Autodesk Cleanup Deployment
 
.DESCRIPTION
    This script is designed to safely and silently remove specific versions
    of Autodesk products from this workstation. It will automatically download
    and utilize the SurgicalAutodeskCleaner module from the PowerShell Gallery.
 
    Targeted Products: $($SelectedProducts -join ', ')
    Targeted Years: $($SelectedYears -join ', ')
 
.NOTES
    Generated by SAC Script Builder on $(Get-Date)
#>
 
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
 
# Ensure module is installed
if (-not (Get-Module -ListAvailable -Name SurgicalAutodeskCleaner)) {
    Write-Host "Installing SurgicalAutodeskCleaner from PSGallery..."
    Install-Module -Name 'SurgicalAutodeskCleaner' -Force -AllowClobber -Scope 'CurrentUser'
}
 
Import-Module SurgicalAutodeskCleaner -Force
 
# Execute silent cleanup
Write-Host "Beginning silent Autodesk cleanup..."
$command
"@

            $scriptContent | Out-File -FilePath $OutPath -Encoding utf8
            Write-Host "`nDeployment Script Generated!" -ForegroundColor Green
            Write-Host "Saved to: $OutPath`n" -ForegroundColor Green
            Write-Host "Raw Command:" -ForegroundColor Cyan
            Write-Host " $command`n" -ForegroundColor Gray
        } else {
            if ($script:SACTarget.IsRemote) {
                Write-Host "`nDispatching Surgical Cleanup to $($script:SACTarget.ComputerName)..." -ForegroundColor Cyan
                $prodArgs = ($SelectedProducts | ForEach-Object { "`"$_`"" }) -join ","
                $yearArgs = $SelectedYears -join ","
                $remoteCmd = "Start-SACCleanup -TargetProducts $prodArgs -TargetYears $yearArgs -Silent"
                
                return Invoke-SACRemote -ComputerName $script:SACTarget.ComputerName -Credential $script:SACTarget.Credential -Command $remoteCmd -AutoInstall
            } else {
                Write-Host "`nExecuting Surgical Cleanup..." -ForegroundColor Cyan
                return Start-SACCleanup -TargetProducts $SelectedProducts -TargetYears $SelectedYears
            }
        }
    }

    # -------------------------------------------------------------------------
    # Main menu loop
    # -------------------------------------------------------------------------
    $needsRefresh = $true
    $installedProducts = @()

    if ($ScanOnly) {
        Invoke-SurgicalCleanupFlow -ScanOnly $true
        return
    }

    while ($true) {
        if (-not (Test-SACRemoteSession)) { Clear-Host }
        else { Write-Host "`n--- SAC Main Menu ---`n" -ForegroundColor Cyan }

        if ($needsRefresh) {
            $scanTarget = if ($script:SACTarget.IsRemote) { $script:SACTarget.ComputerName } else { "Local Machine" }
            Write-Host " Scanning ${scanTarget} for installed Autodesk products..." -ForegroundColor DarkGray
            
            try {
                $installedProducts = Get-InstalledAutodeskSummary
            } catch {
                Write-Host " [!] Discovery scan failed: $($_.Exception.Message)" -ForegroundColor Red
                $installedProducts = @()
            }
            $needsRefresh = $false

            # Re-clear after scan to show the clean menu
            if (-not (Test-SACRemoteSession)) { Clear-Host }
        }

        # Box-drawing characters generated at runtime via [char] casts.
        # Source file stays pure 7-bit ASCII - no BOM or encoding dependency.
        $TL = [string][char]0x2554  # top-left corner
        $TR = [string][char]0x2557  # top-right corner
        $BL = [string][char]0x255A  # bottom-left corner
        $BR = [string][char]0x255D  # bottom-right corner
        $ML = [string][char]0x2560  # mid-left tee
        $MR = [string][char]0x2563  # mid-right tee
        $H  = [string][char]0x2550  # horizontal double line
        $V  = [string][char]0x2551  # vertical double line

        # Fixed inner width (between the vertical borders) = 80 chars
        $W = 80

        function Write-BoxLine {
            param(
                [string]$Text = "",
                [string]$Color = "Cyan",
                [string]$BorderColor = "DarkCyan"
            )
            $inner = " $Text".PadRight($W)
            Write-Host $V -ForegroundColor $BorderColor -NoNewline
            Write-Host $inner -ForegroundColor $Color -NoNewline
            Write-Host $V -ForegroundColor $BorderColor
        }

        $border     = $H * $W
        $borderLine = $V + (" " * $W) + $V

        # Top border + title
        $title     = " SURGICAL AUTODESK CLEANER v2.4.1"
        $titlePad  = $title.PadLeft(($W + $title.Length) / 2)
        Write-Host "$TL$border$TR" -ForegroundColor DarkCyan
        Write-BoxLine -Text $titlePad -Color "Cyan"
        Write-Host "$ML$border$MR" -ForegroundColor DarkCyan

        # Detected products
        if ($installedProducts -and $installedProducts.Count -gt 0) {
            Write-BoxLine -Text "Detected Products:" -Color "DarkCyan"
            foreach ($p in ($installedProducts | Select-Object -First 5)) {
                $truncated = if ($p.Length -gt ($W - 4)) { $p.Substring(0, $W - 7) + "..." } else { $p }
                Write-BoxLine -Text " $truncated" -Color "Gray"
            }
            if ($installedProducts.Count -gt 5) {
                Write-BoxLine -Text " ...and $($installedProducts.Count - 5) more" -Color "DarkGray"
            }
        } else {
            Write-BoxLine -Text " No versioned Autodesk products detected." -Color "Yellow"
        }

        # Last-run status badge
        if ($script:SACLastRunStatus) {
            $st = $script:SACLastRunStatus
            Write-Host "$ML$border$MR" -ForegroundColor DarkCyan
            if ($st.Criticals -gt 0) {
                Write-BoxLine -Text " [!] Last Run ($($st.Operation)): Attention Required" -Color "Red" -BorderColor "DarkCyan"
                Write-BoxLine -Text " [$($st.Criticals)] critical item(s) failed" -Color "Red" -BorderColor "DarkCyan"
                Write-BoxLine -Text " [$($st.Warnings)] minor notice(s) logged" -Color "DarkGray" -BorderColor "DarkCyan"
                Write-BoxLine -Text " Log: $($st.LogDir)" -Color "DarkGray" -BorderColor "DarkCyan"
            } elseif ($st.Warnings -gt 0) {
                Write-BoxLine -Text " [~] Last Run ($($st.Operation)): Attention Items" -Color "DarkYellow" -BorderColor "DarkCyan"
                Write-BoxLine -Text " [$($st.Warnings)] minor notice(s) logged" -Color "DarkYellow" -BorderColor "DarkCyan"
                Write-BoxLine -Text " Log: $($st.LogDir)" -Color "DarkGray" -BorderColor "DarkCyan"
            } else {
                Write-BoxLine -Text " [OK] Last Run ($($st.Operation)): Completed successfully" -Color "Green" -BorderColor "DarkCyan"
                Write-BoxLine -Text " Time elapsed: $($st.Elapsed)" -Color "Green" -BorderColor "DarkCyan"
                Write-BoxLine -Text " Log: $($st.LogDir)" -Color "DarkGray" -BorderColor "DarkCyan"
            }
        }

        # Menu options
        Write-Host "$ML$border$MR" -ForegroundColor DarkCyan
        Write-Host $borderLine    -ForegroundColor DarkCyan
        Write-BoxLine -Text " [1] Surgical Cleanup Targeted uninstall by product/year"
        Write-BoxLine -Text " [2] Master Purge Scorched-earth full system removal"
        Write-BoxLine -Text " [3] Reset User Profile Rename/clear per-user AppData and reg"
        Write-BoxLine -Text " [4] Reset Licensing Wipe CLM, token cache and FlexNet"
        Write-BoxLine -Text " [5] Pre-Flight Scan Simulate cleanup, export CSV report"
        Write-BoxLine -Text " [6] Build Cleanup Script Build an SAC script to run later"
        Write-BoxLine -Text " [7] Restore User Profile List/restore SAC backup folders"
        Write-BoxLine -Text " [8] View All Products List all detected Autodesk software"
        if ($script:SACLastRunStatus.LogDir) {
            $vColor = if ($script:SACLastRunStatus.Criticals -gt 0) { "Red" } else { "Yellow" }
            Write-BoxLine -Text " [L] View Last Run Logs Examine transcript or attention items" -Color $vColor
        }
        Write-Host $borderLine    -ForegroundColor DarkCyan
        $targetStr = if ($script:SACTarget.IsRemote) { 
            if ($script:SACTarget.Credential.UserName) { "$($script:SACTarget.ComputerName) ($($script:SACTarget.Credential.UserName))" }
            else { $script:SACTarget.ComputerName }
        } else { "Local Machine" }
        Write-BoxLine -Text " [T] Target Remote Machine Currently: $targetStr" -Color "Yellow"
        Write-BoxLine -Text " [Q] Quit"
        if ($PSVersionTable.PSVersion.Major -lt 7) {
            Write-Host $borderLine    -ForegroundColor DarkCyan
            Write-BoxLine -Text " Notice: Interactive mode is best experienced in PowerShell 7 (Core)+" -Color "DarkGray"
        }
        Write-Host $borderLine    -ForegroundColor DarkCyan
        Write-Host "$BL$border$BR" -ForegroundColor DarkCyan
        Write-Host ""

        $choice = Read-Host " Select an option"

        switch ($choice.Trim().ToUpper()) {

            "1" {
                $script:SACLastRunStatus = Invoke-SurgicalCleanupFlow -ScanOnly $false
                $needsRefresh = $true
                Invoke-SACPause
            }

            "2" {
                Write-Host "`n *** MASTER PURGE - This will remove ALL Autodesk software ***" -ForegroundColor Red
                Write-Host " This action is irreversible. All products, services, and" -ForegroundColor Yellow
                Write-Host " registry data will be forcefully removed from this machine.`n" -ForegroundColor Yellow
                $confirm = Read-Host " Type 'PURGE' to confirm"
                if ($confirm -eq "PURGE") {
                    if ($script:SACTarget.IsRemote) {
                        Write-Host "`n Dispatching Master Purge to $($script:SACTarget.ComputerName)..." -ForegroundColor Cyan
                        $script:SACLastRunStatus = Invoke-SACRemote -ComputerName $script:SACTarget.ComputerName -Credential $script:SACTarget.Credential -Command "Start-SACPurge -Silent" -AutoInstall
                    } else {
                        $script:SACLastRunStatus = Start-SACPurge
                    }
                    $needsRefresh = $true
                } else {
                    Write-Host " Master Purge cancelled." -ForegroundColor Yellow
                }
                Invoke-SACPause
            }

            "3" {
                Write-Host "`n Roaming folders will be RENAMED (backed up) by default." -ForegroundColor Cyan
                Write-Host " Local cache folders will be DELETED outright.`n" -ForegroundColor Cyan

                $delRoaming = $false
                $delChoice = Read-Host " Delete Roaming instead of rename? (y/N)"
                if ($delChoice.Trim().ToLower() -eq "y") { $delRoaming = $true }

                if ($script:SACTarget.IsRemote) {
                    Write-Host "`n Dispatching Profile Reset to $($script:SACTarget.ComputerName)..." -ForegroundColor Cyan
                    $flags = if ($delRoaming) { "-DeleteRoaming -Silent" } else { "-Silent" }
                    $script:SACLastRunStatus = Invoke-SACRemote -ComputerName $script:SACTarget.ComputerName -Credential $script:SACTarget.Credential -Command "Reset-SACUserProfile $flags" -AutoInstall
                } else {
                    if ($delRoaming) { $script:SACLastRunStatus = Reset-SACUserProfile -DeleteRoaming } else { $script:SACLastRunStatus = Reset-SACUserProfile }
                }
                $needsRefresh = $true
                Invoke-SACPause
            }

            "4" {
                Write-Host "`n FlexNet (adsk* stubs) removal is optional.`n" -ForegroundColor Cyan
                $flexChoice = Read-Host " Also remove Autodesk FlexNet stubs? (y/N)"
                $includeFlex = ($flexChoice.Trim().ToLower() -eq "y")

                if ($script:SACTarget.IsRemote) {
                    Write-Host "`n Dispatching Licensing Reset to $($script:SACTarget.ComputerName)..." -ForegroundColor Cyan
                    $flags = if ($includeFlex) { "-IncludeFlexNet -Silent" } else { "-Silent" }
                    $script:SACLastRunStatus = Invoke-SACRemote -ComputerName $script:SACTarget.ComputerName -Credential $script:SACTarget.Credential -Command "Reset-SACLicensing $flags" -AutoInstall
                } else {
                    if ($includeFlex) { $script:SACLastRunStatus = Reset-SACLicensing -IncludeFlexNet } else { $script:SACLastRunStatus = Reset-SACLicensing }
                }
                $needsRefresh = $true
                Invoke-SACPause
            }

            "5" {
                Invoke-SurgicalCleanupFlow -ScanOnly $true
                Invoke-SACPause
            }

            "6" {
                Invoke-SurgicalCleanupFlow -BuildScript
                Invoke-SACPause
            }

            "7" {
                Restore-SACUserProfile -List
                Write-Host ""
                $restoreChoice = Read-Host " Restore a specific backup? Enter full path (or press Enter to return)"
                if (-not [string]::IsNullOrWhiteSpace($restoreChoice)) {
                    Restore-SACUserProfile -Restore -BackupPath $restoreChoice.Trim()
                    $needsRefresh = $true
                }
                Invoke-SACPause
            }

            "8" {
                $selected = Invoke-SACSelection -Items $installedProducts -Title "Detected Autodesk Products on ${targetStr}" -ViewerOnly
                if ($selected) {
                    try {
                        $text = $selected -join "`r`n"
                        $text | Set-Clipboard -ErrorAction Stop
                        Write-Host "`n [OK] $($selected.Count) product(s) copied to clipboard." -ForegroundColor Green
                    } catch {
                        Write-Host "`n [!] Failed to copy to clipboard (terminal/host restriction)." -ForegroundColor Yellow
                    }
                }
                Invoke-SACPause
            }

            "L" {
                if (-not $script:SACLastRunStatus.LogDir) {
                    Write-Host "`n No log directory found for the last run." -ForegroundColor Yellow
                    Start-Sleep -Seconds 1
                    continue
                }

                $logDir = $script:SACLastRunStatus.LogDir
                $files = @()

                Write-Host "`n Retrieving log file list from ${targetStr}..." -ForegroundColor DarkGray
                if ($script:SACTarget.IsRemote) {
                    $cmdParams = @{
                        ComputerName = $script:SACTarget.ComputerName
                        ScriptBlock  = { Get-ChildItem -Path $using:logDir -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name }
                        ErrorAction  = "SilentlyContinue"
                    }
                    if ($script:SACTarget.Credential) { $cmdParams["Credential"] = $script:SACTarget.Credential }
                    $files = Invoke-Command @cmdParams
                } else {
                    $files = Get-ChildItem -Path $logDir -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Name
                }

                if (-not $files) {
                    Write-Host " No log files found in $logDir" -ForegroundColor Yellow
                    Invoke-SACPause
                    continue
                }

                $selectedFile = Invoke-SACSelection -Items $files -Title "Select Log File to View"
                if ($selectedFile) {
                    $filePath = Join-Path $logDir $selectedFile[0]
                    Write-Host "`n Reading $selectedFile..." -ForegroundColor DarkGray
                    
                    $content = @()
                    if ($script:SACTarget.IsRemote) {
                        $cmdParams = @{
                            ComputerName = $script:SACTarget.ComputerName
                            ScriptBlock  = { Get-Content -Path $using:filePath -Tail 5000 -ErrorAction SilentlyContinue }
                            ErrorAction  = "SilentlyContinue"
                        }
                        if ($script:SACTarget.Credential) { $cmdParams["Credential"] = $script:SACTarget.Credential }
                        $content = Invoke-Command @cmdParams
                    } else {
                        $content = Get-Content -Path $filePath -Tail 5000 -ErrorAction SilentlyContinue
                    }

                    if ($content) {
                        Invoke-SACSelection -Items $content -Title "Viewing: $selectedFile (Last 5000 lines)" -ViewerOnly
                    } else {
                        Write-Host " Log file is empty or inaccessible." -ForegroundColor Yellow
                        Invoke-SACPause
                    }
                }
            }

            "T" {
                $comp = Read-Host "`n Enter remote computer name (or leave blank to reset)"
                if ([string]::IsNullOrWhiteSpace($comp) -or $comp.Trim().ToLower() -eq "local") {
                    $script:SACTarget = [PSCustomObject]@{ ComputerName = "localhost"; Credential = $null; IsRemote = $false }
                    $needsRefresh = $true
                    Write-Host " Target successfully reset to LOCAL MACHINE." -ForegroundColor Green
                    Start-Sleep -Seconds 1
                } else {
                    $res = Connect-SACTarget -ComputerName $comp.Trim()
                    if ($res.Connected) {
                        # We consider it remote if it's NOT explicitly "localhost"
                        $script:SACTarget = [PSCustomObject]@{ 
                            ComputerName = $res.ComputerName; 
                            Credential = $res.Credential; 
                            IsRemote = ($res.ComputerName -ne "localhost") 
                        }
                        $needsRefresh = $true
                        Write-Host " Target successfully set to REMOTE: $($res.ComputerName)." -ForegroundColor Green
                        Start-Sleep -Seconds 1
                    } else {
                        # Recalculate target display string for the error message to avoid stale data
                        $currentSetting = if ($script:SACTarget.IsRemote) { $script:SACTarget.ComputerName } else { "Local Machine" }
                        Write-Host "`n [!] FAILED to connect to $($comp.Trim())." -ForegroundColor Red
                        Write-Host " Target remains set to: $currentSetting" -ForegroundColor Yellow
                        Invoke-SACPause
                    }
                }
            }

            "Q" {
                Write-Host "`nExiting Surgical Autodesk Cleaner. Goodbye.`n" -ForegroundColor Cyan
                return
            }

            default {
                Write-Host "`n Invalid option. Please select 1-7 or Q." -ForegroundColor Yellow
                Start-Sleep -Seconds 1
            }
        }
    }
}