src/public/Execution/Invoke-AitherRetry.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
    Execute a script block with retry logic

.DESCRIPTION
    Executes a script block with configurable retry attempts and delays.
    Useful for operations that may fail transiently (network calls, file operations, etc.).

.PARAMETER ScriptBlock
    The script block to execute

.PARAMETER MaxAttempts
    Maximum number of attempts (default: 3)

.PARAMETER DelaySeconds
    Delay between attempts in seconds (default: 5)

.PARAMETER ErrorMessage
    Custom error message prefix

.EXAMPLE
    Invoke-AitherRetry -ScriptBlock {
        Invoke-WebRequest -Uri "https://example.com"
    } -MaxAttempts 5 -DelaySeconds 10

    Retry web request up to 5 times with 10 second delays

.EXAMPLE
    Invoke-AitherRetry -ScriptBlock {
        Copy-Item -Path $source -Destination $dest
    }

    Retry file copy with default settings (3 attempts, 5 second delay)

.OUTPUTS
    Object - The result of the script block execution

.NOTES
    Throws the last exception if all attempts fail.
    Logs each attempt for debugging.

.LINK
    Invoke-AitherScript
#>

function Invoke-AitherRetry {
[CmdletBinding()]
param(
    [Parameter()]
    [int]$DelaySeconds = 5,

    [Parameter()]
    [string]$ErrorMessage = "Operation failed",

    [Parameter(HelpMessage = "Show command output in console.")]
    [switch]$ShowOutput
)

begin {
    # Manage logging targets for this execution
    $originalLogTargets = $script:AitherLogTargets
    if ($ShowOutput) {
        if ($script:AitherLogTargets -notcontains 'Console') {
            $script:AitherLogTargets += 'Console'
        }
    }
    else {
        # Ensure Console is NOT in targets if ShowOutput is not specified
        $script:AitherLogTargets = $script:AitherLogTargets | Where-Object { $_ -ne 'Console' }
    }
}

process {
    try {
        # During module validation, skip execution
        if ($PSCmdlet.MyInvocation.InvocationName -eq '.' -and -not $ScriptBlock) {
            return
        }

        $hasWriteAitherLog = Get-Command Write-AitherLog -ErrorAction SilentlyContinue

        $attempt = 1
        $lastError = $null

        while ($attempt -le $MaxAttempts) {
            try {
                if ($hasWriteAitherLog) {
                    Write-AitherLog -Message "Attempt $attempt of $MaxAttempts" -Level Debug -Source 'Invoke-AitherRetry'
                }

                $result = & $ScriptBlock

                if ($hasWriteAitherLog) {
                    Write-AitherLog -Message "Operation succeeded on attempt $attempt" -Level Debug -Source 'Invoke-AitherRetry'
                }
                return $result
            }
            catch {
                $lastError = $_

                if ($hasWriteAitherLog) {
                    Write-AitherLog -Message "$ErrorMessage (attempt $attempt of $MaxAttempts): $($_.Exception.Message)" -Level Warning -Source 'Invoke-AitherRetry'
                }

                if ($attempt -lt $MaxAttempts) {
                    if ($hasWriteAitherLog) {
                        Write-AitherLog -Message "Retrying in $DelaySeconds seconds..." -Level Debug -Source 'Invoke-AitherRetry'
                    }
                    Start-Sleep -Seconds $DelaySeconds
                }

                $attempt++
            }
        }

        # If we get here, all attempts failed
        if ($hasWriteAitherLog) {
            Write-AitherLog -Message "$ErrorMessage - All $MaxAttempts attempts failed" -Level Error -Source 'Invoke-AitherRetry'
        }

        throw $lastError
    }
    finally {
        # Restore original log targets
        $script:AitherLogTargets = $originalLogTargets
    }
}

}