#Requires -Version 7.4

function Get-vSEALRegistryKey {
    <#
    .SYNOPSIS
        Gets registry key values from the Windows registry.

    .DESCRIPTION
        Retrieves registry key values with support for WOW6432Node redirection
        and user-specific registry access via SID. Uses Convert-vSEALRegistryPath
        internally for path normalization.

    .PARAMETER LiteralPath
        The literal registry key path to read from (e.g., HKLM:\SOFTWARE\Microsoft).
        Wildcards are not processed. This is the default parameter set.

    .PARAMETER Path
        The registry key path to read from. Supports wildcards.

    .PARAMETER Name
        The name of the specific value to retrieve. If not specified, returns the
        specific value (unlike "*" which returns all values).

    .PARAMETER Wow6432Node
        Access the 32-bit registry view on 64-bit systems.

    .PARAMETER SID
        User SID for accessing user-specific registry (converts HKCU to HKU\SID).

    .PARAMETER ReturnEmptyKeyIfExists
        If the registry key exists but has no values, return the key object instead of $null.

    .PARAMETER DoNotExpandEnvironmentNames
        Do not expand environment variables in ExpandString (REG_EXPAND_SZ) values.

    .OUTPUTS
        [System.Object] The registry value(s) or $null if not found.

    .EXAMPLE
        Get-vSEALRegistryKey -LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion' -Name 'ProgramFilesDir'
        Returns the value of ProgramFilesDir.

    .EXAMPLE
        Get-vSEALRegistryKey -Key 'HKLM:\SOFTWARE\Microsoft' -Wow6432Node
        Returns all values from the 32-bit view of the registry key.

    .EXAMPLE
        Get-vSEALRegistryKey -LiteralPath 'HKCU:\Software\Test' -SID 'S-1-5-21-123456789'
        Returns values from another user's registry hive.

    .EXAMPLE
        Get-vSEALRegistryKey -Path 'HKLM:\SOFTWARE\Microsoft\*' -Name 'Version'
        Returns the Version value from all subkeys matching the wildcard pattern.
    #>
    [CmdletBinding(DefaultParameterSetName = 'LiteralPath')]
    [OutputType([System.Object])]
    param(
        [Parameter(Mandatory, Position = 0, ParameterSetName = 'LiteralPath')]
        [ValidateNotNullOrEmpty()]
        [Alias('Key')]
        [string]$LiteralPath,

        [Parameter(Mandatory, Position = 0, ParameterSetName = 'Path')]
        [ValidateNotNullOrEmpty()]
        [SupportsWildcards()]
        [string]$Path,

        [Parameter(Position = 1)]
        [string]$Name,

        [Parameter(Position = 2)]
        [switch]$Wow6432Node,

        [Parameter()]
        [string]$SID,

        [Parameter()]
        [switch]$ReturnEmptyKeyIfExists,

        [Parameter()]
        [switch]$DoNotExpandEnvironmentNames
    )

    # Determine which path to use
    $registryPath = if ($PSCmdlet.ParameterSetName -eq 'Path') { $Path } else { $LiteralPath }

    # Convert the registry path using the helper function
    $convertParams = @{
        Key = $registryPath
    }
    if ($Wow6432Node) {
        $convertParams['Wow6432Node'] = $true
    }
    if ($SID) {
        $convertParams['SID'] = $SID
    }

    try {
        $convertedPath = Convert-vSEALRegistryPath @convertParams
    }
    catch {
        Write-vSEALLog -Message "Failed to convert registry path '$registryPath': $($_.Exception.Message)" -Level 'ERROR'
        return $null
    }

    Write-vSEALLog -Message "Reading registry key: $convertedPath" -Level 'VERBOSE'

    # Handle wildcard paths
    if ($PSCmdlet.ParameterSetName -eq 'Path' -and [System.Management.Automation.WildcardPattern]::ContainsWildcardCharacters($Path)) {
        $matchingPaths = Get-Item -Path $convertedPath -ErrorAction SilentlyContinue
        if (-not $matchingPaths) {
            Write-vSEALLog -Message "No registry keys matched pattern: $convertedPath" -Level 'VERBOSE'
            return $null
        }

        $results = @()
        foreach ($matchingPath in $matchingPaths) {
            $result = Get-vSEALRegistryKeyInternal -ConvertedPath $matchingPath.PSPath -Name $Name `
                -ReturnEmptyKeyIfExists:$ReturnEmptyKeyIfExists -DoNotExpandEnvironmentNames:$DoNotExpandEnvironmentNames
            if ($result) {
                $results += $result
            }
        }
        return $results
    }

    return Get-vSEALRegistryKeyInternal -ConvertedPath $convertedPath -Name $Name `
        -ReturnEmptyKeyIfExists:$ReturnEmptyKeyIfExists -DoNotExpandEnvironmentNames:$DoNotExpandEnvironmentNames
}

function Get-vSEALRegistryKeyInternal {
    param(
        [string]$ConvertedPath,
        [string]$Name,
        [switch]$ReturnEmptyKeyIfExists,
        [switch]$DoNotExpandEnvironmentNames
    )

    # Check if the key exists
    if (-not (Test-Path -LiteralPath $ConvertedPath)) {
        Write-vSEALLog -Message "Registry key not found: $ConvertedPath" -Level 'VERBOSE'
        return $null
    }

    try {
        if ([string]::IsNullOrEmpty($Name)) {
            # Get all values from the key when Name is not specified
            $result = Get-ItemProperty -LiteralPath $ConvertedPath -ErrorAction Stop
            if ($null -eq $result -or ($result.PSObject.Properties | Where-Object { $_.Name -notlike 'PS*' }).Count -eq 0) {
                if ($ReturnEmptyKeyIfExists) {
                    Write-vSEALLog -Message "Registry key exists but is empty, returning key object: $ConvertedPath" -Level 'VERBOSE'
                    return Get-Item -LiteralPath $ConvertedPath
                }
                Write-vSEALLog -Message "Registry key has no values: $ConvertedPath" -Level 'VERBOSE'
                return $null
            }
            Write-vSEALLog -Message "Retrieved all values from registry key: $ConvertedPath" -Level 'VERBOSE'
            return $result
        }
        else {
            # Get specific value
            $getParams = @{
                LiteralPath = $ConvertedPath
                Name        = $Name
                ErrorAction = 'Stop'
            }

            $result = Get-ItemProperty @getParams

            # Get the value, handling environment variable expansion
            $value = $result.$Name

            if ($DoNotExpandEnvironmentNames) {
                # Re-read without expansion using .NET registry API
                try {
                    $regKey = Get-Item -LiteralPath $ConvertedPath
                    $value = $regKey.GetValue($Name, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
                }
                catch {
                    Write-vSEALLog -Message "Failed to read value without expansion, using default value" -Level 'VERBOSE'
                }
            }

            Write-vSEALLog -Message "Retrieved value '$Name' from registry key: $ConvertedPath" -Level 'VERBOSE'
            return $value
        }
    }
    catch [System.Management.Automation.ItemNotFoundException] {
        Write-vSEALLog -Message "Registry value '$Name' not found in key: $ConvertedPath" -Level 'VERBOSE'
        return $null
    }
    catch {
        Write-vSEALLog -Message "Error reading registry key '$ConvertedPath': $($_.Exception.Message)" -Level 'ERROR'
        return $null
    }
}
