Public/Get-PathAnalysis.ps1

function Get-PathAnalysis {
    <#
    .SYNOPSIS
        Analyzes the Windows PATH environment variable for issues.
         
    .DESCRIPTION
        Scans both User and Machine PATH entries to detect:
        - Paths that no longer exist (obsolete)
        - Duplicate entries
        - Invalid paths
         
    .PARAMETER Target
        Which PATH to analyze: User, Machine, or Both. Default is Both.
         
    .PARAMETER IssuesOnly
        If specified, only returns entries with issues.
         
    .EXAMPLE
        Get-PathAnalysis
         
    .EXAMPLE
        Get-PathAnalysis -Target User -IssuesOnly
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param(
        [ValidateSet('User', 'Machine', 'Both')]
        [string]$Target = 'Both',
        
        [switch]$IssuesOnly
    )

    $results = @()
    $entries = Get-PathEntries -Target Both  # Always get both for cross-scope detection
    $allPaths = @()

    # Find cross-scope duplicates (User paths that exist in Machine)
    $crossScopeDuplicates = Find-CrossScopeDuplicates -UserPaths $entries.User -MachinePaths $entries.Machine
    $crossScopeUserPaths = @{}
    foreach ($dup in $crossScopeDuplicates) {
        $normalized = $dup.ExpandedPath
        $crossScopeUserPaths[$normalized] = $dup.MachinePath
    }

    # Process User paths (only if requested)
    if ($Target -eq 'User' -or $Target -eq 'Both') {
        foreach ($path in $entries.User) {
            $allPaths += @{
                Path  = $path
                Scope = 'User'
            }
        }
    }

    # Process Machine paths (only if requested)
    if ($Target -eq 'Machine' -or $Target -eq 'Both') {
        foreach ($path in $entries.Machine) {
            $allPaths += @{
                Path  = $path
                Scope = 'Machine'
            }
        }
    }

    # Track seen paths for duplicate detection
    $seenPaths = @{}

    foreach ($item in $allPaths) {
        $testResult = Test-PathEntry -Path $item.Path
        $normalizedPath = $testResult.Expanded.TrimEnd('\').ToLowerInvariant()
        
        # Determine issues
        $issues = @()
        $status = '✓'
        
        if (-not $testResult.Exists) {
            $issues += 'Does not exist'
            $status = '✗'
        }
        
        if ($seenPaths.ContainsKey($normalizedPath)) {
            $issues += "Duplicate of: $($seenPaths[$normalizedPath])"
            $status = '⚠'
        }
        else {
            $seenPaths[$normalizedPath] = $item.Path
        }

        # Check for cross-scope redundancy (User paths that exist in Machine)
        if ($item.Scope -eq 'User' -and $crossScopeUserPaths.ContainsKey($normalizedPath)) {
            $issues += "Redundant (exists in Machine PATH)"
            if ($status -eq '✓') { $status = '⚠' }
        }
        
        $resultItem = [PSCustomObject]@{
            Status       = $status
            Path         = $item.Path
            Scope        = $item.Scope
            Exists       = $testResult.Exists
            Issue        = if ($issues.Count -gt 0) { $issues -join '; ' } else { '-' }
            ExpandedPath = $testResult.Expanded
        }
        
        if ($IssuesOnly) {
            if ($issues.Count -gt 0) {
                $results += $resultItem
            }
        }
        else {
            $results += $resultItem
        }
    }

    # Add summary info
    $userCharCount = Get-PathCharacterCount -Paths $entries.User
    $machineCharCount = Get-PathCharacterCount -Paths $entries.Machine
    $crossScopeCount = $crossScopeDuplicates.Count
    
    Write-Verbose "User PATH: $($entries.User.Count) entries, $userCharCount characters"
    Write-Verbose "Machine PATH: $($entries.Machine.Count) entries, $machineCharCount characters"
    Write-Verbose "Cross-scope redundant entries: $crossScopeCount"
    Write-Verbose "Total issues found: $(($results | Where-Object { $_.Status -ne '✓' }).Count)"

    return $results
}