functions/public/Wait-KlippyPrintFinished.ps1

function Wait-KlippyPrintFinished {
    <#
    .SYNOPSIS
        Waits for a print job to finish on a Klipper printer.

    .DESCRIPTION
        Monitors the print_stats state via WebSocket and waits until the
        print completes, is cancelled, or errors out.

    .PARAMETER Id
        The unique identifier of the printer.

    .PARAMETER PrinterName
        The friendly name of the printer.

    .PARAMETER InputObject
        A printer object from pipeline input.

    .PARAMETER Timeout
        Maximum time to wait in seconds. Default is 86400 (24 hours).

    .PARAMETER ShowProgress
        Display print progress updates.

    .PARAMETER Quiet
        Suppress all progress messages.

    .EXAMPLE
        Wait-KlippyPrintFinished -PrinterName "voronv2" -ShowProgress
        Waits for the current print to finish with progress updates.

    .EXAMPLE
        Start-KlippyPrint "test.gcode" | Wait-KlippyPrintFinished -Timeout 3600
        Starts a print and waits up to 1 hour for it to finish.

    .OUTPUTS
        PSCustomObject with print completion information.
    #>

    [CmdletBinding(DefaultParameterSetName = 'Default')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory = $true, ParameterSetName = 'ById')]
        [ValidateNotNullOrEmpty()]
        [string]$Id,

        [Parameter(Mandatory = $true, ParameterSetName = 'ByName', Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string]$PrinterName,

        [Parameter(Mandatory = $true, ParameterSetName = 'ByObject', ValueFromPipeline = $true)]
        [PSCustomObject]$InputObject,

        [Parameter()]
        [ValidateRange(1, 604800)]  # Up to 1 week
        [int]$Timeout = 86400,

        [Parameter()]
        [switch]$ShowProgress,

        [Parameter()]
        [switch]$Quiet
    )

    process {
        # Resolve printer
        $resolveParams = @{}
        switch ($PSCmdlet.ParameterSetName) {
            'ById' { $resolveParams['Id'] = $Id }
            'ByName' { $resolveParams['PrinterName'] = $PrinterName }
            'ByObject' { $resolveParams['InputObject'] = $InputObject }
        }

        $printer = Resolve-KlippyPrinterTarget @resolveParams

        # Terminal states
        $terminalStates = @('complete', 'cancelled', 'error', 'standby')

        # Check current state
        try {
            $printStats = Get-KlippyObject -PrinterName $printer.PrinterName -ObjectName "print_stats"
            if ($printStats.State -in $terminalStates) {
                if (-not $Quiet) {
                    Write-Host "[$($printer.PrinterName)] Print already finished. State: $($printStats.State)"
                }
                return [PSCustomObject]@{
                    PSTypeName     = 'KlippyCLI.WaitResult'
                    PrinterId      = $printer.Id
                    PrinterName    = $printer.PrinterName
                    State          = $printStats.State
                    Filename       = $printStats.Filename
                    PrintDuration  = [TimeSpan]::FromSeconds($printStats.PrintDuration)
                    FilamentUsed   = [Math]::Round($printStats.FilamentUsed / 1000, 2)
                    WaitTime       = [TimeSpan]::Zero
                    Success        = ($printStats.State -eq 'complete')
                }
            }
        }
        catch {
            Write-Verbose "Could not get current print state: $_"
        }

        if (-not $Quiet) {
            Write-Host "[$($printer.PrinterName)] Waiting for print to finish..."
        }

        $startTime = [datetime]::UtcNow
        $ws = $null
        $lastState = 'unknown'
        $lastProgress = 0
        $filename = ''
        $printDuration = 0
        $filamentUsed = 0

        try {
            $ws = New-KlippyWebSocketClient -Printer $printer
            $null = $ws.Subscribe(@{
                print_stats    = @('state', 'filename', 'print_duration', 'filament_used')
                display_status = @('progress')
            })

            $deadline = [datetime]::UtcNow.AddSeconds($Timeout)
            $lastProgressUpdate = [datetime]::MinValue

            while ([datetime]::UtcNow -lt $deadline) {
                $message = $ws.Receive(5000)

                if ($null -ne $message -and $message.method -eq 'notify_status_update' -and $message.params) {
                    $status = $message.params[0]

                    # Update print stats
                    if ($status.print_stats) {
                        if ($status.print_stats.state) {
                            $lastState = $status.print_stats.state
                        }
                        if ($status.print_stats.filename) {
                            $filename = $status.print_stats.filename
                        }
                        if ($status.print_stats.print_duration) {
                            $printDuration = $status.print_stats.print_duration
                        }
                        if ($status.print_stats.filament_used) {
                            $filamentUsed = $status.print_stats.filament_used
                        }

                        # Check for terminal state
                        if ($lastState -in $terminalStates) {
                            $stateMessage = switch ($lastState) {
                                'complete'  { "Complete!" }
                                'cancelled' { "Cancelled" }
                                'error'     { "Error!" }
                                'standby'   { "Stopped" }
                                default     { $lastState }
                            }

                            if (-not $Quiet) {
                                Write-Host "[$($printer.PrinterName)] Print finished: $stateMessage"
                            }

                            return [PSCustomObject]@{
                                PSTypeName     = 'KlippyCLI.WaitResult'
                                PrinterId      = $printer.Id
                                PrinterName    = $printer.PrinterName
                                State          = $lastState
                                Filename       = $filename
                                PrintDuration  = [TimeSpan]::FromSeconds([int]$printDuration)
                                FilamentUsed   = [Math]::Round($filamentUsed / 1000, 2)
                                WaitTime       = [datetime]::UtcNow - $startTime
                                Success        = ($lastState -eq 'complete')
                            }
                        }
                    }

                    # Update progress
                    if ($status.display_status.progress) {
                        $lastProgress = [Math]::Round($status.display_status.progress * 100, 1)
                    }

                    # Show progress if enabled
                    if ($ShowProgress -and -not $Quiet -and ([datetime]::UtcNow - $lastProgressUpdate).TotalSeconds -ge 30) {
                        $duration = [TimeSpan]::FromSeconds([int]$printDuration)
                        Write-Host " Progress: $lastProgress% | Duration: $($duration.ToString('hh\:mm\:ss')) | State: $lastState"
                        $lastProgressUpdate = [datetime]::UtcNow
                    }
                }
            }

            # Timeout
            if (-not $Quiet) {
                Write-Host "[$($printer.PrinterName)] Timeout!"
            }

            Write-Warning "[$($printer.PrinterName)] Timeout waiting for print to finish after $Timeout seconds"

            return [PSCustomObject]@{
                PSTypeName     = 'KlippyCLI.WaitResult'
                PrinterId      = $printer.Id
                PrinterName    = $printer.PrinterName
                State          = $lastState
                Filename       = $filename
                Progress       = $lastProgress
                PrintDuration  = [TimeSpan]::FromSeconds([int]$printDuration)
                FilamentUsed   = [Math]::Round($filamentUsed / 1000, 2)
                WaitTime       = [datetime]::UtcNow - $startTime
                Success        = $false
            }
        }
        catch {
            if (-not $Quiet) {
                Write-Host "[$($printer.PrinterName)] Error!"
            }
            Write-Error "[$($printer.PrinterName)] Error waiting for print to finish: $_"

            return [PSCustomObject]@{
                PSTypeName     = 'KlippyCLI.WaitResult'
                PrinterId      = $printer.Id
                PrinterName    = $printer.PrinterName
                State          = $lastState
                Filename       = $filename
                WaitTime       = [datetime]::UtcNow - $startTime
                Success        = $false
                Error          = $_.Exception.Message
            }
        }
        finally {
            if ($ws) {
                $ws.Close()
            }
        }
    }
}