Private/PayloadEvents.ps1

function Get-FilteredHypervisorEvents {
    param(
        [NutanixHypervisorEvent[]]$Events,
        [Nullable[DateTime]]$CutoffUtc,
        [ref]$RemovedCount
    )

    $filteredEvents = [System.Collections.Generic.List[NutanixHypervisorEvent]]::new()
    foreach ($hypervisorEvent in $Events) {
        if (-not $hypervisorEvent -or [string]::IsNullOrWhiteSpace($hypervisorEvent.start_time)) {
            continue
        }

        if ($null -ne $CutoffUtc) {
            $eventTime = ConvertFrom-RfcUtcTimestampOrNull -Value $hypervisorEvent.start_time
            if ($eventTime -and $eventTime -le $CutoffUtc) {
                $RemovedCount.Value += 1
                continue
            }
        }

        $filteredEvents.Add($hypervisorEvent)
    }

    return ,$filteredEvents
}

function ConvertTo-FilteredHypervisorDataItem {
    param(
        [NutanixHypervisorDataItem]$DataItem,
        [Nullable[DateTime]]$CutoffUtc,
        [ref]$RemovedCount
    )

    if (-not $DataItem -or -not $DataItem.events -or $DataItem.events.Count -eq 0) {
        return $DataItem
    }

    $filteredEvents = Get-FilteredHypervisorEvents `
        -Events $DataItem.events `
        -CutoffUtc $CutoffUtc `
        -RemovedCount $RemovedCount
    if ($filteredEvents.Count -eq 0) {
        return $null
    }

    $newDataItem = [NutanixHypervisorDataItem]::new()
    $newDataItem.host = $DataItem.host
    $newDataItem.events = @($filteredEvents)
    $newDataItem.virtual_machines = $DataItem.virtual_machines
    return $newDataItem
}

function ConvertTo-FilteredHypervisorPayload {
    param(
        [NutanixHypervisorPayload]$Payload,
        [Nullable[DateTime]]$CutoffUtc,
        [ref]$RemovedCount
    )

    if (-not $Payload.data -or $Payload.data.Count -eq 0) {
        return $Payload
    }

    $newDataItems = [System.Collections.Generic.List[NutanixHypervisorDataItem]]::new()
    foreach ($dataItem in $Payload.data) {
        $newDataItem = ConvertTo-FilteredHypervisorDataItem `
            -DataItem $dataItem `
            -CutoffUtc $CutoffUtc `
            -RemovedCount $RemovedCount
        if ($null -ne $newDataItem) {
            $newDataItems.Add($newDataItem)
        }
    }

    if ($newDataItems.Count -eq 0) {
        return $null
    }

    $newPayload = [NutanixHypervisorPayload]::new()
    $newPayload.schema_version = $Payload.schema_version
    $newPayload.source = $Payload.source
    $newPayload.customer_environment = $Payload.customer_environment
    $newPayload.version = $Payload.version
    $newPayload.data = @($newDataItems)
    return $newPayload
}

function Remove-PayloadEventsByCutoff {
    <#
    .SYNOPSIS
        Filters out already-sent events from hypervisor payloads.

    .DESCRIPTION
        Removes events with startTime <= CutoffUtc to prevent re-sending
        data that was already sent in previous runs.

    .PARAMETER Payloads
        Array of NutanixHypervisorPayload objects to process.

    .PARAMETER CutoffUtc
        Events with startTime <= this timestamp are filtered out. If null, no filtering.

    .OUTPUTS
        Array of NutanixHypervisorPayload objects with filtered events.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [AllowEmptyCollection()]
        [AllowNull()]
        [NutanixHypervisorPayload[]]$Payloads,

        [Parameter(Mandatory = $false)]
        [Nullable[DateTime]]$CutoffUtc = $null
    )

    if (-not $Payloads -or $Payloads.Count -eq 0) {
        return @()
    }

    $removedCount = 0
    $result = [System.Collections.Generic.List[NutanixHypervisorPayload]]::new()

    foreach ($payload in $Payloads) {
        if ($null -eq $payload) {
            $result.Add($payload)
            continue
        }

        $newPayload = ConvertTo-FilteredHypervisorPayload `
            -Payload $payload `
            -CutoffUtc $CutoffUtc `
            -RemovedCount ([ref]$removedCount)
        if ($null -ne $newPayload) {
            $result.Add($newPayload)
        }
    }

    if ($removedCount -gt 0) {
        Write-CustomLog -Message "Filtered out $removedCount already-sent events" -Severity 'INFO'
    }

    return $result
}

function Get-PayloadEventsFiltered {
    <#
    .SYNOPSIS
        Filters payloads to remove events that were already sent.

    .DESCRIPTION
        Uses the last_sent_utc watermark from state to filter out events
        that have already been sent in previous runs.

    .PARAMETER Payloads
        Array of NutanixHypervisorPayload objects to filter.

    .PARAMETER State
        The connector state containing the last_sent_utc watermark.

    .OUTPUTS
        Array of NutanixHypervisorPayload objects with already-sent events removed.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [AllowEmptyCollection()]
        [AllowNull()]
        [NutanixHypervisorPayload[]]$Payloads,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        $State
    )

    $cutoffUtc = $null
    if ($null -ne $State -and $null -ne $State.watermarks -and -not [string]::IsNullOrWhiteSpace($State.watermarks.last_sent_utc)) {
        $cutoffUtc = ConvertFrom-RfcUtcTimestampOrNull -Value $State.watermarks.last_sent_utc
    }

    return Remove-PayloadEventsByCutoff -Payloads $Payloads -CutoffUtc $cutoffUtc
}