Private/Core/Install-ApplicationUpdate.ps1

function Install-ApplicationUpdate {
    <#
    .SYNOPSIS
        Installs an application update using winget
    .DESCRIPTION
        Handles the actual installation of an application update with pre/post scripts
    .PARAMETER AppId
        The winget app ID to update
    .PARAMETER AppConfig
        Optional managed application configuration
    .PARAMETER Force
        Force installation even if conflicting processes are running
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$AppId,
        
        [Parameter()]
        [ManagedApplication]$AppConfig,
        
        [Parameter()]
        [switch]$Force
    )
    
    $result = [InstallationResult]::new()
    $result.AppId = $AppId
    $result.Status = [InstallationStatus]::InProgress
    
    $startTime = Get-Date
    
    try {
        Write-PatchLog "Starting update for $AppId" -Type Info
        
        # Get app name
        $pkg = Get-WinGetPackage -Id $AppId -ErrorAction SilentlyContinue
        if ($pkg) {
            $result.AppName = $pkg.Name
        }
        else {
            $result.AppName = $AppId
        }
        
        # Check for conflicting processes
        if ($AppConfig -and $AppConfig.ConflictingProcesses.Count -gt 0) {
            $running = Test-ConflictingProcess -ProcessNames $AppConfig.ConflictingProcesses
            if ($running -and -not $Force) {
                $result.Status = [InstallationStatus]::Deferred
                $result.Message = "Conflicting processes running: $($AppConfig.ConflictingProcesses -join ', ')"
                $result.ExitCode = 1602  # User cancelled / deferred
                Write-PatchLog $result.Message -Type Warning
                return $result
            }
            elseif ($running -and $Force) {
                # Force close conflicting processes
                Write-PatchLog "Force closing conflicting processes for $AppId" -Type Warning
                foreach ($procName in $AppConfig.ConflictingProcesses) {
                    $procNameClean = $procName -replace '\.exe$', ''
                    Get-Process -Name $procNameClean -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
                }
                Start-Sleep -Seconds 2
            }
        }
        
        # Run pre-installation script if configured
        if ($AppConfig -and $AppConfig.PreScript) {
            Write-PatchLog "Running pre-install script for $AppId" -Type Info
            try {
                $scriptBlock = [ScriptBlock]::Create($AppConfig.PreScript)
                Invoke-Command -ScriptBlock $scriptBlock -ErrorAction Stop
            }
            catch {
                Write-PatchLog "Pre-install script failed: $_" -Type Warning
            }
        }
        
        # Build update parameters
        $updateParams = @{
            Id = $AppId
            Mode = 'Silent'
        }
        
        if ($AppConfig -and $AppConfig.InstallArguments) {
            $updateParams['Override'] = $AppConfig.InstallArguments
        }
        
        # Perform the update
        Write-PatchLog "Installing update for $AppId" -Type Info
        $updateResult = Update-WinGetPackage @updateParams -ErrorAction Stop
        
        # Check result
        if ($updateResult.Status -eq 'Ok' -or $updateResult.RebootRequired) {
            $result.Status = [InstallationStatus]::Success
            $result.ExitCode = 0
            $result.Message = "Successfully updated $($result.AppName)"
            
            if ($updateResult.RebootRequired) {
                $result.RebootRequired = $true
                $result.Message += " (reboot required)"
            }
            
            Write-PatchLog $result.Message -Type Info
        }
        else {
            $result.Status = [InstallationStatus]::Failed
            $result.ExitCode = 1
            $result.Message = "Update failed for $($result.AppName): $($updateResult.Status)"
            Write-PatchLog $result.Message -Type Error
        }
        
        # Run post-installation script if configured
        if ($AppConfig -and $AppConfig.PostScript -and $result.Status -eq [InstallationStatus]::Success) {
            Write-PatchLog "Running post-install script for $AppId" -Type Info
            try {
                $scriptBlock = [ScriptBlock]::Create($AppConfig.PostScript)
                Invoke-Command -ScriptBlock $scriptBlock -ErrorAction Stop
            }
            catch {
                Write-PatchLog "Post-install script failed: $_" -Type Warning
            }
        }
        
        # Check if app config indicates reboot required
        if ($AppConfig -and $AppConfig.RequiresReboot) {
            $result.RebootRequired = $true
        }
    }
    catch {
        $result.Status = [InstallationStatus]::Failed
        $result.ExitCode = 1
        $result.Message = "Update failed for $AppId : $_"
        Write-PatchLog $result.Message -Type Error
    }
    finally {
        $result.Duration = (Get-Date) - $startTime
    }
    
    return $result
}