src/task.ps1

# SPDX-License-Identifier: Apache-2.0

$script:XO_TASK_FIELDS = "id,properties,start,status,result,updatedAt,end,progress"

function ConvertTo-XoTaskObject {
    <#
    .SYNOPSIS
        Convert a task object from the API to a PowerShell object.
    .DESCRIPTION
        Convert a task object from the API to a PowerShell object with proper properties.
    .PARAMETER InputObject
        The task object from the API.
    #>

    [CmdletBinding()]
    [OutputType("XoPowershell.Task")]
    param(
        [Parameter(Mandatory, ValueFromPipeline, Position = 0)]
        $InputObject
    )

    process {
        $name = if ($InputObject.properties.name) {
            $InputObject.properties.name
        }
        elseif ($InputObject.properties.method) {
            $InputObject.properties.method
        }
        else {
            "Unknown"
        }

        $type = if ($InputObject.properties.type) { $InputObject.properties.type } else { "" }

        $startTime = if ($InputObject.start -and $InputObject.start -gt 0) {
            [System.DateTimeOffset]::FromUnixTimeMilliseconds($InputObject.start).ToLocalTime()
        }
        else {
            $null
        }

        $endTime = if ($InputObject.end -and $InputObject.end -gt 0) {
            [System.DateTimeOffset]::FromUnixTimeMilliseconds($InputObject.end).ToLocalTime()
        }
        else {
            $null
        }

        $message = if ($InputObject.result.message) {
            $InputObject.result.message
        }
        elseif ($InputObject.result.code) {
            $InputObject.result.code
        }
        else {
            ""
        }

        $props = @{
            PSTypeName = "XoPowershell.Task"
            TaskId     = $InputObject.id
            Name       = $name
            Type       = $type
            Status     = $InputObject.status
            Progress   = if ($null -ne $InputObject.progress) { $InputObject.progress } else { 0 }
            StartTime  = $startTime
            EndTime    = $endTime
            Message    = $message
        }

        [PSCustomObject]$props
    }
}

function ConvertFrom-XoTaskHref {
    <#
    .SYNOPSIS
        Convert a task URL to a task object
    .DESCRIPTION
        Extracts the task ID from a URL and retrieves the task from the API
    .PARAMETER Uri
        The task URL to convert
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline, Position = 0)]
        [string]$Uri
    )

    process {
        if ($Uri -notmatch "\/rest\/v0\/tasks\/([0-9a-z]+)") {
            throw ("Bad task href format: {0}" -f $Uri)
        }

        $taskId = $matches[1]
        Get-XoTask -TaskId $taskId
    }
}

function Get-XoSingleTaskById {
    <#
    .SYNOPSIS
        Get a single task by ID
    .DESCRIPTION
        Retrieves a single task from the API by its ID
    .PARAMETER TaskId
        The ID of the task to retrieve
    .PARAMETER Params
        Additional parameters to pass to the API
    #>

    [CmdletBinding()]
    param (
        [string]$TaskId,
        [hashtable]$Params
    )

    try {
        Write-Verbose "Getting task with ID $TaskId"
        $uri = "$script:XoHost/rest/v0/tasks/$TaskId"
        $taskData = Invoke-RestMethod -Uri $uri @script:XoRestParameters -Body $Params

        if ($taskData) {
            return ConvertTo-XoTaskObject -InputObject $taskData
        }
    }
    catch {
        throw ("Failed to retrieve task with ID {0}: {1}" -f $TaskId, $_)
    }
    return $null
}

function Get-XoTask {
    <#
    .SYNOPSIS
        Get tasks from Xen Orchestra.
    .DESCRIPTION
        Retrieves tasks from Xen Orchestra. Can retrieve specific tasks by their ID
        or filter tasks by status.
    .PARAMETER TaskId
        The ID(s) of the task(s) to retrieve.
    .PARAMETER Status
        Filter tasks by status. Valid values: pending, success, failure.
    .PARAMETER Limit
        Maximum number of results to return. Default is 25 if not specified.
    .EXAMPLE
        Get-XoTask
        Returns up to 25 tasks of any status.
    .EXAMPLE
        Get-XoTask -Status failure
        Returns failed tasks.
    .EXAMPLE
        Get-XoTask -TaskId "0m8k2zkzi"
        Returns the task with the specified ID.
    .EXAMPLE
        Get-XoTask -Limit 5
        Returns the first 5 tasks.
    #>

    [CmdletBinding(DefaultParameterSetName = "Filter")]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0, ParameterSetName = "TaskId")]
        [ValidateNotNullOrEmpty()]
        [string[]]$TaskId,

        [Parameter(ParameterSetName = "Filter")]
        [ValidateSet("pending", "success", "failure")]
        [string]$Status,

        [Parameter(ParameterSetName = "Filter")]
        [int]$Limit = $script:XoSessionLimit
    )

    begin {
        if (-not $script:XoHost -or -not $script:XoRestParameters) {
            throw ("Not connected to Xen Orchestra. Call Connect-XoSession first.")
        }

        $params = @{
            fields = $script:XO_TASK_FIELDS
        }
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq "TaskId") {
            foreach ($id in $TaskId) {
                Get-XoSingleTaskById -TaskId $id -Params $params
            }
        }
    }

    end {
        if ($PSCmdlet.ParameterSetName -eq "Filter") {
            if ($Status) {
                $params['filter'] = $Status
            }

            if ($Limit) {
                $params['limit'] = $Limit
            }

            try {
                Write-Verbose "Getting tasks with parameters: $($params | ConvertTo-Json -Compress)"
                $uri = "$script:XoHost/rest/v0/tasks"
                $tasksResponse = Invoke-RestMethod -Uri $uri @script:XoRestParameters -Body $params

                if ($null -eq $tasksResponse -or $tasksResponse.Count -eq 0) {
                    Write-Verbose "No tasks found matching criteria"
                    return
                }

                Write-Verbose "Found $($tasksResponse.Count) tasks"

                foreach ($taskItem in $tasksResponse) {
                    ConvertTo-XoTaskObject -InputObject $taskItem
                }
            }
            catch {
                if ($PSBoundParameters.ContainsKey('Status')) {
                    throw ("Failed to retrieve tasks with status {0}: {1}" -f $Status, $_)
                }
                else {
                    throw ("Failed to retrieve tasks: {0}" -f $_)
                }
            }
        }
    }
}

New-Alias -Name Get-XoTaskDetails -Value Get-XoTask

function Wait-XoTask {
    <#
    .SYNOPSIS
        Wait for task completion.
    .DESCRIPTION
        Waits for the specified tasks to complete and optionally returns the result.
    .PARAMETER TaskId
        The ID(s) of the task(s) to wait for.
    .PARAMETER PassThru
        If specified, returns the task objects after completion.
    .EXAMPLE
        Wait-XoTask -TaskId "0m8k2zkzi"
        Waits for the task to complete.
    .EXAMPLE
        Wait-XoTask -TaskId "0m8k2zkzi" -PassThru
        Waits for the task to complete and returns the task object.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string[]]$TaskId,

        [Parameter()]
        [switch]$PassThru
    )

    begin {
        $params = @{
            fields = $script:XO_TASK_FIELDS
            wait   = "result"
        }
        $ids = @()
    }

    process {
        $ids += $TaskId
    }

    end {
        foreach ($id in $ids) {
            try {
                $uri = "$script:XoHost/rest/v0/tasks/$id"
                $result = Invoke-RestMethod -Uri $uri @script:XoRestParameters -Body $params

                if ($PassThru -and $result) {
                    ConvertTo-XoTaskObject -InputObject $result
                }
            }
            catch {
                throw ("Error waiting for task {0}: {1}" -f $id, $_)
            }
        }
    }
}