functions/private/ConvertTo-KlippyPascalCaseObject.ps1

function ConvertTo-KlippyPascalCaseObject {
    <#
    .SYNOPSIS
        Converts snake_case property names to PascalCase recursively.

    .DESCRIPTION
        Transforms Moonraker API responses from snake_case to PowerShell-style PascalCase.
        Handles nested objects, arrays, and preserves values.

    .PARAMETER InputObject
        The object to transform.

    .PARAMETER Depth
        Maximum recursion depth. Default is 20.

    .EXAMPLE
        $response | ConvertTo-KlippyPascalCaseObject

    .OUTPUTS
        PSCustomObject with PascalCase property names.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [AllowNull()]
        $InputObject,

        [Parameter()]
        [int]$Depth = 20
    )

    process {
        if ($Depth -le 0) {
            Write-Warning "Maximum recursion depth reached"
            return $InputObject
        }

        # Handle null
        if ($null -eq $InputObject) {
            return $null
        }

        # Handle primitives (string, int, bool, datetime, etc.)
        if ($InputObject -is [string] -or
            $InputObject -is [int] -or
            $InputObject -is [long] -or
            $InputObject -is [double] -or
            $InputObject -is [decimal] -or
            $InputObject -is [bool] -or
            $InputObject -is [datetime]) {
            return $InputObject
        }

        # Handle arrays
        if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string] -and $InputObject -isnot [System.Collections.IDictionary]) {
            $result = @()
            foreach ($item in $InputObject) {
                $result += ConvertTo-KlippyPascalCaseObject -InputObject $item -Depth ($Depth - 1)
            }
            return $result
        }

        # Handle dictionaries/hashtables
        if ($InputObject -is [System.Collections.IDictionary]) {
            $result = [ordered]@{}
            foreach ($key in $InputObject.Keys) {
                $pascalKey = ConvertTo-PascalCase -SnakeCase $key
                $result[$pascalKey] = ConvertTo-KlippyPascalCaseObject -InputObject $InputObject[$key] -Depth ($Depth - 1)
            }
            return [PSCustomObject]$result
        }

        # Handle PSCustomObject and other objects
        if ($InputObject -is [PSCustomObject] -or $InputObject.PSObject.Properties) {
            $result = [ordered]@{}
            foreach ($prop in $InputObject.PSObject.Properties) {
                $pascalKey = ConvertTo-PascalCase -SnakeCase $prop.Name
                $result[$pascalKey] = ConvertTo-KlippyPascalCaseObject -InputObject $prop.Value -Depth ($Depth - 1)
            }
            return [PSCustomObject]$result
        }

        # Fallback - return as-is
        return $InputObject
    }
}

function ConvertTo-PascalCase {
    <#
    .SYNOPSIS
        Converts a snake_case string to PascalCase.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [string]$SnakeCase
    )

    if ([string]::IsNullOrEmpty($SnakeCase)) {
        return $SnakeCase
    }

    # Split by underscore and capitalize each part
    $parts = $SnakeCase -split '_'
    $result = foreach ($part in $parts) {
        if ($part.Length -gt 0) {
            $part.Substring(0, 1).ToUpper() + $part.Substring(1).ToLower()
        }
    }

    return ($result -join '')
}