Support/Package/Lifecycle/Eigenverft.Manifested.Sandbox.Package.Install.Existing.ps1

<#
    Eigenverft.Manifested.Sandbox.Package.Install — existing-install discovery, registry probe, and reuse/adopt decisions.
    Dot-sourced from Eigenverft.Manifested.Sandbox.psm1 (mirrored in TestImports.ps1) before Package.Install.ps1.
#>


function Resolve-PackageExistingInstallRoot {
<#
.SYNOPSIS
Resolves an install directory from a discovered existing-install candidate path.
 
.DESCRIPTION
Uses the existing-install root rules to turn a discovered file path such as
`code.cmd` into the install directory that owns that file.
 
.PARAMETER ExistingInstallDiscovery
The existing-install discovery definition object.
 
.PARAMETER CandidatePath
The discovered file or directory path.
 
.EXAMPLE
Resolve-PackageExistingInstallRoot -ExistingInstallDiscovery $package.existingInstallDiscovery -CandidatePath $candidatePath
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$ExistingInstallDiscovery,

        [Parameter(Mandatory = $true)]
        [string]$CandidatePath
    )

    if (Test-Path -LiteralPath $CandidatePath -PathType Container) {
        return (Resolve-Path -LiteralPath $CandidatePath -ErrorAction Stop).Path
    }

    $leafName = Split-Path -Leaf $CandidatePath
    foreach ($rule in @($ExistingInstallDiscovery.installRootRules)) {
        if (-not $rule.PSObject.Properties['match'] -or $null -eq $rule.match) {
            continue
        }

        $matchKind = if ($rule.match.PSObject.Properties['kind']) { [string]$rule.match.kind } else { $null }
        $matchValue = if ($rule.match.PSObject.Properties['value']) { [string]$rule.match.value } else { $null }
        if ([string]::Equals($matchKind, 'fileName', [System.StringComparison]::OrdinalIgnoreCase) -and
            [string]::Equals($matchValue, $leafName, [System.StringComparison]::OrdinalIgnoreCase)) {
            $candidateDirectory = Split-Path -Parent $CandidatePath
            $installRootRelativePath = if ($rule.PSObject.Properties['installRootRelativePath']) { [string]$rule.installRootRelativePath } else { '.' }
            return [System.IO.Path]::GetFullPath((Join-Path $candidateDirectory $installRootRelativePath))
        }
    }

    return (Split-Path -Parent $CandidatePath)
}

function Resolve-PackageExistingUninstallRegistryCandidate {
<#
.SYNOPSIS
Resolves an existing-install candidate from Windows uninstall registry keys.
 
.DESCRIPTION
Keeps Package JSON mapping separate from the generic registry helpers. The
search location provides concrete registry paths and the path source that should
be interpreted as the install directory.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$SearchLocation
    )

    if (-not $SearchLocation.PSObject.Properties['paths'] -or @($SearchLocation.paths).Count -eq 0) {
        throw "Package existingInstallDiscovery windowsUninstallRegistryKey search is missing paths."
    }
    if (-not $SearchLocation.PSObject.Properties['installDirectorySource'] -or [string]::IsNullOrWhiteSpace([string]$SearchLocation.installDirectorySource)) {
        throw "Package existingInstallDiscovery windowsUninstallRegistryKey search is missing installDirectorySource."
    }

    foreach ($registryPath in @($SearchLocation.paths | ForEach-Object { [string]$_ } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })) {
        $entry = Get-WindowsUninstallRegistryEntry -Path $registryPath
        if (-not $entry -or -not [string]::Equals([string]$entry.Status, 'Ready', [System.StringComparison]::OrdinalIgnoreCase)) {
            continue
        }

        $pathResolution = Resolve-WindowsUninstallRegistryEntryPath -Entry $entry -Source ([string]$SearchLocation.installDirectorySource)
        if (-not $pathResolution -or -not [string]::Equals([string]$pathResolution.Status, 'Ready', [System.StringComparison]::OrdinalIgnoreCase)) {
            continue
        }

        if (Test-Path -LiteralPath $pathResolution.ResolvedPath -PathType Container) {
            return [pscustomobject]@{
                CandidatePath     = $pathResolution.ResolvedPath
                RegistryEntry     = $entry
                PathResolution    = $pathResolution
            }
        }
    }

    return $null
}

function Get-PackageExistingInstallSearchLocations {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [object[]]$SearchLocations
    )

    $indexedLocations = New-Object System.Collections.Generic.List[object]
    $index = 0
    foreach ($searchLocation in @($SearchLocations)) {
        if ($null -eq $searchLocation) {
            continue
        }
        $indexedLocations.Add([pscustomobject]@{
            SearchLocation = $searchLocation
            SearchOrder    = if ($searchLocation.PSObject.Properties['searchOrder']) { [int]$searchLocation.searchOrder } else { [int]::MaxValue }
            Index          = $index
        }) | Out-Null
        $index++
    }

    return @($indexedLocations.ToArray() | Sort-Object -Property SearchOrder, Index | ForEach-Object { $_.SearchLocation })
}

function Find-PackageExistingPackage {
<#
.SYNOPSIS
Finds an existing package install that may be reused or adopted.
 
.DESCRIPTION
Searches command, path, and directory candidates from the release
existingInstallDiscovery block and attaches the first matching install
directory to the Package result.
 
.PARAMETER PackageResult
The Package result object to enrich.
 
.EXAMPLE
Find-PackageExistingPackage -PackageResult $result
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    if (-not [string]::IsNullOrWhiteSpace([string]$PackageResult.InstallDirectory) -and
        (Test-Path -LiteralPath $PackageResult.InstallDirectory -PathType Container)) {
        $resolvedPackageOwnedInstallDirectory = [System.IO.Path]::GetFullPath([string]$PackageResult.InstallDirectory)
        $PackageResult.ExistingPackage = [pscustomobject]@{
            SearchKind       = 'packageTargetInstallPath'
            CandidatePath    = $resolvedPackageOwnedInstallDirectory
            InstallDirectory = $resolvedPackageOwnedInstallDirectory
            Decision         = 'Pending'
            Validation       = $null
            Classification   = $null
            OwnershipRecord  = $null
        }
        Write-PackageExecutionMessage -Message ("[DISCOVERY] Found Package target install directory '{0}'." -f $resolvedPackageOwnedInstallDirectory)
        return $PackageResult
    }

    $package = $PackageResult.Package
    if (-not $package -or -not $package.PSObject.Properties['existingInstallDiscovery'] -or $null -eq $package.existingInstallDiscovery) {
        return $PackageResult
    }

    $existingInstallDiscovery = $package.existingInstallDiscovery
    if ($existingInstallDiscovery.PSObject.Properties['enableDetection'] -and (-not [bool]$existingInstallDiscovery.enableDetection)) {
        return $PackageResult
    }

    foreach ($searchLocation in @(Get-PackageExistingInstallSearchLocations -SearchLocations @($existingInstallDiscovery.searchLocations))) {
        $candidatePath = $null
        $discoveryDetails = $null
        switch -Exact ([string]$searchLocation.kind) {
            'command' {
                if (-not $searchLocation.PSObject.Properties['name'] -or [string]::IsNullOrWhiteSpace([string]$searchLocation.name)) {
                    throw "Package existingInstallDiscovery search for release '$($package.id)' is missing command name."
                }
                $candidatePath = Get-ResolvedApplicationPath -CommandName ([string]$searchLocation.name)
            }
            'path' {
                if (-not $searchLocation.PSObject.Properties['path'] -or [string]::IsNullOrWhiteSpace([string]$searchLocation.path)) {
                    throw "Package existingInstallDiscovery search for release '$($package.id)' is missing path."
                }
                $resolvedPath = Resolve-PackagePathValue -PathValue ([string]$searchLocation.path)
                if (Test-Path -LiteralPath $resolvedPath) {
                    $candidatePath = $resolvedPath
                }
            }
            'directory' {
                if (-not $searchLocation.PSObject.Properties['path'] -or [string]::IsNullOrWhiteSpace([string]$searchLocation.path)) {
                    throw "Package existingInstallDiscovery search for release '$($package.id)' is missing directory path."
                }
                $resolvedPath = Resolve-PackagePathValue -PathValue ([string]$searchLocation.path)
                if (Test-Path -LiteralPath $resolvedPath -PathType Container) {
                    $candidatePath = $resolvedPath
                }
            }
            'windowsUninstallRegistryKey' {
                $registryCandidate = Resolve-PackageExistingUninstallRegistryCandidate -SearchLocation $searchLocation
                if ($registryCandidate) {
                    $candidatePath = $registryCandidate.CandidatePath
                    $discoveryDetails = $registryCandidate
                }
            }
            default {
                throw "Unsupported Package existingInstallDiscovery search kind '$($searchLocation.kind)'."
            }
        }

        if ([string]::IsNullOrWhiteSpace($candidatePath)) {
            continue
        }

        $installDirectory = Resolve-PackageExistingInstallRoot -ExistingInstallDiscovery $existingInstallDiscovery -CandidatePath $candidatePath
        if (-not (Test-Path -LiteralPath $installDirectory -PathType Container)) {
            continue
        }

        $PackageResult.ExistingPackage = [pscustomobject]@{
            SearchKind       = $searchLocation.kind
            CandidatePath    = $candidatePath
            InstallDirectory = $installDirectory
            Decision         = 'Pending'
            Validation       = $null
            Classification   = $null
            OwnershipRecord  = $null
            DiscoveryDetails = $discoveryDetails
        }
        Write-PackageExecutionMessage -Message ("[DISCOVERY] Found existing package candidate '{0}' via '{1}'." -f $candidatePath, $searchLocation.kind)
        return $PackageResult
    }

    return $PackageResult
}

function Resolve-PackageExistingPackageDecision {
<#
.SYNOPSIS
Evaluates how Package should react to a discovered existing install.
 
.DESCRIPTION
Validates the discovered install, combines the result with ownership
classification and release-specific policy switches, and records whether the
current run should reuse, adopt, ignore, or replace the install.
 
.PARAMETER PackageResult
The Package result object to enrich.
 
.EXAMPLE
Resolve-PackageExistingPackageDecision -PackageResult $result
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$PackageResult
    )

    if (-not $PackageResult.ExistingPackage) {
        return $PackageResult
    }

    $package = $PackageResult.Package
    $existingInstallPolicy = if ($package.PSObject.Properties['existingInstallPolicy']) { $package.existingInstallPolicy } else { [pscustomobject]@{} }
    $originalInstallDirectory = $PackageResult.InstallDirectory
    $PackageResult.InstallDirectory = $PackageResult.ExistingPackage.InstallDirectory
    $PackageResult = Test-PackageInstalledPackage -PackageResult $PackageResult
    $PackageResult.ExistingPackage.Validation = $PackageResult.Validation

    if (-not $PackageResult.Validation.Accepted) {
        $PackageResult.ExistingPackage.Decision = 'ExistingInstallValidationFailed'
        $PackageResult.InstallDirectory = $originalInstallDirectory
        $PackageResult.Validation = $null
        return $PackageResult
    }

    $ownershipRecord = if ($PackageResult.Ownership -and $PackageResult.Ownership.OwnershipRecord) {
        $PackageResult.Ownership.OwnershipRecord
    }
    else {
        $null
    }

    $classification = if ($PackageResult.Ownership -and $PackageResult.Ownership.Classification) {
        [string]$PackageResult.Ownership.Classification
    }
    else {
        'ExternalInstall'
    }

    $allowAdoptExternal = $false
    if ($existingInstallPolicy.PSObject.Properties['allowAdoptExternal']) {
        $allowAdoptExternal = [bool]$existingInstallPolicy.allowAdoptExternal
    }

    $upgradeAdoptedInstall = $false
    if ($existingInstallPolicy.PSObject.Properties['upgradeAdoptedInstall']) {
        $upgradeAdoptedInstall = [bool]$existingInstallPolicy.upgradeAdoptedInstall
    }

    $requirePackageOwnership = $false
    if ($existingInstallPolicy.PSObject.Properties['requirePackageOwnership']) {
        $requirePackageOwnership = [bool]$existingInstallPolicy.requirePackageOwnership
    }

    $sameRelease = $false
    if ($ownershipRecord) {
        $sameRelease = [string]::Equals([string]$ownershipRecord.currentReleaseId, [string]$PackageResult.PackageId, [System.StringComparison]::OrdinalIgnoreCase) -and
            [string]::Equals([string]$ownershipRecord.currentVersion, [string]$PackageResult.PackageVersion, [System.StringComparison]::OrdinalIgnoreCase)
    }

    if ([string]::Equals($classification, 'PackageTarget', [System.StringComparison]::OrdinalIgnoreCase) -and -not $ownershipRecord) {
        $PackageResult.ExistingPackage.Decision = 'ReusePackageOwned'
        $PackageResult.InstallOrigin = 'PackageReused'
        Write-PackageExecutionMessage -Message ("[DECISION] Reusing Package-owned target install '{0}'." -f $PackageResult.ExistingPackage.InstallDirectory)
        Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}' with installOrigin='{1}'." -f $PackageResult.ExistingPackage.Decision, $PackageResult.InstallOrigin)
        return $PackageResult
    }

    if ([string]::Equals($classification, 'PackageTarget', [System.StringComparison]::OrdinalIgnoreCase) -and $ownershipRecord) {
        if ([string]::Equals([string]$ownershipRecord.ownershipKind, 'AdoptedExternal', [System.StringComparison]::OrdinalIgnoreCase)) {
            if ($sameRelease -or (-not $upgradeAdoptedInstall)) {
                $PackageResult.ExistingPackage.Decision = 'AdoptExternal'
                $PackageResult.InstallOrigin = 'AdoptedExternal'
                Write-PackageExecutionMessage -Message ("[DECISION] Reusing adopted external install '{0}'." -f $PackageResult.ExistingPackage.InstallDirectory)
                Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}' with installOrigin='{1}'." -f $PackageResult.ExistingPackage.Decision, $PackageResult.InstallOrigin)
                return $PackageResult
            }

            $PackageResult.ExistingPackage.Decision = 'UpgradeAdoptedInstall'
            $PackageResult.InstallDirectory = $originalInstallDirectory
            $PackageResult.Validation = $null
            Write-PackageExecutionMessage -Level 'WRN' -Message ("[DECISION] Replacing adopted install at '{0}' with a Package-owned install." -f $PackageResult.ExistingPackage.InstallDirectory)
            Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}'." -f $PackageResult.ExistingPackage.Decision)
            return $PackageResult
        }

        if ($sameRelease) {
            $PackageResult.ExistingPackage.Decision = 'ReusePackageOwned'
            $PackageResult.InstallOrigin = 'PackageReused'
            Write-PackageExecutionMessage -Message ("[DECISION] Reusing Package-owned install '{0}'." -f $PackageResult.ExistingPackage.InstallDirectory)
            Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}' with installOrigin='{1}'." -f $PackageResult.ExistingPackage.Decision, $PackageResult.InstallOrigin)
            return $PackageResult
        }

        $PackageResult.ExistingPackage.Decision = 'ReplacePackageOwnedInstall'
        $PackageResult.InstallDirectory = $originalInstallDirectory
        $PackageResult.Validation = $null
        Write-PackageExecutionMessage -Level 'WRN' -Message ("[DECISION] Replacing outdated Package-owned install at '{0}'." -f $PackageResult.ExistingPackage.InstallDirectory)
        Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}'." -f $PackageResult.ExistingPackage.Decision)
        return $PackageResult
    }

    if ($requirePackageOwnership) {
        $PackageResult.ExistingPackage.Decision = 'ExternalIgnored'
        $PackageResult.InstallDirectory = $originalInstallDirectory
        $PackageResult.Validation = $null
        Write-PackageExecutionMessage -Level 'WRN' -Message ("[DECISION] Ignoring external install '{0}' because Package ownership is required." -f $PackageResult.ExistingPackage.InstallDirectory)
        Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}'." -f $PackageResult.ExistingPackage.Decision)
        return $PackageResult
    }

    if ($allowAdoptExternal) {
        $PackageResult.ExistingPackage.Decision = 'AdoptExternal'
        $PackageResult.InstallOrigin = 'AdoptedExternal'
        Write-PackageExecutionMessage -Message ("[DECISION] Adopting external install '{0}'." -f $PackageResult.ExistingPackage.InstallDirectory)
        Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}' with installOrigin='{1}'." -f $PackageResult.ExistingPackage.Decision, $PackageResult.InstallOrigin)
        return $PackageResult
    }

    $PackageResult.ExistingPackage.Decision = 'ExternalIgnored'
    $PackageResult.InstallDirectory = $originalInstallDirectory
    $PackageResult.Validation = $null
    Write-PackageExecutionMessage -Level 'WRN' -Message ("[DECISION] Ignoring external install '{0}'." -f $PackageResult.ExistingPackage.InstallDirectory)
    Write-PackageExecutionMessage -Message ("[STATE] Existing install decision resolved to '{0}'." -f $PackageResult.ExistingPackage.Decision)
    return $PackageResult
}