functions/public/Copy-KlippyGcodeItem.ps1

function Copy-KlippyGcodeItem {
    <#
    .SYNOPSIS
        Copies a G-code file or folder on a Klipper printer.

    .DESCRIPTION
        Creates a copy of a file or folder within the printer's gcodes storage.
        Supports pipeline input from Get-KlippyGcodeFile or Get-KlippyGcodeFolder.

    .PARAMETER Id
        The unique identifier of the printer.

    .PARAMETER PrinterName
        The friendly name of the printer.

    .PARAMETER InputObject
        A GcodeFile or GcodeFolder object from pipeline input.

    .PARAMETER Path
        Source path of the item to copy.

    .PARAMETER Destination
        Destination path for the copy.

    .PARAMETER PassThru
        Return the copied item object.

    .EXAMPLE
        Copy-KlippyGcodeItem -Path "benchy.gcode" -Destination "benchy_backup.gcode"
        Creates a copy with a new name.

    .EXAMPLE
        Copy-KlippyGcodeItem -Path "model.gcode" -Destination "archive/"
        Copies a file to a subfolder keeping the same name.

    .EXAMPLE
        Get-KlippyGcodeFile -Path "important.gcode" | Copy-KlippyGcodeItem -Destination "backup/"
        Copies a file via pipeline.

    .OUTPUTS
        PSCustomObject with item information (when -PassThru is used).
    #>

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

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

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

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

        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string]$Destination,

        [Parameter()]
        [switch]$PassThru
    )

    process {
        # Determine source path and printer
        if ($PSCmdlet.ParameterSetName -eq 'ByObject') {
            $printer = Resolve-KlippyPrinterTarget -Id $InputObject.PrinterId
            $sourcePath = $InputObject.Path
            $isDirectory = $InputObject.PSObject.TypeNames -contains 'KlippyCLI.GcodeFolder'
        }
        else {
            # Resolve printer
            $resolveParams = @{}
            switch ($PSCmdlet.ParameterSetName) {
                'ById' { $resolveParams['Id'] = $Id }
                'ByName' { $resolveParams['PrinterName'] = $PrinterName }
            }

            $printer = Resolve-KlippyPrinterTarget @resolveParams
            $sourcePath = $Path.TrimStart('/')
            $isDirectory = $false  # Will be determined by API
        }

        # Clean destination path
        $destPath = $Destination.TrimStart('/')

        # If destination ends with /, append source filename
        if ($destPath.EndsWith('/')) {
            $destPath = $destPath + (Split-Path $sourcePath -Leaf)
        }

        if ($PSCmdlet.ShouldProcess("$($printer.PrinterName):gcodes/$sourcePath -> gcodes/$destPath", "Copy item")) {
            try {
                Write-Verbose "[$($printer.PrinterName)] Copying: $sourcePath -> $destPath"

                $response = Invoke-KlippyHttpRequest -Printer $printer -Endpoint "server/files/copy" -Method POST -QueryParameters @{
                    source = "gcodes/$sourcePath"
                    dest   = "gcodes/$destPath"
                }

                Write-Verbose "[$($printer.PrinterName)] Item copied successfully"

                if ($PassThru) {
                    # Determine the actual result type
                    $resultItem = $response.Item
                    if ($resultItem.Path) {
                        $actualPath = $resultItem.Path -replace '^gcodes/', ''
                    }
                    else {
                        $actualPath = $destPath
                    }

                    if ($isDirectory -or ($resultItem -and $resultItem.Dirname)) {
                        [PSCustomObject]@{
                            PSTypeName  = 'KlippyCLI.GcodeFolder'
                            PrinterId   = $printer.Id
                            PrinterName = $printer.PrinterName
                            Path        = $actualPath
                            Name        = Split-Path $actualPath -Leaf
                            Modified    = Get-Date
                        }
                    }
                    else {
                        [PSCustomObject]@{
                            PSTypeName  = 'KlippyCLI.GcodeFile'
                            PrinterId   = $printer.Id
                            PrinterName = $printer.PrinterName
                            Path        = $actualPath
                            Name        = Split-Path $actualPath -Leaf
                            Size        = $resultItem.Size
                            SizeMB      = if ($resultItem.Size) { [Math]::Round($resultItem.Size / 1MB, 2) } else { $null }
                            Modified    = Get-Date
                        }
                    }
                }
            }
            catch {
                Write-Error "[$($printer.PrinterName)] Failed to copy '$sourcePath': $_"
            }
        }
    }
}