Core/Execution.psm1

<#
    .SYNOPSIS
    CCF Execution Engine - Parallel Computing (Optimized)
     
    .DESCRIPTION
    Proporciona capacidades de ejecución paralela utilizando RunspacePools.
    Incluye lógica de reintentos automáticos y telemetría de rendimiento.
#>


function Invoke-CCFParallel {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [scriptblock]$ScriptBlock,
        
        [Parameter(Mandatory = $true)]
        [array]$InputObjects,
        
        [int]$MaxThreads = [int]$env:NUMBER_OF_PROCESSORS,
        
        [hashtable]$Arguments = @{},
        
        [int]$RetryCount = 0,
        
        [int]$RetryDelayMs = 500
    )
    
    $totalTimer = [System.Diagnostics.Stopwatch]::StartNew()
    Log-Info "Inicializando motor paralelo (Hilos: $MaxThreads, Reintentos: $RetryCount)"
    
    if ($MaxThreads -lt 2) { $MaxThreads = 2 }
    
    $rsPool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads)
    $rsPool.Open()
    
    $jobs = New-Object System.Collections.Generic.List[PSCustomObject]
    $finalResults = New-Object System.Collections.Generic.List[PSCustomObject]

    # ScriptBlock Wrapper para Telemetría y Errores
    $wrapper = {
        param($obj, $args, $sb, $tries)
        $timer = [System.Diagnostics.Stopwatch]::StartNew()
        $currentTry = 0
        $success = $false
        $output = $null
        $lastError = $null

        while ($currentTry -le $tries -and -not $success) {
            try {
                $output = & $sb $obj $args
                $success = $true
            }
            catch {
                $lastError = $_.Exception.Message
                $currentTry++
                if ($currentTry -le $tries) { Start-Sleep -Milliseconds 200 }
            }
        }
        $timer.Stop()
        return [PSCustomObject]@{
            Output   = $output
            Duration = $timer.Elapsed.TotalSeconds
            Success  = $success
            Error    = $lastError
            Retries  = $currentTry
        }
    }
    
    try {
        foreach ($obj in $InputObjects) {
            $ps = [PowerShell]::Create().AddScript($wrapper)
            $ps.RunspacePool = $rsPool
            $ps.AddArgument($obj).AddArgument($Arguments).AddArgument($ScriptBlock).AddArgument($RetryCount)
            
            $jobs.Add([PSCustomObject]@{
                    PowerShell = $ps
                    Handle     = $ps.BeginInvoke()
                    Target     = $obj
                })
        }
        
        while ($jobs.Count -gt 0) {
            $done = $jobs | Where-Object { $_.Handle.IsCompleted }
            foreach ($job in $done) {
                try {
                    $res = $job.PowerShell.EndInvoke($job.Handle)
                    $finalResults.Add([PSCustomObject]@{
                            Target   = $job.Target
                            Output   = $res.Output
                            Duration = $res.Duration
                            Status   = if ($res.Success) { "Success" } else { "Failed" }
                            Retries  = $res.Retries
                            Error    = $res.Error
                        })
                }
                catch {
                    Log-Error "Fallo fatal en Runspace para $($job.Target)"
                }
                finally {
                    $job.PowerShell.Dispose()
                    $jobs.Remove($job) | Out-Null
                }
            }
            if ($jobs.Count -gt 0) { Start-Sleep -Milliseconds 100 }
        }
        
        $totalTimer.Stop()
        Log-Success "Paralelización finalizada en $($totalTimer.Elapsed.TotalSeconds)s."
        return $finalResults
    }
    finally {
        $rsPool.Close()
        $rsPool.Dispose()
    }
}

Export-ModuleMember -Function Invoke-CCFParallel