#===========================================================================
# Tests - AppX Management
#===========================================================================

BeforeAll {
    $script:repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
    . (Join-Path $script:repoRoot "functions\private\Get-WinUtilInstalledAPPX.ps1")
    . (Join-Path $script:repoRoot "functions\private\Install-WinUtilAPPX.ps1")
    . (Join-Path $script:repoRoot "functions\private\Remove-WinUtilAPPX.ps1")
    . (Join-Path $script:repoRoot "functions\private\Remove-WinUtilProvisionedAPPX.ps1")
    . (Join-Path $script:repoRoot "functions\public\Invoke-WPFAppxInstall.ps1")
    . (Join-Path $script:repoRoot "functions\public\Invoke-WPFAppxRemoval.ps1")
    . (Join-Path $script:repoRoot "functions\public\Invoke-WPFButton.ps1")

    $tokens = $null
    $parseErrors = $null
    $provisionedSourcePath = Join-Path $script:repoRoot "functions\private\Remove-WinUtilProvisionedAPPX.ps1"
    $provisionedSourceAst = [System.Management.Automation.Language.Parser]::ParseFile($provisionedSourcePath, [ref]$tokens, [ref]$parseErrors)
    $ps5CommandAssignment = $provisionedSourceAst.Find({
        param($node)
        $node -is [System.Management.Automation.Language.AssignmentStatementAst] -and
            $node.Left -is [System.Management.Automation.Language.VariableExpressionAst] -and
            $node.Left.VariablePath.UserPath -eq "ps5Command"
    }, $true)
    $script:provisionedRemovalScriptBlock = $ps5CommandAssignment.Right.Expression.ScriptBlock.GetScriptBlock()

    $installSourcePath = Join-Path $script:repoRoot "functions\private\Install-WinUtilAPPX.ps1"
    $installSourceAst = [System.Management.Automation.Language.Parser]::ParseFile($installSourcePath, [ref]$tokens, [ref]$parseErrors)
    $installCommandAssignment = $installSourceAst.Find({
        param($node)
        $node -is [System.Management.Automation.Language.AssignmentStatementAst] -and
            $node.Left -is [System.Management.Automation.Language.VariableExpressionAst] -and
            $node.Left.VariablePath.UserPath -eq "ps5Command"
    }, $true)
    $script:appxInstallScriptBlock = $installCommandAssignment.Right.Expression.ScriptBlock.GetScriptBlock()

    function Write-WinUtilLog {
        param($Message, $Level, $Component)
    }
    function Show-WinUtilMessage {
        param($Message, $Title, $Button, $Icon)
    }
    function Invoke-WPFRunspace {
        param($ArgumentList, $ParameterList, [scriptblock]$ScriptBlock)
    }
    function Invoke-WPFUIThread {
        param([scriptblock]$ScriptBlock)
    }
    function Set-WinUtilTweaksProgressIndicator {
        param($Visible, $Label, $Percent)
    }
    function powershell.exe { }
    function Get-AppxPackage {
        param($Name, [switch]$AllUsers, $ErrorAction)
    }
    function Add-AppxPackage {
        param($Register, [switch]$DisableDevelopmentMode, $ErrorAction)
    }
    function Install-WinUtilWinget { }
    function Install-WinUtilProgramWinget {
        param($Action, $Programs)
    }
    function Remove-AppxPackage {
        param(
            [Parameter(ValueFromPipeline = $true)]
            $InputObject,
            $Package,
            [switch]$AllUsers,
            $ErrorAction
        )
        process { }
    }
    function Remove-WinUtilProvisionedAPPX {
        param($PackageList)
    }
    function Get-AppxProvisionedPackage {
        param([switch]$Online, $ErrorAction)
    }
    function Remove-AppxProvisionedPackage {
        param(
            [Parameter(ValueFromPipeline = $true)]
            $InputObject,
            [switch]$Online,
            $PackageName,
            $ErrorAction
        )
        process { }
    }
    function Get-Package {
        [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidOverwritingBuiltInCmdlets', '', Justification='Test shim is intentionally mocked by Pester.')]
        param($Name, $ErrorAction)
    }
    function Uninstall-Package {
        [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidOverwritingBuiltInCmdlets', '', Justification='Test shim is intentionally mocked by Pester.')]
        param(
            [Parameter(ValueFromPipeline = $true)]
            $InputObject,
            [switch]$Force
        )
        process { }
    }
}

Describe "Get-WinUtilInstalledAPPX" {
    BeforeEach {
        Mock Write-WinUtilLog { }
        Mock powershell.exe {
            $global:LASTEXITCODE = 0
            @("Example.One", "Example.Two")
        }
    }

    It "queries installed package names through Windows PowerShell" {
        $result = Get-WinUtilInstalledAPPX

        $result | Should -Be @("Example.One", "Example.Two")
        Should -Invoke -CommandName powershell.exe -Times 1 -Exactly
        Should -Invoke -CommandName Write-WinUtilLog -Times 0 -Exactly
    }

    It "logs query failures and returns no package names" {
        Mock powershell.exe {
            $global:LASTEXITCODE = 1
            "AppX query failed"
        }

        $result = @(Get-WinUtilInstalledAPPX)

        $result | Should -HaveCount 0
        Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
            $Level -eq "ERROR" -and
                $Component -eq "AppX" -and
                $Message -eq "Failed to get installed AppX packages: AppX query failed"
        }
    }
}

Describe "Install-WinUtilAPPX" {
    BeforeEach {
        Mock Write-WinUtilLog { }
        Mock Install-WinUtilWinget { }
        Mock Install-WinUtilProgramWinget { }
        Mock powershell.exe {
            $global:LASTEXITCODE = 0
            "C:\Program Files\WindowsApps\Example.Package\AppxManifest.xml"
        }
    }

    It "uses a local manifest without contacting the Microsoft Store" {
        Install-WinUtilAPPX -Name "Example.Package" -StoreId "9EXAMPLE1234"

        Should -Invoke -CommandName Install-WinUtilWinget -Times 0 -Exactly
        Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 0 -Exactly
        Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
            $Component -eq "AppX" -and
                $Message -like "Registered local AppX manifest for Example.Package*"
        }
    }

    It "falls back to the Microsoft Store when no local manifest is available" {
        Mock powershell.exe { $global:LASTEXITCODE = 0 }

        Install-WinUtilAPPX -Name "Example.Package" -StoreId "9EXAMPLE1234"

        Should -Invoke -CommandName Install-WinUtilWinget -Times 1 -Exactly
        Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly -ParameterFilter {
            $Action -eq "Install" -and $Programs.Count -eq 1 -and $Programs[0] -eq "msstore:9EXAMPLE1234"
        }
    }

    It "logs local registration failures before using the Microsoft Store" {
        Mock powershell.exe {
            $global:LASTEXITCODE = 1
            "Registration failed"
        }

        Install-WinUtilAPPX -Name "Example.Package" -StoreId "9EXAMPLE1234"

        Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
            $Level -eq "WARN" -and
                $Component -eq "AppX" -and
                $Message -eq "Local AppX registration failed for Example.Package: Registration failed"
        }
        Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 1 -Exactly
    }

    It "throws after logging an error when neither install method is available" {
        Mock powershell.exe { $global:LASTEXITCODE = 0 }

        { Install-WinUtilAPPX -Name "Example.Package" } |
            Should -Throw "Unable to install Example.Package because no local manifest or Microsoft Store ID is available."

        Should -Invoke -CommandName Install-WinUtilProgramWinget -Times 0 -Exactly
        Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
            $Level -eq "ERROR" -and
                $Component -eq "AppX" -and
                $Message -eq "Unable to install Example.Package because no local manifest or Microsoft Store ID is available."
        }
    }

    It "registers an installed package manifest through Windows PowerShell" {
        Mock Get-AppxPackage {
            [pscustomobject]@{
                InstallLocation = "C:\Program Files\WindowsApps\Example.Package"
                Version = [version]"2.0.0.0"
            }
        }
        Mock Get-AppxProvisionedPackage { }
        Mock Test-Path { $LiteralPath -eq "C:\Program Files\WindowsApps\Example.Package\AppxManifest.xml" }
        Mock Add-AppxPackage { }

        $result = & $script:appxInstallScriptBlock "Example.Package"

        $result | Should -Be "C:\Program Files\WindowsApps\Example.Package\AppxManifest.xml"
        Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly
        Should -Invoke -CommandName Add-AppxPackage -Times 1 -Exactly
    }
}

Describe "Get installed AppX selection" {
    BeforeEach {
        $script:sync = [Hashtable]::Synchronized(@{
            ProcessRunning = $false
            configs = @{
                feature = @{}
                appxHashtable = @{
                    WPFAppxExample = [pscustomobject]@{ PackageId = "Example.Package" }
                    WPFAppxMissing = [pscustomobject]@{ PackageId = "Missing.Package" }
                }
            }
            WPFAppxExample = [pscustomobject]@{ IsChecked = $false }
            WPFAppxMissing = [pscustomobject]@{ IsChecked = $false }
        })

        Mock Set-WinUtilTweaksProgressIndicator { }
        Mock Get-WinUtilInstalledAPPX { @("Example.Package") }
        Mock Invoke-WPFAppxInstall { }
    }

    AfterEach {
        Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
    }

    It "selects configured packages returned by the compatibility-safe query" {
        Invoke-WPFButton -Button "WPFGetInstalledAppx"

        Should -Invoke -CommandName Get-WinUtilInstalledAPPX -Times 1 -Exactly
        $script:sync.WPFAppxExample.IsChecked | Should -BeTrue
        $script:sync.WPFAppxMissing.IsChecked | Should -BeFalse
    }

    It "routes the install button to the AppX install workflow" {
        Invoke-WPFButton -Button "WPFInstallSelectedAppx"

        Should -Invoke -CommandName Invoke-WPFAppxInstall -Times 1 -Exactly
    }
}

Describe "Remove-WinUtilAPPX" {
    BeforeEach {
        Mock Write-Host { }
        Mock Write-WinUtilLog { }
        Mock Get-AppxPackage {
            @(
                [pscustomobject]@{
                    Name = $Name
                    PackageFullName = "$Name.FullName"
                }
                [pscustomobject]@{
                    Name = $Name
                    PackageFullName = "$Name.FullName"
                }
            )
        }
        Mock Remove-AppxPackage { }
    }

    It "removes matching installed AppX packages" {
        Remove-WinUtilAPPX -Name "Microsoft.Xbox*"

        Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly -ParameterFilter {
            $Name -eq "*Microsoft.Xbox**" -and $AllUsers -eq $true
        }
        Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly -ParameterFilter {
            $Package -eq "*Microsoft.Xbox**.FullName" -and
                $AllUsers -eq $true -and
                $ErrorAction -eq "Stop"
        }
    }

    It "logs installed AppX removal failures" {
        Mock Remove-AppxPackage { throw "Removal failed" }

        { Remove-WinUtilAPPX -Name "Example.Package" } | Should -Not -Throw

        Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter {
            $Level -eq "ERROR" -and
                $Component -eq "AppX" -and
                $Message -eq "Failed to remove AppX package *Example.Package*.FullName: Removal failed"
        }
    }
}

Describe "Remove-WinUtilProvisionedAPPX" {
    BeforeEach {
        Mock Get-AppxProvisionedPackage {
            @(
                [pscustomobject]@{ DisplayName = "Example.One"; PackageName = "Example.One_1.0" }
                [pscustomobject]@{ DisplayName = "Example.Two"; PackageName = "Example.Two_1.0" }
            )
        }
        Mock Remove-AppxProvisionedPackage { }
    }

    It "queries provisioned packages once for all selected package names" {
        & $script:provisionedRemovalScriptBlock "Example.One" "Example.Two"

        Should -Invoke -CommandName Get-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
            $Online -eq $true
        }
        Should -Invoke -CommandName Remove-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
            $PackageName -eq "Example.One_1.0" -and $Online -eq $true
        }
        Should -Invoke -CommandName Remove-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
            $PackageName -eq "Example.Two_1.0" -and $Online -eq $true
        }
    }

    It "surfaces provisioned package removal failures from the child process" {
        Mock Remove-AppxProvisionedPackage { throw "DISM failed" }

        { & $script:provisionedRemovalScriptBlock "Example.One" } |
            Should -Throw "*Failed to remove provisioned AppX package Example.One_1.0: DISM failed*"
        Should -Invoke -CommandName Remove-AppxProvisionedPackage -Times 1 -Exactly -ParameterFilter {
            $PackageName -eq "Example.One_1.0" -and
                $Online -eq $true -and
                $ErrorAction -eq "Stop"
        }
    }

    It "throws after logging child process failures" {
        $source = Get-Content -Path $provisionedSourcePath -Raw

        $source | Should -Match '\$removalOutput = powershell\.exe .* 2>&1'
        $source | Should -Match 'Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message \$errorMessage'
        $source | Should -Match '(?s)AppX provisioned package removal failed:.*throw \$errorMessage.*AppX provisioned package removal completed\.'
    }
}

Describe "Invoke-WPFAppxInstall" {
    BeforeEach {
        $script:sync = [Hashtable]::Synchronized(@{
            ProcessRunning = $false
            Form = [pscustomobject]@{ Dispatcher = [pscustomobject]@{} }
            selectedAppx = [System.Collections.Generic.List[string]]::new()
            configs = @{
                appxHashtable = @{
                    WPFAppxExample = [pscustomobject]@{
                        Content = "Example App"
                        PackageId = "Example.Package"
                        StoreId = "9EXAMPLE1234"
                    }
                }
            }
        })
        $script:capturedAppxInstallScriptBlock = $null
        $script:capturedAppxInstallParameterList = $null
        $script:appxInstallProcessRunningAtLaunch = $null

        Mock Show-WinUtilMessage { "OK" }
        Mock Write-Host { }
        Mock Write-WinUtilLog { }
        Mock Set-WinUtilTweaksProgressIndicator { }
        Mock Invoke-WPFUIThread { }
        Mock Install-WinUtilAPPX { }
        Mock Invoke-WPFRunspace {
            $script:appxInstallProcessRunningAtLaunch = $script:sync.ProcessRunning
            $script:capturedAppxInstallScriptBlock = $ScriptBlock
            $script:capturedAppxInstallParameterList = $ParameterList
            [pscustomobject]@{ MockHandle = $true }
        }
    }

    AfterEach {
        Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name capturedAppxInstallScriptBlock -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name capturedAppxInstallParameterList -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name appxInstallProcessRunningAtLaunch -Scope Script -ErrorAction SilentlyContinue
    }

    It "prompts and exits when no AppX packages are selected for install" {
        Invoke-WPFAppxInstall

        Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
            $Message -eq "No AppX Package selected" -and
                $Title -eq "Error" -and
                $Button -eq "OK" -and
                $Icon -eq "Error"
        }
        Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
    }

    It "prevents overlapping AppX install operations" {
        $script:sync.ProcessRunning = $true
        $script:sync.selectedAppx.Add("WPFAppxExample")

        Invoke-WPFAppxInstall

        Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
            $Message -eq "An AppX process is currently running." -and
                $Title -eq "WinUtil" -and
                $Button -eq "OK" -and
                $Icon -eq "Warning"
        }
        Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
    }

    It "installs selected AppX packages with their Store IDs" {
        $script:sync.selectedAppx.Add("WPFAppxExample")

        Invoke-WPFAppxInstall
        $script:appxInstallProcessRunningAtLaunch | Should -BeTrue
        & $script:capturedAppxInstallScriptBlock -selected @("WPFAppxExample") -apps $script:sync.configs.appxHashtable

        $script:capturedAppxInstallParameterList[0][1][0] | Should -Be "WPFAppxExample"
        Should -Invoke -CommandName Install-WinUtilAPPX -Times 1 -Exactly -ParameterFilter {
            $Name -eq "Example.Package" -and $StoreId -eq "9EXAMPLE1234"
        }
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "Installing Example App (1/1)" -and $Percent -eq 0
        }
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "Installed Example App (1/1)" -and $Percent -eq 100
        }
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "AppX install finished" -and $Percent -eq 100
        }
        Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
            $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
        }
        $script:sync.ProcessRunning | Should -BeFalse
    }

    It "shows failure feedback and clears ProcessRunning when install fails" {
        $script:sync.selectedAppx.Add("WPFAppxExample")
        Mock Install-WinUtilAPPX { throw "Install failed" }

        Invoke-WPFAppxInstall
        & $script:capturedAppxInstallScriptBlock -selected @("WPFAppxExample") -apps $script:sync.configs.appxHashtable

        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "AppX install failed" -and $Percent -eq 100
        }
        Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
            $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
        }
        $script:sync.ProcessRunning | Should -BeFalse
    }
}

Describe "Invoke-WPFAppxRemoval entrypoint" {
    BeforeEach {
        $script:sync = [Hashtable]::Synchronized(@{
            ProcessRunning = $false
            selectedAppx = [System.Collections.Generic.List[string]]::new()
            configs = @{
                appxHashtable = @{}
            }
        })
        $script:capturedAppxScriptBlock = $null
        $script:capturedAppxParameterList = $null
        $script:appxRemovalProcessRunningAtLaunch = $null

        Mock Show-WinUtilMessage { "OK" }
        Mock Invoke-WPFRunspace {
            $script:appxRemovalProcessRunningAtLaunch = $script:sync.ProcessRunning
            $script:capturedAppxScriptBlock = $ScriptBlock
            $script:capturedAppxParameterList = $ParameterList
            [pscustomobject]@{ MockHandle = $true }
        }
    }

    AfterEach {
        Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name capturedAppxScriptBlock -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name capturedAppxParameterList -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name appxRemovalProcessRunningAtLaunch -Scope Script -ErrorAction SilentlyContinue
    }

    It "prompts and exits when no AppX packages are selected" {
        Invoke-WPFAppxRemoval

        Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
            $Message -eq "No AppX Package selected" -and
                $Title -eq "Error" -and
                $Button -eq "OK" -and
                $Icon -eq "Error"
        }
        Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
    }

    It "prevents overlapping AppX removal operations" {
        $script:sync.ProcessRunning = $true
        $script:sync.selectedAppx.Add("WPFAppxExample")

        Invoke-WPFAppxRemoval

        Should -Invoke -CommandName Show-WinUtilMessage -Times 1 -Exactly -ParameterFilter {
            $Message -eq "An AppX process is currently running." -and
                $Title -eq "WinUtil" -and
                $Button -eq "OK" -and
                $Icon -eq "Warning"
        }
        Should -Invoke -CommandName Invoke-WPFRunspace -Times 0 -Exactly
    }

    It "passes selected AppX keys and app metadata to the removal runspace" {
        $script:sync.selectedAppx.Add("WPFAppxExample")
        $script:sync.configs.appxHashtable["WPFAppxExample"] = [pscustomobject]@{
            Content = "Example App"
            PackageId = "Example.Package"
        }

        Invoke-WPFAppxRemoval
        $script:sync.selectedAppx.Add("WPFAppxChangedAfterLaunch")

        $script:appxRemovalProcessRunningAtLaunch | Should -BeTrue
        $script:capturedAppxParameterList[0][1] | Should -HaveCount 1
        Should -Invoke -CommandName Show-WinUtilMessage -Times 0 -Exactly
        Should -Invoke -CommandName Invoke-WPFRunspace -Times 1 -Exactly -ParameterFilter {
            $ScriptBlock -is [scriptblock] -and
                $ParameterList.Count -eq 2 -and
                $ParameterList[0][0] -eq "selected" -and
                $ParameterList[0][1][0] -eq "WPFAppxExample" -and
                $ParameterList[1][0] -eq "apps" -and
                $ParameterList[1][1]["WPFAppxExample"].PackageId -eq "Example.Package"
        }
    }
}

Describe "Invoke-WPFAppxRemoval runspace body" {
    BeforeEach {
        $script:sync = [Hashtable]::Synchronized(@{
            ProcessRunning = $false
            Form = [pscustomobject]@{ Dispatcher = [pscustomobject]@{} }
            selectedAppx = [System.Collections.Generic.List[string]]::new()
            configs = @{
                appxHashtable = @{}
            }
        })
        $script:capturedAppxScriptBlock = $null
        $script:apps = @{
            WPFAppxExample = [pscustomobject]@{
                Content = "Example App"
                PackageId = "Example.Package"
            }
            WPFAppxMicrosoft_XboxGamingOverlay = [pscustomobject]@{
                Content = "Xbox Gaming Overlay"
                PackageId = "Microsoft.XboxGamingOverlay"
            }
            WPFAppxMicrosoft_WindowsNotepad = [pscustomobject]@{
                Content = "Notepad"
                PackageId = "Microsoft.WindowsNotepad"
            }
            WPFAppxMSTeams = [pscustomobject]@{
                Content = "Microsoft Teams"
                PackageId = "MSTeams"
            }
        }

        Mock Invoke-WPFRunspace {
            $script:capturedAppxScriptBlock = $ScriptBlock
            [pscustomobject]@{ MockHandle = $true }
        }
        Mock Show-WinUtilMessage { "OK" }
        Mock Write-Host { }
        Mock Write-WinUtilLog { }
        Mock Set-WinUtilTweaksProgressIndicator { }
        Mock Invoke-WPFUIThread { }
        Mock Stop-Process { }
        Mock Set-ItemProperty { }
        Mock Get-AppxPackage {
            [pscustomobject]@{
                Name = $Name
                PackageFullName = "$Name.FullName"
            }
        }
        Mock Remove-AppxPackage { }
        Mock Remove-WinUtilProvisionedAPPX { }
        Mock Get-Package {
            [pscustomobject]@{
                Name = "Microsoft Teams Meeting Add-in"
            }
        }
        Mock Uninstall-Package { }
    }

    AfterEach {
        Remove-Variable -Name sync -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name capturedAppxScriptBlock -Scope Script -ErrorAction SilentlyContinue
        Remove-Variable -Name apps -Scope Script -ErrorAction SilentlyContinue
    }

    It "removes selected AppX packages and clears ProcessRunning when finished" {
        $selected = @("WPFAppxExample")
        $script:sync.selectedAppx.Add("WPFAppxExample")
        $script:sync.configs.appxHashtable = $script:apps

        Invoke-WPFAppxRemoval
        & $script:capturedAppxScriptBlock -selected $selected -apps $script:apps

        Should -Invoke -CommandName Get-AppxPackage -Times 1 -Exactly -ParameterFilter {
            $Name -eq "*Example.Package*" -and $AllUsers -eq $true
        }
        Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly -ParameterFilter {
            $Package -eq "*Example.Package*.FullName"
        }
        Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter {
            $PackageList.Count -eq 1 -and $PackageList[0] -eq "Example.Package"
        }
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "Removing Example App (1/1)" -and $Percent -eq 0
        }
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "Removed Example App (1/1)" -and $Percent -eq 90
        }
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "AppX removal finished" -and $Percent -eq 100
        }
        Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
            $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
        }
        $script:sync.ProcessRunning | Should -BeFalse
    }

    It "shows failure feedback and clears ProcessRunning when removal fails" {
        $selected = @("WPFAppxExample")
        $script:sync.selectedAppx.Add("WPFAppxExample")
        $script:sync.configs.appxHashtable = $script:apps
        Mock Remove-WinUtilAPPX { throw "Removal failed" }

        Invoke-WPFAppxRemoval
        & $script:capturedAppxScriptBlock -selected $selected -apps $script:apps

        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "AppX removal failed" -and $Percent -eq 100
        }
        Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
            $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
        }
        Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 0 -Exactly
        $script:sync.ProcessRunning | Should -BeFalse
    }

    It "removes packages without UI progress during headless autorun" {
        $selected = @("WPFAppxExample")
        $script:sync.Remove("Form")
        $script:sync.selectedAppx.Add("WPFAppxExample")
        $script:sync.configs.appxHashtable = $script:apps

        Invoke-WPFAppxRemoval
        & $script:capturedAppxScriptBlock -selected $selected -apps $script:apps

        Should -Invoke -CommandName Remove-AppxPackage -Times 1 -Exactly
        Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 0 -Exactly
        Should -Invoke -CommandName Invoke-WPFUIThread -Times 0 -Exactly
        $script:sync.ProcessRunning | Should -BeFalse
    }

    It "shows failure feedback when provisioned package removal fails" {
        $selected = @("WPFAppxExample")
        $script:sync.selectedAppx.Add("WPFAppxExample")
        $script:sync.configs.appxHashtable = $script:apps
        Mock Remove-WinUtilProvisionedAPPX { throw "Provisioned removal failed" }

        Invoke-WPFAppxRemoval
        & $script:capturedAppxScriptBlock -selected $selected -apps $script:apps

        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 1 -Exactly -ParameterFilter {
            $Visible -eq $true -and $Label -eq "AppX removal failed" -and $Percent -eq 100
        }
        Should -Invoke -CommandName Set-WinUtilTweaksProgressIndicator -Times 0 -Exactly -ParameterFilter {
            $Label -eq "AppX removal finished"
        }
        Should -Invoke -CommandName Invoke-WPFUIThread -Times 1 -Exactly -ParameterFilter {
            $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "Error" -overlay "warning"*'
        }
        Should -Invoke -CommandName Invoke-WPFUIThread -Times 0 -Exactly -ParameterFilter {
            $ScriptBlock.ToString() -like '*Set-WinUtilTaskbaritem -state "None" -overlay "checkmark"*'
        }
        $script:sync.ProcessRunning | Should -BeFalse
    }

    It "applies special cleanup for Xbox overlay, Notepad, and Teams selections" {
        $selected = @(
            "WPFAppxMicrosoft_XboxGamingOverlay",
            "WPFAppxMicrosoft_WindowsNotepad",
            "WPFAppxMSTeams"
        )
        foreach ($key in $selected) {
            $script:sync.selectedAppx.Add($key)
        }
        $script:sync.configs.appxHashtable = $script:apps

        Invoke-WPFAppxRemoval
        & $script:capturedAppxScriptBlock -selected $selected -apps $script:apps

        Should -Invoke -CommandName Stop-Process -Times 1 -Exactly -ParameterFilter {
            $Name -eq "GameBarFTServer" -and
                $Force -eq $true -and
                $Confirm -eq $false -and
                $ErrorAction -eq "SilentlyContinue"
        }
        Should -Invoke -CommandName Set-ItemProperty -Times 1 -Exactly -ParameterFilter {
            $Path -eq "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\GameDVR" -and
                $Name -eq "AppCaptureEnabled" -and
                $Value -eq 0
        }
        Should -Invoke -CommandName Stop-Process -Times 1 -Exactly -ParameterFilter {
            $Name -eq "dllhost" -and
                $Force -eq $true -and
                $Confirm -eq $false -and
                $ErrorAction -eq "SilentlyContinue"
        }
        Should -Invoke -CommandName Get-Package -Times 1 -Exactly -ParameterFilter {
            $Name -eq "Microsoft Teams*"
        }
        Should -Invoke -CommandName Uninstall-Package -Times 1 -Exactly -ParameterFilter {
            $InputObject.Name -eq "Microsoft Teams Meeting Add-in" -and $Force -eq $true
        }
        Should -Invoke -CommandName Remove-AppxPackage -Times 3 -Exactly
        Should -Invoke -CommandName Remove-WinUtilProvisionedAPPX -Times 1 -Exactly -ParameterFilter {
            $PackageList.Count -eq 3 -and
            $PackageList[0] -eq "Microsoft.XboxGamingOverlay" -and
            $PackageList[1] -eq "Microsoft.WindowsNotepad" -and
            $PackageList[2] -eq "MSTeams"
        }
        $script:sync.ProcessRunning | Should -BeFalse
    }
}
