functions/private/Read-KlippyPrinterRegistry.ps1

function Read-KlippyPrinterRegistry {
    <#
    .SYNOPSIS
        Reads the printer registry from disk.

    .DESCRIPTION
        Reads and validates the printers.json registry file.
        Returns an empty array if the file doesn't exist.
        Validates schema on read and throws on invalid data.

    .OUTPUTS
        System.Collections.Generic.List[PSCustomObject] - Array of printer entries.
    #>

    [CmdletBinding()]
    [OutputType([System.Collections.Generic.List[PSCustomObject]])]
    param()

    $registryFile = Get-KlippyRegistryPath -FileOnly

    # Return empty list if file doesn't exist
    if (-not (Test-Path -Path $registryFile -PathType Leaf)) {
        Write-Verbose "Registry file not found, returning empty list"
        return , [System.Collections.Generic.List[PSCustomObject]]::new()
    }

    try {
        $content = Get-Content -Path $registryFile -Raw -ErrorAction Stop

        # Handle empty file
        if ([string]::IsNullOrWhiteSpace($content)) {
            Write-Verbose "Registry file is empty, returning empty list"
            return , [System.Collections.Generic.List[PSCustomObject]]::new()
        }

        $printers = $content | ConvertFrom-Json -ErrorAction Stop
    }
    catch {
        throw "Failed to read or parse registry file '$registryFile': $_"
    }

    # Ensure we have an array
    if ($null -eq $printers) {
        return , [System.Collections.Generic.List[PSCustomObject]]::new()
    }

    # Convert single object to array
    if ($printers -isnot [System.Array]) {
        $printers = @($printers)
    }

    # Validate schema for each printer entry
    $validatedPrinters = [System.Collections.Generic.List[PSCustomObject]]::new()
    $requiredProperties = @('Id', 'PrinterName', 'Uri', 'IsDefault', 'CreatedAt', 'ModifiedAt')

    foreach ($printer in $printers) {
        # Check required properties
        foreach ($prop in $requiredProperties) {
            if (-not ($printer.PSObject.Properties.Name -contains $prop)) {
                throw "Invalid printer entry: missing required property '$prop'"
            }
        }

        # Validate Id is a valid GUID
        $guidResult = [System.Guid]::Empty
        if (-not [System.Guid]::TryParse($printer.Id, [ref]$guidResult)) {
            throw "Invalid printer entry: 'Id' must be a valid GUID (got: $($printer.Id))"
        }

        # Validate PrinterName is not empty
        if ([string]::IsNullOrWhiteSpace($printer.PrinterName)) {
            throw "Invalid printer entry: 'PrinterName' cannot be empty"
        }

        # Validate Uri is a valid URI
        $uriResult = $null
        if (-not [System.Uri]::TryCreate($printer.Uri, [System.UriKind]::Absolute, [ref]$uriResult)) {
            throw "Invalid printer entry: 'Uri' must be a valid absolute URI (got: $($printer.Uri))"
        }

        # Validate IsDefault is boolean
        if ($printer.IsDefault -isnot [bool]) {
            throw "Invalid printer entry: 'IsDefault' must be a boolean"
        }

        # Create normalized printer object with proper types
        $validatedPrinter = [PSCustomObject]@{
            PSTypeName   = 'KlippyCLI.Printer'
            Id           = $printer.Id
            PrinterName  = $printer.PrinterName
            Uri          = $printer.Uri
            ApiKey       = $printer.ApiKey
            Description  = $printer.Description
            IsDefault    = [bool]$printer.IsDefault
            CreatedAt    = [datetime]$printer.CreatedAt
            ModifiedAt   = [datetime]$printer.ModifiedAt
        }

        $validatedPrinters.Add($validatedPrinter)
    }

    return , $validatedPrinters
}