SendDrop.psm1

#Region './Public/Brands/Get-SendDropBrand.ps1' -1

function Get-SendDropBrand
{
    <#
    .SYNOPSIS
        Retrieves brands from the current SendDrop organization.

    .DESCRIPTION
        The Get-SendDropBrand function retrieves brands from the SendDrop API.
        When no ID is specified, it returns all brands for the current organization.
        When an ID is specified, it returns a specific brand by ID.
        It requires a valid connection established via Connect-SendDrop.

    .PARAMETER Id
        The UUID of the brand to retrieve. If not specified, all brands are returned.

    .EXAMPLE
        Get-SendDropBrand

        Retrieves all brands for the current organization.

    .EXAMPLE
        Get-SendDropBrand | Select-Object -Property Id, Name

        Retrieves all brands and displays their ID and Name properties.

    .EXAMPLE
        Get-SendDropBrand -Id "550e8400-e29b-41d4-a716-446655440000"

        Retrieves a specific brand by its ID.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Uses the API endpoints:
        - GET /api/v1/organization/brands (list all)
        - GET /api/v1/organization/brands/{brandId} (get specific)
    #>

    [CmdletBinding()]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $false,
            HelpMessage = "The UUID of the brand to retrieve.")]
        [guid]$Id
    )

    try
    {
        if ($Id)
        {
            Write-Verbose "Retrieving brand with ID: $Id"
            $endpoint = "/organization/brands/$Id"
        }
        else
        {
            Write-Verbose "Retrieving all brands from SendDrop organization."
            $endpoint = "/organization/brands"
        }

        $brands = Invoke-SendDropAPICall -Method GET -Endpoint $endpoint
        return $brands
    }
    catch
    {
        Write-Error "Failed to retrieve brands: $_"
    }
}
#EndRegion './Public/Brands/Get-SendDropBrand.ps1' 66
#Region './Public/Brands/Get-SendDropbrandLogo.ps1' -1

function Get-SendDropBrandLogo
{
    <#
    .SYNOPSIS
        Retrieves the logo for a specific SendDrop brand.

    .DESCRIPTION
        The Get-SendDropBrandLogo function retrieves the logo image for a specific brand
        by its brand ID from the SendDrop API. The logo is returned as a PNG image.
        It requires a valid connection established via Connect-SendDrop.

    .PARAMETER Id
        The UUID of the brand for which to retrieve the logo. This parameter is required.

    .PARAMETER OutPath
        Optional file path where the logo image should be saved. If not specified,
        the image bytes are returned as output.

    .EXAMPLE
        Get-SendDropBrandLogo -Id "550e8400-e29b-41d4-a716-446655440000"

        Retrieves the logo for the specified brand and returns the image bytes.

    .EXAMPLE
        Get-SendDropBrandLogo -Id "550e8400-e29b-41d4-a716-446655440000" -OutPath "C:\logo.png"

        Retrieves the logo for the specified brand and saves it to the specified file path.

    .EXAMPLE
        Get-SendDropBrand | Get-SendDropBrandLogo

        Retrieves logos for all brands by piping the results from Get-SendDropBrand.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Uses the API endpoint: GET /api/v1/organization/brands/{brandId}/logo
        Returns PNG image data.
        Supports pipeline input from Get-SendDropBrand.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param (
        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The UUID of the brand to retrieve the logo for.")]
        [guid]$Id,

        [Parameter(Mandatory = $false,
            HelpMessage = "File path where the logo image should be saved. e.g. ./logo.png")]
        [string]$OutPath
    )

    process
    {
        if (-not $Id)
        {
            Write-Error "Brand ID is required. Provide it via -Id parameter or pipeline input."
            return
        }
        $OutPath = $OutPath ? $OutPath : ("./" + (New-TemporaryFile).BaseName + ".png")

        try
        {
            Write-Verbose "Retrieving logo for brand ID: $Id"
            $endpoint = "/organization/brands/$Id/logo"

            # Get the raw response for the image
            $response = Invoke-SendDropAPICall -Method GET -Endpoint $endpoint -Raw
            Write-Verbose "Saving logo to: $OutPath"
            $response.Content | Set-Content -Path $OutPath -AsByteStream -Force
            Write-Verbose "Logo saved successfully to: $OutPath"
            return $OutPath
        }
        catch
        {
            Write-Error "Failed to retrieve brand logo: $_"
        }
    }
}
#EndRegion './Public/Brands/Get-SendDropbrandLogo.ps1' 80
#Region './Public/Connect-SendDrop.ps1' -1

function Connect-SendDrop
{
    <#
    .SYNOPSIS
        Establishes a connection to the SendDrop API using a Bearer Token.

    .DESCRIPTION
        The Connect-SendDrop function authenticates with the SendDrop API by storing the provided Bearer Token
        for use in subsequent API calls. If no token is provided, the function will prompt the user to enter it.

        The Bearer Token is stored securely in memory for the duration of the PowerShell session.

    .PARAMETER Token
        The Bearer Token to use for authentication with the SendDrop API.
        If not provided, the function will prompt for it interactively.

    .PARAMETER APIuri
        The base URI for the SendDrop API. Defaults to "https://api.senddrop.dev".

    .EXAMPLE
        Connect-SendDrop -Token "your-bearer-token-here" -APIuri "https://custom-api.senddrop.dev"

        Connects to a custom SendDrop API endpoint using the specified Bearer Token.

    .EXAMPLE
        Connect-SendDrop

        Prompts for the Bearer Token and connects to SendDrop.

    .NOTES
        The Bearer Token is stored in a script-level variable and will persist for the current PowerShell session.
        To disconnect, restart PowerShell or call this function with a different token.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false,
            HelpMessage = "The Bearer Token for SendDrop API authentication. If not provided, you will be prompted to enter it.")]
        [Alias("BearerToken", "APIkey")]
        $Token,

        [Parameter(Mandatory = $false,
            HelpMessage = "Force reconnection even if already connected.")]
        [switch]$Force,

        [Parameter(Mandatory = $false,
            HelpMessage = "The base URI for the SendDrop API. e.g. https://api.senddrop.com/api/v1")]
        [string]$APIuri = "https://api.senddrop.com/api/v1"
    )

    # Check if already connected
    if ($script:SendDropToken -and $script:SendDropAPIuri -eq $APIuri -and -not $Force)
    {
        Write-Warning "Already connected to SendDrop API at $APIuri. Use -Force to reconnect with a different token or API URI."
        return
    }

    # If no token provided, prompt for it
    if (-not $Token)
    {
        $Token = Read-Host -Prompt "Enter your SendDrop Bearer Token" -AsSecureString
        # Convert SecureString to plain text for API calls
        $Token = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
            [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token)
        )
    }
    # Test token if type pscredential
    if ($Token -is [System.Management.Automation.PSCredential])
    {
        $Token = $Token.GetNetworkCredential().Password
    }

    # Store the token and API URI in script scope
    $script:SendDropToken = $Token
    $script:SendDropAPIuri = $APIuri

    # Test the connection by using the existing shipment retrieval cmdlet
    try
    {
        Write-Verbose "Testing SendDrop connection to $script:SendDropAPIuri"

        if (Get-Command Get-SendDropShipment -ErrorAction SilentlyContinue)
        {
            $testResult = Get-SendDropShipment -Page 1 -PageSize 1
        }
        else
        {
            throw "The cmdlet Get-SendDropShipment is not available for connection validation."
        }

        if ($testResult)
        {
            Write-Host "Successfully connected to SendDrop API at $script:SendDropAPIuri." -ForegroundColor Green
        }
        else
        {
            throw "Received an unexpected empty response while validating the SendDrop connection."
        }
    }
    catch
    {
        Write-Error "Failed to connect to SendDrop API: $_"
        # Clear the token and API URI on failure
        $script:SendDropToken = $null
        $script:SendDropAPIuri = $null
        throw
    }
}
#EndRegion './Public/Connect-SendDrop.ps1' 108
#Region './Public/Disconnect-SendDrop.ps1' -1

function Disconnect-SendDrop
{
    <#
    .SYNOPSIS
        Disconnects from the SendDrop API session.

    .DESCRIPTION
        The Disconnect-SendDrop function clears the stored SendDrop Bearer Token
        and API URI from the current PowerShell session.
        After calling this function, subsequent SendDrop API calls require reconnecting
        with Connect-SendDrop.

    .PARAMETER Confirm
        Prompts for confirmation before disconnecting.

    .EXAMPLE
        Disconnect-SendDrop

        Disconnects the current SendDrop API session.

    .NOTES
        This function does not revoke the token on the SendDrop server.
        It only clears locally stored session data.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param ()

    if ($PSCmdlet.ShouldProcess('Disconnect SendDrop', 'Clear stored SendDrop connection information.'))
    {
        if (-not $script:SendDropToken -and -not $script:SendDropAPIuri)
        {
            Write-Warning 'No SendDrop connection is currently established.'
            return
        }
        $script:SendDropToken = $null
        $script:SendDropAPIuri = $null
        Write-Host 'Disconnected from SendDrop. Local session state cleared.' -ForegroundColor Green
    }
}
#EndRegion './Public/Disconnect-SendDrop.ps1' 40
#Region './Public/Invoke-SendDropAPICall.ps1' -1

function Invoke-SendDropAPICall
{
    <#
    .SYNOPSIS
        Invokes an API call to the SendDrop service using the Bearer Token from Connect-SendDrop.

    .DESCRIPTION
        The Invoke-SendDropAPICall function makes HTTP requests to the SendDrop API endpoints.
        It automatically includes the Bearer Token obtained from the Connect-SendDrop function
        in the Authorization header. The function supports various HTTP methods and handles
        JSON serialization/deserialization automatically.

    .PARAMETER Method
        The HTTP method to use for the API call (GET, POST, PUT, DELETE, PATCH).

    .PARAMETER Endpoint
        The API endpoint path (relative to the base URL). Should start with a forward slash.

    .PARAMETER Body
        The request body as a PowerShell object. Will be automatically converted to JSON.

    .PARAMETER Headers
        Additional headers to include in the request as a hashtable.

    .PARAMETER ContentType
        The content type of the request body. Defaults to 'application/json'.

    .PARAMETER Raw
        Returns the raw WebResponse object instead of parsed JSON.

    .EXAMPLE
        Invoke-SendDropAPICall -Method GET -Endpoint "/users"

        Retrieves a list of users from the SendDrop API.

    .EXAMPLE
        $userData = @{ name = "John Doe"; email = "john@example.com" }
        Invoke-SendDropAPICall -Method POST -Endpoint "/users" -Body $userData

        Creates a new user with the provided data.

    .EXAMPLE
        Invoke-SendDropAPICall -Method GET -Endpoint "/data" -Raw

        Retrieves data and returns the raw WebResponse object.

    .NOTES
        Requires a valid Bearer Token to be set by calling Connect-SendDrop first.
        The base URL is configured via the Connect-SendDrop function.
    #>

    [CmdletBinding()]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            HelpMessage = "The HTTP method to use (GET, POST, PUT, DELETE, PATCH).")]
        [ValidateSet("GET", "POST", "PUT", "DELETE", "PATCH")]
        [string]$Method,

        [Parameter(Mandatory = $true,
            HelpMessage = "The API endpoint path (relative to base URL, should start with /).")]
        [string]$Endpoint,

        [Parameter(Mandatory = $false,
            HelpMessage = "The request body as a PowerShell object (will be converted to JSON).")]
        [object]$Body,

        [Parameter(Mandatory = $false,
            HelpMessage = "Additional headers to include in the request.")]
        [hashtable]$Headers = @{},

        [Parameter(Mandatory = $false,
            HelpMessage = "Content type of the request body.")]
        [string]$ContentType = "application/json",

        [Parameter(Mandatory = $false,
            HelpMessage = "Return raw WebResponse object instead of parsed JSON.")]
        [switch]$Raw
    )

    # Check if token and API URI are available
    if (-not $script:SendDropToken)
    {
        throw "No Bearer Token found. Please connect first using Connect-SendDrop."
    }

    if (-not $script:SendDropAPIuri)
    {
        throw "No API URI found. Please connect first using Connect-SendDrop."
    }

    # Base URL for SendDrop API
    $baseUrl = $script:SendDropAPIuri

    # Construct full URI
    $uri = $baseUrl + $Endpoint

    # Prepare headers
    $requestHeaders = @{
        "Authorization" = "Bearer $($script:SendDropToken)"
        "Content-Type"  = $ContentType
    }

    # Add any additional headers
    foreach ($key in $Headers.Keys)
    {
        $requestHeaders[$key] = $Headers[$key]
    }

    # Prepare request parameters
    $params = @{
        Method  = $Method
        Uri     = $uri
        Headers = $requestHeaders
    }

    # Add body if provided
    if ($Body)
    {
        if ($ContentType -eq "application/json")
        {
            $params.Body = $Body | ConvertTo-Json -Depth 10
        }
        else
        {
            $params.Body = $Body
        }
    }

    try
    {
        Write-Verbose "Invoking $Method request to $uri"
        write-verbose "TSE request parameters: $($params | Out-String)or"
        $response = Invoke-WebRequest @params

        if ($Raw)
        {
            return $response
        }

        # Parse JSON response
        if ($response.Content)
        {
            try
            {
                $parsedContent = $response.Content | ConvertFrom-Json
                return $parsedContent
            }
            catch
            {
                Write-Warning "Failed to parse response as JSON. Returning raw content."
                return $response.Content
            }
        }

        return $null
    }
    catch
    {
        # Handle HTTP errors
        if ($_.Exception.Response)
        {
            $statusCode = $_.Exception.Response.StatusCode.value__
            $statusDescription = $_.Exception.Response.StatusDescription

            $errorMessage = "API call failed with status $statusCode ($statusDescription)"

            # Try to get error details from response
            try
            {
                $errorContent = $_.Exception.Response.GetResponseStream()
                $reader = New-Object System.IO.StreamReader($errorContent)
                $errorDetails = $reader.ReadToEnd() | ConvertFrom-Json
                if ($errorDetails.message)
                {
                    $errorMessage += ": $($errorDetails.message)"
                }
            }
            catch
            {
                # Ignore if we can't parse error details
            }

            throw $errorMessage
        }
        else
        {
            throw "API call failed: $($_.Exception.Message)"
        }
    }
}
#EndRegion './Public/Invoke-SendDropAPICall.ps1' 191
#Region './Public/Shipments/Add-SendDropShipmentDocument.ps1' -1

function Add-SendDropShipmentDocument
{
    <#
    .SYNOPSIS
        Uploads a PDF document for a shipment.

    .DESCRIPTION
        The Add-SendDropShipmentDocument function uploads a PDF document to an existing shipment
        through the SendDrop API. It uses the previously configured connection and Bearer Token.

    .PARAMETER ShipmentId
        The ID of the shipment to add the document to.

    .PARAMETER FilePath
        The path to the PDF file to upload.

    .PARAMETER DocumentType
        The type of document being uploaded (e.g., "label", "invoice", "customs", "other").

    .PARAMETER DocumentName
        Optional name for the document. If not provided, the filename will be used.

    .PARAMETER Confirm
        Prompts for confirmation before uploading the document.

    .EXAMPLE
        Add-SendDropShipmentDocument -ShipmentId "SHIP123456" -FilePath "C:\Labels\shipping_label.pdf" -DocumentType "label"

        Uploads a shipping label PDF to the specified shipment.

    .EXAMPLE
        Add-SendDropShipmentDocument -ShipmentId "SHIP123456" -FilePath ".\invoice.pdf" -DocumentType "invoice" -DocumentName "Invoice Q4 2023"

        Uploads an invoice PDF with a custom name to the shipment.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Only PDF files are supported.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The ID of the shipment to add the document to.")]
        [string]$ShipmentId,

        [Parameter(Mandatory = $true,
            HelpMessage = "The path to the PDF file to upload.")]
        [ValidateScript({ Test-Path $_ -PathType Leaf })]
        [string]$FilePath,

        [Parameter(Mandatory = $true,
            HelpMessage = "The type of document being uploaded.")]
        [ValidateSet("label", "invoice", "customs", "proof_of_delivery", "other")]
        [string]$DocumentType,

        [Parameter(Mandatory = $false,
            HelpMessage = "Optional name for the document.")]
        [string]$DocumentName
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Validate shipment ID
        if (-not $ShipmentId)
        {
            throw "ShipmentId cannot be empty."
        }

        # Validate file exists and is PDF
        $fileInfo = Get-Item $FilePath
        if ($fileInfo.Extension -ne '.pdf')
        {
            throw "Only PDF files are supported. File extension: $($fileInfo.Extension)"
        }

        # Set default document name if not provided
        if (-not $DocumentName)
        {
            $DocumentName = $fileInfo.Name
        }

        $endpoint = "/shipments/$ShipmentId/documents"

        # Prepare multipart form data
        $boundary = "----FormBoundary" + [Guid]::NewGuid().ToString("N")
        $fileContent = [System.IO.File]::ReadAllBytes($FilePath)
        $fileContentBase64 = [System.Convert]::ToBase64String($fileContent)

        $bodyParts = @(
            "--$boundary",
            "Content-Disposition: form-data; name=`"document`"; filename=`"$($fileInfo.Name)`"",
            "Content-Type: application/pdf",
            "",
            $fileContentBase64,
            "--$boundary",
            "Content-Disposition: form-data; name=`"documentType`"",
            "",
            $DocumentType,
            "--$boundary",
            "Content-Disposition: form-data; name=`"documentName`"",
            "",
            $DocumentName,
            "--$boundary--"
        )

        $body = $bodyParts -join "`r`n"

        $headers = @{
            "Content-Type" = "multipart/form-data; boundary=$boundary"
        }

        if ($PSCmdlet.ShouldProcess("Upload document to shipment $ShipmentId", "Are you sure you want to upload this document?"))
        {
            try
            {
                Write-Verbose "Uploading document '$DocumentName' to shipment $ShipmentId..."

                $result = Invoke-SendDropAPICall -Method POST -Endpoint $endpoint -Body $body -Headers $headers -ContentType "multipart/form-data; boundary=$boundary"

                Write-Host "Document uploaded successfully to shipment $ShipmentId." -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Error uploading document: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/Add-SendDropShipmentDocument.ps1' 138
#Region './Public/Shipments/Copy-SendDropShipment.ps1' -1

function Copy-SendDropShipment
{
    <#
    .SYNOPSIS
        Creates a copy of an existing shipment, establishing a parent-child relationship.

    .DESCRIPTION
        The Copy-SendDropShipment function duplicates an existing shipment through the SendDrop API
        and establishes a parent-child relationship between the original and the copy.
        It uses the previously configured connection and Bearer Token.

    .PARAMETER ShipmentId
        The ID of the shipment to copy.

    .PARAMETER CopyData
        Optional hashtable or PSCustomObject containing modifications for the copied shipment.
        If not provided, an exact copy will be created.

    .PARAMETER Confirm
        Prompts for confirmation before creating the shipment copy.

    .EXAMPLE
        Copy-SendDropShipment -ShipmentId "SHIP123456"

        Creates an exact copy of the shipment with ID SHIP123456, establishing a parent-child relationship.

    .EXAMPLE
        $modifications = @{
            recipient = @{
                name = "Jane Smith"
                address = @{
                    street = "456 Oak Ave"
                    city = "Somewhere"
                    postalCode = "67890"
                    country = "US"
                }
            }
        }
        Copy-SendDropShipment -ShipmentId "SHIP123456" -CopyData $modifications

        Creates a copy of the shipment with modified recipient information.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        The original shipment becomes the parent, and the copy becomes the child in the relationship.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The ID of the shipment to copy.")]
        [string]$ShipmentId,

        [Parameter(Mandatory = $false,
            HelpMessage = "Optional modifications for the copied shipment.")]
        [object]$CopyData
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Validate shipment ID
        if (-not $ShipmentId)
        {
            throw "ShipmentId cannot be empty."
        }

        $endpoint = "/shipments/$ShipmentId/copy"

        # Prepare the request body
        $requestBody = @{}
        if ($CopyData)
        {
            $requestBody = $CopyData
        }

        if ($PSCmdlet.ShouldProcess("Copy shipment $ShipmentId", "Are you sure you want to create a copy of this shipment?"))
        {
            try
            {
                Write-Verbose "Creating copy of shipment $ShipmentId..."

                $result = Invoke-SendDropAPICall -Method POST -Endpoint $endpoint -Body $requestBody

                Write-Host "Shipment copy created successfully. Parent: $ShipmentId, Child: $($result.id)" -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Error creating shipment copy: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/Copy-SendDropShipment.ps1' 103
#Region './Public/Shipments/Get-SendDropShipment.ps1' -1

function Get-SendDropShipment
{
    <#
    .SYNOPSIS
        Searches and retrieves shipments with pagination, sorting, filtering, and tab filtering, or retrieves a single shipment by ID.

    .DESCRIPTION
        The Get-SendDropShipment function searches for shipments through the SendDrop API
        with support for pagination, sorting, various filters, and tab-based filtering.
        Alternatively, it can retrieve a single shipment by its ID.
        It uses the previously configured connection and Bearer Token.

    .PARAMETER Id
        The UUID of the shipment to retrieve. If specified, retrieves a single shipment.
        This parameter supports pipeline input by property name.

    .PARAMETER Page
        The page number for pagination (default: 1). Ignored when Id is specified.

    .PARAMETER PageSize
        The number of shipments per page (default: 50, max: 100). Ignored when Id is specified.

    .PARAMETER SortBy
        The field to sort by (e.g., "createdAt", "updatedAt", "trackingNumber"). Ignored when Id is specified.

    .PARAMETER SortOrder
        The sort order: "asc" or "desc" (default: "desc"). Ignored when Id is specified.

    .PARAMETER Status
        Filter by shipment status (e.g., "pending", "shipped", "delivered", "cancelled"). Ignored when Id is specified.

    .PARAMETER Tab
        Filter by tab/category (e.g., "active", "completed", "returns", "all"). Ignored when Id is specified.

    .PARAMETER CreatedAfter
        Filter shipments created after this date (as DateTime or ISO string). Ignored when Id is specified.

    .PARAMETER CreatedBefore
        Filter shipments created before this date (as DateTime or ISO string). Ignored when Id is specified.

    .PARAMETER UpdatedAfter
        Filter shipments updated after this date (as DateTime or ISO string). Ignored when Id is specified.

    .PARAMETER UpdatedBefore
        Filter shipments updated before this date (as DateTime or ISO string). Ignored when Id is specified.

    .PARAMETER TrackingNumber
        Filter by tracking number. Ignored when Id is specified.

    .PARAMETER RecipientName
        Filter by recipient name (partial match). Ignored when Id is specified.

    .PARAMETER Carrier
        Filter by carrier name. Ignored when Id is specified.

    .EXAMPLE
        Get-SendDropShipment

        Retrieves the first page of shipments (default sorting by newest first).

    .EXAMPLE
        Get-SendDropShipment -Id "550e8400-e29b-41d4-a716-446655440000"

        Retrieves a single shipment by its ID.

    .EXAMPLE
        "550e8400-e29b-41d4-a716-446655440000" | Get-SendDropShipment

        Retrieves a single shipment by ID via pipeline.

    .EXAMPLE
        Get-SendDropShipment -Page 2 -PageSize 25 -Status "shipped"

        Retrieves page 2 with 25 shipped shipments per page.

    .EXAMPLE
        Get-SendDropShipment -Tab "active" -SortBy "createdAt" -SortOrder "asc"

        Retrieves active shipments sorted by creation date ascending.

    .EXAMPLE
        Get-SendDropShipment -CreatedAfter "2024-01-01" -CreatedBefore "2024-12-31" -Carrier "DHL"

        Retrieves shipments created in 2024 that were shipped with DHL.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Date parameters accept DateTime objects or ISO 8601 formatted strings.
        When Id is specified, all other parameters are ignored.
    #>

    [CmdletBinding()]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The UUID of the shipment to retrieve.")]
        [string]$Id,

        [Parameter(Mandatory = $false,
            HelpMessage = "The page number for pagination.")]
        [ValidateRange(1, [int]::MaxValue)]
        [int]$Page = 1,

        [Parameter(Mandatory = $false,
            HelpMessage = "The number of shipments per page.")]
        [ValidateRange(1, 100)]
        [int]$PageSize = 50,

        [Parameter(Mandatory = $false,
            HelpMessage = "The field to sort by.")]
        [ValidateSet("createdAt", "updatedAt", "trackingNumber", "status", "recipientName")]
        [string]$SortBy,

        [Parameter(Mandatory = $false,
            HelpMessage = "The sort order.")]
        [ValidateSet("asc", "desc")]
        [string]$SortOrder = "desc",

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter by shipment status.")]
        [ValidateSet("pending", "processing", "shipped", "in_transit", "delivered", "cancelled", "returned")]
        [string]$Status,

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter by tab/category.")]
        [ValidateSet("active", "completed", "returns", "all")]
        [string]$Tab = "all",

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter shipments created after this date.")]
        [object]$CreatedAfter,

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter shipments created before this date.")]
        [object]$CreatedBefore,

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter shipments updated after this date.")]
        [object]$UpdatedAfter,

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter shipments updated before this date.")]
        [object]$UpdatedBefore,

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter by tracking number.")]
        [string]$TrackingNumber,

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter by recipient name.")]
        [string]$RecipientName,

        [Parameter(Mandatory = $false,
            HelpMessage = "Filter by carrier name.")]
        [string]$Carrier
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        if ($Id)
        {
            # Retrieve single shipment by ID
            $endpoint = "/shipments/$Id"
            try
            {
                Write-Verbose "Retrieving shipment with ID: $Id"
                $result = Invoke-SendDropAPICall -Method GET -Endpoint $endpoint
                Write-Verbose "Retrieved shipment: $($result.id)"
                return $result
            }
            catch
            {
                Write-Error "Error retrieving shipment with ID $Id : $_"
                throw
            }
        }
        else
        {
            # Search shipments with filters
            $endpoint = "/shipments"

            # Build query parameters
            $queryParams = @{}

            # Pagination
            $queryParams["page"] = $Page
            $queryParams["pageSize"] = $PageSize

            # Sorting
            if ($SortBy)
            {
                $queryParams["sortBy"] = $SortBy
                $queryParams["sortOrder"] = $SortOrder
            }

            # Filters
            if ($Status)
            {
                $queryParams["status"] = $Status
            }
            if ($Tab -and $Tab -ne "all")
            {
                $queryParams["tab"] = $Tab
            }

            # Date filters - convert to ISO 8601 format
            if ($CreatedAfter)
            {
                if ($CreatedAfter -is [DateTime])
                {
                    $queryParams["createdAfter"] = $CreatedAfter.ToString("yyyy-MM-ddTHH:mm:ssZ")
                }
                else
                {
                    $queryParams["createdAfter"] = $CreatedAfter
                }
            }

            if ($CreatedBefore)
            {
                if ($CreatedBefore -is [DateTime])
                {
                    $queryParams["createdBefore"] = $CreatedBefore.ToString("yyyy-MM-ddTHH:mm:ssZ")
                }
                else
                {
                    $queryParams["createdBefore"] = $CreatedBefore
                }
            }

            if ($UpdatedAfter)
            {
                if ($UpdatedAfter -is [DateTime])
                {
                    $queryParams["updatedAfter"] = $UpdatedAfter.ToString("yyyy-MM-ddTHH:mm:ssZ")
                }
                else
                {
                    $queryParams["updatedAfter"] = $UpdatedAfter
                }
            }

            if ($UpdatedBefore)
            {
                if ($UpdatedBefore -is [DateTime])
                {
                    $queryParams["updatedBefore"] = $UpdatedBefore.ToString("yyyy-MM-ddTHH:mm:ssZ")
                }
                else
                {
                    $queryParams["updatedBefore"] = $UpdatedBefore
                }
            }

            # Other filters
            if ($TrackingNumber)
            {
                $queryParams["trackingNumber"] = $TrackingNumber
            }
            if ($RecipientName)
            {
                $queryParams["recipientName"] = $RecipientName
            }
            if ($Carrier)
            {
                $queryParams["carrier"] = $Carrier
            }

            # Build query string
            $queryString = ""
            if ($queryParams.Count -gt 0)
            {
                $queryParts = @()
                foreach ($key in $queryParams.Keys)
                {
                    $queryParts += "$key=$([System.Web.HttpUtility]::UrlEncode($queryParams[$key]))"
                }
                $queryString = "?" + ($queryParts -join "&")
            }

            $fullEndpoint = $endpoint + $queryString

            try
            {
                Write-Verbose "Searching shipments with parameters: $($queryParams | ConvertTo-Json -Compress)"
                $result = Invoke-SendDropAPICall -Method GET -Endpoint $fullEndpoint
                Write-Verbose "Retrieved $($result.data.Count) shipments (page $Page of $($result.totalPages), total: $($result.totalCount))"
                # Return the result with pagination metadata
                return $result
            }
            catch
            {
                Write-Error "Error searching shipments: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/Get-SendDropShipment.ps1' 305
#Region './Public/Shipments/Get-SendDropShipmentDocument.ps1' -1

function Get-SendDropShipmentDocument
{
    <#
    .SYNOPSIS
        Downloads a document PDF from a SendDrop shipment.

    .DESCRIPTION
        The Get-SendDropShipmentDocument function retrieves and downloads a PDF document
        from a specific shipment by its shipment ID and document ID from the SendDrop API.
        It requires a valid connection established via Connect-SendDrop.

    .PARAMETER ShipmentId
        The UUID of the shipment containing the document. This parameter is required.

    .PARAMETER DocumentId
        The UUID of the document to download. This parameter is required.

    .PARAMETER OutPath
        Optional file path where the PDF document should be saved. If not specified,
        the PDF bytes are returned as output.

    .EXAMPLE
        Get-SendDropShipmentDocument -ShipmentId "550e8400-e29b-41d4-a716-446655440000" `
            -DocumentId "660e8400-e29b-41d4-a716-446655440000"

        Downloads the document for the specified shipment and returns the PDF bytes.

    .EXAMPLE
        Get-SendDropShipmentDocument -ShipmentId "550e8400-e29b-41d4-a716-446655440000" `
            -DocumentId "660e8400-e29b-41d4-a716-446655440000" `
            -OutPath "C:\documents\shipment-label.pdf"

        Downloads the document for the specified shipment and saves it to the specified file path.

    .EXAMPLE
        Get-SendDropShipment | Get-SendDropShipmentDocument -DocumentId "660e8400-e29b-41d4-a716-446655440000"

        Downloads a document from shipments piped from Get-SendDropShipment.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Uses the API endpoint: GET /api/v1/shipments/{id}/documents/{docId}
        Returns PDF file data.
        Supports pipeline input from Get-SendDropShipment for the ShipmentId parameter.
    #>

    [CmdletBinding()]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $false,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The UUID of the shipment containing the document.")]
        [guid]$ShipmentId,

        [Parameter(Mandatory = $true,
            HelpMessage = "The UUID of the document to download.")]
        [guid]$DocumentId,

        [Parameter(Mandatory = $false,
            HelpMessage = "File path where the PDF document should be saved.")]
        [string]$OutPath
    )

    process
    {
        if (-not $ShipmentId)
        {
            Write-Error "ShipmentId is required. Provide it via -ShipmentId parameter or pipeline input."
            return
        }

        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        try
        {
            Write-Verbose "Retrieving document $DocumentId from shipment $ShipmentId"
            $endpoint = "/shipments/$ShipmentId/documents/$DocumentId"

            # Get the raw response for the PDF
            $response = Invoke-SendDropAPICall -Method GET -Endpoint $endpoint -Raw

            # Check if OutPath is specified
            if ($OutPath)
            {
                Write-Verbose "Saving document to: $OutPath"
                $response.Content | Set-Content -Path $OutPath -AsByteStream -Force
                Write-Verbose "Document saved successfully to: $OutPath"
                return $OutPath
            }
            else
            {
                # Return the raw content (PDF bytes)
                return $response.Content
            }
        }
        catch
        {
            Write-Error "Failed to retrieve shipment document: $_"
        }
    }
}
#EndRegion './Public/Shipments/Get-SendDropShipmentDocument.ps1' 105
#Region './Public/Shipments/New-SendDropShipment.ps1' -1

function New-SendDropShipment
{
    <#
    .SYNOPSIS
        Creates a new shipment in SendDrop.

    .DESCRIPTION
        The New-SendDropShipment function creates a new shipment through the SendDrop API.
        It uses the previously configured connection and Bearer Token.

    .PARAMETER ShipmentData
        A hashtable or PSCustomObject containing the shipment data.
        Must include at least the required fields for a shipment.

    .PARAMETER Confirm
        Prompts for confirmation before creating the shipment.

    .EXAMPLE
        $shipment = @{
            recipient = @{
                name = "John Doe"
                address = @{
                    street = "123 Main St"
                    city = "Anytown"
                    postalCode = "12345"
                    country = "US"
                }
            }
            parcels = @(
                @{
                    weight = 1.5
                    dimensions = @{
                        length = 30
                        width = 20
                        height = 10
                    }
                }
            )
        }
        New-SendDropShipment -ShipmentData $shipment

        Creates a new shipment with the specified data.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            HelpMessage = "The shipment data as a hashtable or object.")]
        [object]$ShipmentData
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Validate shipment data
        if (-not $ShipmentData)
        {
            throw "ShipmentData cannot be empty."
        }

        # Basic validation of required fields
        if (-not $ShipmentData.recipient -or -not $ShipmentData.parcels)
        {
            throw "ShipmentData must contain at least 'recipient' and 'parcels'."
        }

        $endpoint = "/shipments"

        if ($PSCmdlet.ShouldProcess("Create shipment", "Are you sure you want to create this shipment?"))
        {
            try
            {
                Write-Verbose "Creating new shipment..."
                $result = Invoke-SendDropAPICall -Method POST -Endpoint $endpoint -Body $ShipmentData
                Write-Host "Shipment created successfully." -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Error creating shipment: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/New-SendDropShipment.ps1' 95
#Region './Public/Shipments/New-SendDropShipmentLabel.ps1' -1

function New-SendDropShipmentLabel
{
    <#
    .SYNOPSIS
        Creates a new shipment label in SendDrop.

    .DESCRIPTION
        The New-SendDropShipmentLabel function creates a new shipping label through the SendDrop API.
        It uses the previously configured connection and Bearer Token.

    .PARAMETER LabelData
        A hashtable or PSCustomObject containing the label data.
        Must include at least the required fields for a shipping label.

    .PARAMETER Confirm
        Prompts for confirmation before creating the label.

    .EXAMPLE
        $labelData = @{
            shipmentId = "SHIP123456"
            format = "PDF"
            size = "A6"
            includeReturnLabel = $true
        }
        New-SendDropShipmentLabel -LabelData $labelData

        Creates a new shipping label for the specified shipment.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            HelpMessage = "The label data as a hashtable or object.")]
        [object]$LabelData
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Validate label data
        if (-not $LabelData)
        {
            throw "LabelData cannot be empty."
        }

        # Basic validation of required fields
        if (-not $LabelData.shipmentId)
        {
            throw "LabelData must contain at least 'shipmentId'."
        }

        $endpoint = "/labels"

        if ($PSCmdlet.ShouldProcess("Create shipment label", "Are you sure you want to create this shipping label?"))
        {
            try
            {
                Write-Verbose "Creating new shipment label..."

                $result = Invoke-SendDropAPICall -Method POST -Endpoint $endpoint -Body $LabelData

                Write-Host "Shipment label created successfully." -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Error creating shipment label: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/New-SendDropShipmentLabel.ps1' 82
#Region './Public/Shipments/New-SendDropShipmentReturn.ps1' -1

function New-SendDropShipmentReturn
{
    <#
    .SYNOPSIS
        Creates a new return shipment in SendDrop.

    .DESCRIPTION
        The New-SendDropShipmentReturn function creates a new return shipment through the SendDrop API.
        It uses the previously configured connection and Bearer Token.

    .PARAMETER ReturnData
        A hashtable or PSCustomObject containing the return shipment data.
        Must include at least the required fields for a return shipment.

    .PARAMETER Confirm
        Prompts for confirmation before creating the return shipment.

    .EXAMPLE
        $returnShipment = @{
            originalShipmentId = "SHIP123456"
            returnReason = "Damaged item"
            recipient = @{
                name = "John Doe"
                address = @{
                    street = "123 Main St"
                    city = "Anytown"
                    postalCode = "12345"
                    country = "US"
                }
            }
            parcels = @(
                @{
                    weight = 1.2
                    dimensions = @{
                        length = 25
                        width = 15
                        height = 8
                    }
                }
            )
        }
        New-SendDropShipmentReturn -ReturnData $returnShipment

        Creates a new return shipment with the specified data.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            HelpMessage = "The return shipment data as a hashtable or object.")]
        [object]$ReturnData
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Validate return data
        if (-not $ReturnData)
        {
            throw "ReturnData cannot be empty."
        }

        # Basic validation of required fields
        if (-not $ReturnData.recipient -or -not $ReturnData.parcels)
        {
            throw "ReturnData must contain at least 'recipient' and 'parcels'."
        }

        $endpoint = "/returns"

        if ($PSCmdlet.ShouldProcess("Create return shipment", "Are you sure you want to create this return shipment?"))
        {
            try
            {
                Write-Verbose "Creating new return shipment..."

                $result = Invoke-SendDropAPICall -Method POST -Endpoint $endpoint -Body $ReturnData

                Write-Host "Return shipment created successfully." -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Error creating return shipment: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/New-SendDropShipmentReturn.ps1' 99
#Region './Public/Shipments/Update-SendDropShipment.ps1' -1

function Update-SendDropShipment
{
    <#
    .SYNOPSIS
        Updates the recipient and details of an existing SendDrop shipment.

    .DESCRIPTION
        The Update-SendDropShipment function updates the recipient address and shipment details
        through the SendDrop API. It allows you to modify recipient information, product elements,
        carrier type, and other shipment details before it has been shipped.
        It requires a valid connection established via Connect-SendDrop.

    .PARAMETER Id
        The UUID of the shipment to update. This parameter is required.

    .PARAMETER FirstName
        The first name of the recipient.

    .PARAMETER LastName
        The last name of the recipient.

    .PARAMETER CompanyName
        The company name (if applicable).

    .PARAMETER Street
        The street address.

    .PARAMETER HouseNumber
        The house/building number.

    .PARAMETER PostalCode
        The postal code.

    .PARAMETER City
        The city.

    .PARAMETER Province
        The province or state.

    .PARAMETER Country
        The country code.

    .PARAMETER Email
        The email address of the recipient.

    .PARAMETER PhoneNumber
        The phone number of the recipient.

    .PARAMETER CarrierType
        The carrier type (e.g., "GLS_AT", "DHL", etc.).

    .PARAMETER BrandId
        The UUID of the brand to use for this shipment.

    .PARAMETER WeightGrams
        The weight of the shipment in grams.

    .PARAMETER ProductElements
        An array of product elements. Each element should be a hashtable containing:
        sku, title, variantTitle, quantity, unitPrice, currency, hsCode, countryOfOrigin, etc.

    .PARAMETER IncotermCode
        The Incoterm code for international shipments.

    .PARAMETER InvoiceNumber
        The invoice number associated with this shipment.

    .PARAMETER ShipmentData
        A hashtable containing the complete shipment update data. Can be used instead of
        specifying individual parameters.

    .EXAMPLE
        Update-SendDropShipment -Id "550e8400-e29b-41d4-a716-446655440000" `
            -FirstName "John" `
            -LastName "Doe" `
            -Street "123 Main St" `
            -City "Vienna" `
            -PostalCode "1010" `
            -Country "AT"

        Updates the recipient address for the specified shipment.

    .EXAMPLE
        $shipmentData = @{
            firstName = "Jane"
            lastName = "Smith"
            street = "456 Oak Ave"
            city = "Salzburg"
            postalCode = "5020"
            country = "AT"
            email = "jane@example.com"
            phoneNumber = "+43123456789"
        }
        Update-SendDropShipment -Id "550e8400-e29b-41d4-a716-446655440000" -ShipmentData $shipmentData

        Updates the shipment with complete data from a hashtable.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Cannot update a shipment after a label has been created.
        Uses the API endpoint: PATCH /api/v1/shipments/{id}
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The UUID of the shipment to update.")]
        [guid]$Id,

        [Parameter(Mandatory = $false,
            HelpMessage = "The first name of the recipient.")]
        [string]$FirstName,

        [Parameter(Mandatory = $false,
            HelpMessage = "The last name of the recipient.")]
        [string]$LastName,

        [Parameter(Mandatory = $false,
            HelpMessage = "The company name.")]
        [string]$CompanyName,

        [Parameter(Mandatory = $false,
            HelpMessage = "The street address.")]
        [string]$Street,

        [Parameter(Mandatory = $false,
            HelpMessage = "The house/building number.")]
        [string]$HouseNumber,

        [Parameter(Mandatory = $false,
            HelpMessage = "The postal code.")]
        [string]$PostalCode,

        [Parameter(Mandatory = $false,
            HelpMessage = "The city.")]
        [string]$City,

        [Parameter(Mandatory = $false,
            HelpMessage = "The province or state.")]
        [string]$Province,

        [Parameter(Mandatory = $false,
            HelpMessage = "The country code.")]
        [string]$Country,

        [Parameter(Mandatory = $false,
            HelpMessage = "The email address of the recipient.")]
        [string]$Email,

        [Parameter(Mandatory = $false,
            HelpMessage = "The phone number of the recipient.")]
        [string]$PhoneNumber,

        [Parameter(Mandatory = $false,
            HelpMessage = "The carrier type.")]
        [string]$CarrierType,

        [Parameter(Mandatory = $false,
            HelpMessage = "The UUID of the brand for this shipment.")]
        [guid]$BrandId,

        [Parameter(Mandatory = $false,
            HelpMessage = "The weight of the shipment in grams.")]
        [long]$WeightGrams,

        [Parameter(Mandatory = $false,
            HelpMessage = "An array of product elements.")]
        [object[]]$ProductElements,

        [Parameter(Mandatory = $false,
            HelpMessage = "The Incoterm code.")]
        [string]$IncotermCode,

        [Parameter(Mandatory = $false,
            HelpMessage = "The invoice number.")]
        [string]$InvoiceNumber,

        [Parameter(Mandatory = $false,
            ValueFromPipeline = $true,
            HelpMessage = "The complete shipment update data as a hashtable.")]
        [hashtable]$ShipmentData
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Build the request body
        if ($ShipmentData)
        {
            $updateBody = $ShipmentData
        }
        else
        {
            $updateBody = @{}

            # Add only the parameters that were provided
            if ($PSBoundParameters.ContainsKey('FirstName'))
            {
                $updateBody["firstName"] = $FirstName
            }
            if ($PSBoundParameters.ContainsKey('LastName'))
            {
                $updateBody["lastName"] = $LastName
            }
            if ($PSBoundParameters.ContainsKey('CompanyName'))
            {
                $updateBody["companyName"] = $CompanyName
            }
            if ($PSBoundParameters.ContainsKey('Street'))
            {
                $updateBody["street"] = $Street
            }
            if ($PSBoundParameters.ContainsKey('HouseNumber'))
            {
                $updateBody["houseNumber"] = $HouseNumber
            }
            if ($PSBoundParameters.ContainsKey('PostalCode'))
            {
                $updateBody["postalCode"] = $PostalCode
            }
            if ($PSBoundParameters.ContainsKey('City'))
            {
                $updateBody["city"] = $City
            }
            if ($PSBoundParameters.ContainsKey('Province'))
            {
                $updateBody["province"] = $Province
            }
            if ($PSBoundParameters.ContainsKey('Country'))
            {
                $updateBody["country"] = $Country
            }
            if ($PSBoundParameters.ContainsKey('Email'))
            {
                $updateBody["email"] = $Email
            }
            if ($PSBoundParameters.ContainsKey('PhoneNumber'))
            {
                $updateBody["phoneNumber"] = $PhoneNumber
            }
            if ($PSBoundParameters.ContainsKey('CarrierType'))
            {
                $updateBody["carrierType"] = $CarrierType
            }
            if ($PSBoundParameters.ContainsKey('BrandId'))
            {
                $updateBody["brandId"] = $BrandId
            }
            if ($PSBoundParameters.ContainsKey('WeightGrams'))
            {
                $updateBody["weightGrams"] = $WeightGrams
            }
            if ($PSBoundParameters.ContainsKey('ProductElements'))
            {
                $updateBody["productElements"] = $ProductElements
            }
            if ($PSBoundParameters.ContainsKey('IncotermCode'))
            {
                $updateBody["incotermCode"] = $IncotermCode
            }
            if ($PSBoundParameters.ContainsKey('InvoiceNumber'))
            {
                $updateBody["invoiceNumber"] = $InvoiceNumber
            }
        }

        # Validate that at least one field was provided
        if ($updateBody.Count -eq 0)
        {
            Write-Error "No update data provided. Specify at least one parameter to update or provide ShipmentData."
            return
        }

        $endpoint = "/shipments/$Id"

        if ($PSCmdlet.ShouldProcess("Update shipment $Id", "Are you sure you want to update this shipment?"))
        {
            try
            {
                Write-Verbose "Updating shipment $Id with the following data: $($updateBody | ConvertTo-Json)"
                $result = Invoke-SendDropAPICall -Method PATCH -Endpoint $endpoint -Body $updateBody
                Write-Host "Shipment updated successfully." -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Failed to update shipment: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/Update-SendDropShipment.ps1' 299
#Region './Public/Shipments/Update-SendDropShipmentBrand.ps1' -1

function Update-SendDropShipmentBrand
{
    <#
    .SYNOPSIS
        Updates the brand and sender address of a SendDrop shipment.

    .DESCRIPTION
        The Update-SendDropShipmentBrand function updates the brand and optionally the sender address
        of an existing shipment through the SendDrop API.
        It requires a valid connection established via Connect-SendDrop.

    .PARAMETER ShipmentId
        The UUID of the shipment to update. This parameter is required.

    .PARAMETER BrandId
        The UUID of the brand to assign to this shipment. This parameter is required.

    .PARAMETER SenderFirstName
        The first name of the sender.

    .PARAMETER SenderLastName
        The last name of the sender.

    .PARAMETER SenderCompanyName
        The company name of the sender (if applicable).

    .PARAMETER SenderStreet
        The street address of the sender.

    .PARAMETER SenderHouseNumber
        The house/building number of the sender.

    .PARAMETER SenderPostalCode
        The postal code of the sender.

    .PARAMETER SenderCity
        The city of the sender.

    .PARAMETER SenderProvince
        The province or state of the sender.

    .PARAMETER SenderCountry
        The country code of the sender.

    .PARAMETER SenderLatitude
        The latitude coordinate of the sender's address.

    .PARAMETER SenderLongitude
        The longitude coordinate of the sender's address.

    .PARAMETER SenderAddress
        A hashtable containing the complete sender address data. Can be used instead of
        specifying individual sender address parameters. Should contain properties like
        firstName, lastName, companyName, street, houseNumber, postalCode, city, province, country, lat, lng.

    .EXAMPLE
        Update-SendDropShipmentBrand -ShipmentId "550e8400-e29b-41d4-a716-446655440000" `
            -BrandId "660e8400-e29b-41d4-a716-446655440000"

        Updates the brand for the specified shipment without changing the sender address.

    .EXAMPLE
        Update-SendDropShipmentBrand -ShipmentId "550e8400-e29b-41d4-a716-446655440000" `
            -BrandId "660e8400-e29b-41d4-a716-446655440000" `
            -SenderFirstName "John" `
            -SenderLastName "Doe" `
            -SenderCity "Vienna" `
            -SenderPostalCode "1010" `
            -SenderCountry "AT"

        Updates both the brand and sender address for the specified shipment.

    .EXAMPLE
        $senderAddr = @{
            firstName = "Jane"
            lastName = "Smith"
            street = "456 Oak Ave"
            city = "Salzburg"
            postalCode = "5020"
            country = "AT"
        }
        Update-SendDropShipmentBrand -ShipmentId "550e8400-e29b-41d4-a716-446655440000" `
            -BrandId "660e8400-e29b-41d4-a716-446655440000" `
            -SenderAddress $senderAddr

        Updates the brand and sender address using a hashtable.

    .EXAMPLE
        Get-SendDropShipment | Update-SendDropShipmentBrand -BrandId "660e8400-e29b-41d4-a716-446655440000"

        Updates the brand for shipments piped from Get-SendDropShipment.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Uses the API endpoint: PATCH /api/v1/shipments/{id}/brand
        Supports pipeline input from Get-SendDropShipment for the ShipmentId parameter.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The UUID of the shipment to update.")]
        [guid]$ShipmentId,

        [Parameter(Mandatory = $true,
            HelpMessage = "The UUID of the brand to assign to this shipment.")]
        [guid]$BrandId,

        [Parameter(Mandatory = $false,
            HelpMessage = "The first name of the sender.")]
        [string]$SenderFirstName,

        [Parameter(Mandatory = $false,
            HelpMessage = "The last name of the sender.")]
        [string]$SenderLastName,

        [Parameter(Mandatory = $false,
            HelpMessage = "The company name of the sender.")]
        [string]$SenderCompanyName,

        [Parameter(Mandatory = $false,
            HelpMessage = "The street address of the sender.")]
        [string]$SenderStreet,

        [Parameter(Mandatory = $false,
            HelpMessage = "The house/building number of the sender.")]
        [string]$SenderHouseNumber,

        [Parameter(Mandatory = $false,
            HelpMessage = "The postal code of the sender.")]
        [string]$SenderPostalCode,

        [Parameter(Mandatory = $false,
            HelpMessage = "The city of the sender.")]
        [string]$SenderCity,

        [Parameter(Mandatory = $false,
            HelpMessage = "The province or state of the sender.")]
        [string]$SenderProvince,

        [Parameter(Mandatory = $false,
            HelpMessage = "The country code of the sender.")]
        [string]$SenderCountry,

        [Parameter(Mandatory = $false,
            HelpMessage = "The latitude coordinate of the sender's address.")]
        [double]$SenderLatitude,

        [Parameter(Mandatory = $false,
            HelpMessage = "The longitude coordinate of the sender's address.")]
        [double]$SenderLongitude,

        [Parameter(Mandatory = $false,
            HelpMessage = "The complete sender address data as a hashtable.")]
        [hashtable]$SenderAddress
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Build the request body
        $updateBody = @{
            brandId = $BrandId
        }

        # Build sender address if any sender parameters are provided
        if ($SenderAddress)
        {
            $updateBody["senderAddress"] = $SenderAddress
        }
        else
        {
            $senderAddr = @{}

            # Add only the sender address parameters that were provided
            if ($PSBoundParameters.ContainsKey('SenderFirstName'))
            {
                $senderAddr["firstName"] = $SenderFirstName
            }
            if ($PSBoundParameters.ContainsKey('SenderLastName'))
            {
                $senderAddr["lastName"] = $SenderLastName
            }
            if ($PSBoundParameters.ContainsKey('SenderCompanyName'))
            {
                $senderAddr["companyName"] = $SenderCompanyName
            }
            if ($PSBoundParameters.ContainsKey('SenderStreet'))
            {
                $senderAddr["street"] = $SenderStreet
            }
            if ($PSBoundParameters.ContainsKey('SenderHouseNumber'))
            {
                $senderAddr["houseNumber"] = $SenderHouseNumber
            }
            if ($PSBoundParameters.ContainsKey('SenderPostalCode'))
            {
                $senderAddr["postalCode"] = $SenderPostalCode
            }
            if ($PSBoundParameters.ContainsKey('SenderCity'))
            {
                $senderAddr["city"] = $SenderCity
            }
            if ($PSBoundParameters.ContainsKey('SenderProvince'))
            {
                $senderAddr["province"] = $SenderProvince
            }
            if ($PSBoundParameters.ContainsKey('SenderCountry'))
            {
                $senderAddr["country"] = $SenderCountry
            }
            if ($PSBoundParameters.ContainsKey('SenderLatitude'))
            {
                $senderAddr["lat"] = $SenderLatitude
            }
            if ($PSBoundParameters.ContainsKey('SenderLongitude'))
            {
                $senderAddr["lng"] = $SenderLongitude
            }

            # Only add senderAddress if any fields were provided
            if ($senderAddr.Count -gt 0)
            {
                $updateBody["senderAddress"] = $senderAddr
            }
        }

        $endpoint = "/shipments/$ShipmentId/brand"

        if ($PSCmdlet.ShouldProcess("Update shipment $ShipmentId brand", "Are you sure you want to update this shipment's brand?"))
        {
            try
            {
                Write-Verbose "Updating brand for shipment $ShipmentId with the following data: $($updateBody | ConvertTo-Json)"
                $result = Invoke-SendDropAPICall -Method PATCH -Endpoint $endpoint -Body $updateBody
                Write-Host "Shipment brand updated successfully." -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Failed to update shipment brand: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/Update-SendDropShipmentBrand.ps1' 253
#Region './Public/Shipments/Update-SendDropShipmentComment.ps1' -1

function Update-SendDropShipmentComment
{
    <#
    .SYNOPSIS
        Updates the internal comment of a SendDrop shipment.

    .DESCRIPTION
        The Update-SendDropShipmentComment function updates the internal comment of an existing shipment
        through the SendDrop API. The comment is for internal tracking and organization purposes.
        It requires a valid connection established via Connect-SendDrop.

    .PARAMETER ShipmentId
        The UUID of the shipment to update. This parameter is required.

    .PARAMETER Comment
        The comment text to set for the shipment. This parameter is required.

    .EXAMPLE
        Update-SendDropShipmentComment -ShipmentId "550e8400-e29b-41d4-a716-446655440000" `
            -Comment "Delivered to customer, package slightly damaged"

        Updates the internal comment for the specified shipment.

    .EXAMPLE
        Update-SendDropShipmentComment -ShipmentId "550e8400-e29b-41d4-a716-446655440000" `
            -Comment "Follow up required" -Confirm

        Updates the comment with a confirmation prompt.

    .EXAMPLE
        Get-SendDropShipment | Update-SendDropShipmentComment -Comment "Important order - fragile contents"

        Updates the comment for shipments piped from Get-SendDropShipment.

    .NOTES
        Requires a valid connection established via Connect-SendDrop.
        Uses the API endpoint: PATCH /api/v1/shipments/{id}/comment
        The comment is for internal use and is not visible to the customer.
        Supports pipeline input from Get-SendDropShipment for the ShipmentId parameter.
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([object])]
    param (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = "The UUID of the shipment to update.")]
        [guid]$ShipmentId,

        [Parameter(Mandatory = $true,
            HelpMessage = "The comment text to set for the shipment.")]
        [string]$Comment
    )

    process
    {
        # Validate connection
        if (-not $script:SendDropToken)
        {
            throw "No SendDrop connection found. Please use Connect-SendDrop first."
        }

        # Build the request body
        $updateBody = @{
            comment = $Comment
        }

        $endpoint = "/shipments/$ShipmentId/comment"

        if ($PSCmdlet.ShouldProcess("Update shipment $ShipmentId comment", "Are you sure you want to update this shipment's comment?"))
        {
            try
            {
                Write-Verbose "Updating comment for shipment $ShipmentId with text: $Comment"
                $result = Invoke-SendDropAPICall -Method PATCH -Endpoint $endpoint -Body $updateBody
                Write-Host "Shipment comment updated successfully." -ForegroundColor Green
                return $result
            }
            catch
            {
                Write-Error "Failed to update shipment comment: $_"
                throw
            }
        }
    }
}
#EndRegion './Public/Shipments/Update-SendDropShipmentComment.ps1' 86