Private/jobs-functions.ps1

function Show-StuckJobs {
    param(
        [int]$PageSize = 10,
        [switch]$Html
    )

    if (-not $Global:MakeReport -and -not $Html) { Clear-Host }
    Write-Host "`n[⏳ Stuck Kubernetes Jobs]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching Job Data..." -ForegroundColor Yellow

    # Fetch jobs
    $kubectlOutput = kubectl get jobs --all-namespaces -o json 2>&1 | Out-String

    if (-not $Global:MakeReport -and -not $Html) { $thresholds = Get-KubeBuddyThresholds }
    else {
        $thresholds = Get-KubeBuddyThresholds -Silent
    }

    # Check for errors
    if ($kubectlOutput -match "error|not found|forbidden") {
        Write-Host "`r🤖 ❌ Error retrieving job data: $kubectlOutput" -ForegroundColor Red
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[⏳ Stuck Kubernetes Jobs]`n"
            Write-ToReport "❌ Error retrieving job data: $kubectlOutput"
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>❌ Error retrieving job data: $kubectlOutput</strong></p>" }
        return
    }

    if ($kubectlOutput -match "^{") {
        $jobs = $kubectlOutput | ConvertFrom-Json | Select-Object -ExpandProperty items
    }
    else {
        Write-Host "`r🤖 ❌ Unexpected response from kubectl. No valid JSON received." -ForegroundColor Red
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[⏳ Stuck Kubernetes Jobs]`n"
            Write-ToReport "❌ Unexpected response from kubectl. No valid JSON received."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>❌ Unexpected response from kubectl. No valid JSON received.</strong></p>" }
        return
    }

    if (-not $jobs -or $jobs.Count -eq 0) {
        Write-Host "`r🤖 ✅ No jobs found in the cluster." -ForegroundColor Green
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[⏳ Stuck Kubernetes Jobs]`n"
            Write-ToReport "✅ No jobs found in the cluster."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>✅ No jobs found in the cluster.</strong></p>" }
        return
    }

    Write-Host "`r🤖 ✅ Jobs fetched. (Total: $($jobs.Count))" -ForegroundColor Green
    Write-Host -NoNewline "`n🤖 Analyzing Stuck Jobs..." -ForegroundColor Yellow

    # Filter stuck jobs
    $stuckJobs = $jobs | Where-Object { 
        (-not $_.status.conditions -or $_.status.conditions.type -notcontains "Complete") -and # Not marked complete
        $_.status.PSObject.Properties['active'] -and $_.status.active -gt 0 -and # Has active pods
        (-not $_.status.PSObject.Properties['ready'] -or $_.status.ready -eq 0) -and # No ready pods
        (-not $_.status.PSObject.Properties['succeeded'] -or $_.status.succeeded -eq 0) -and # Not succeeded
        (-not $_.status.PSObject.Properties['failed'] -or $_.status.failed -eq 0) -and # Not failed
        $_.status.PSObject.Properties['startTime'] -and # Has a startTime
        ((New-TimeSpan -Start $_.status.startTime -End (Get-Date)).TotalHours -gt $thresholds.stuck_job_hours)
    }

    if (-not $stuckJobs -or $stuckJobs.Count -eq 0) {
        Write-Host "`r🤖 ✅ No stuck jobs found." -ForegroundColor Green
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[⏳ Stuck Kubernetes Jobs]`n"
            Write-ToReport "✅ No stuck jobs found."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>✅ No stuck jobs found.</strong></p>" }
        return
    }

    Write-Host "`r🤖 ✅ Job analysis complete. ($($stuckJobs.Count) stuck jobs detected)" -ForegroundColor Green

    # If -Html is specified, return an HTML table
    if ($Html) {
        # Build PSCustomObject array
        $tableData = foreach ($job in $stuckJobs) {
            $ns = $job.metadata.namespace
            $jobName = $job.metadata.name
            $ageHours = ((New-TimeSpan -Start $job.status.startTime -End (Get-Date)).TotalHours) -as [int]

            [PSCustomObject]@{
                Namespace = $ns
                Job       = $jobName
                Age_Hours = $ageHours
                Status    = "🟡 Stuck"
            }
        }

        # Convert to HTML
        $htmlTable = $tableData |
        ConvertTo-Html -Fragment -Property Namespace, Job, Age_Hours, Status |
        Out-String

        # Insert note about total
        $htmlTable = "<p><strong>⚠️ Total Stuck Jobs Found:</strong> $($stuckJobs.Count)</p>" + $htmlTable
        return $htmlTable
    }

    # If in report mode (no -Html), do original ASCII approach
    if ($Global:MakeReport) {
        Write-ToReport "`n[⏳ Stuck Kubernetes Jobs]`n"
        Write-ToReport "⚠️ Total Stuck Jobs Found: $($stuckJobs.Count)"
        Write-ToReport "---------------------------------------------"

        $tableData = @()
        foreach ($job in $stuckJobs) {
            $ns = $job.metadata.namespace
            $jobName = $job.metadata.name
            $ageHours = ((New-TimeSpan -Start $job.status.startTime -End (Get-Date)).TotalHours) -as [int]
            
            $tableData += [PSCustomObject]@{
                Namespace = $ns
                Job       = $jobName
                Age_Hours = $ageHours
                Status    = "🟡 Stuck"
            }
        }

        $tableString = $tableData | Format-Table Namespace, Job, Age_Hours, Status -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    # Otherwise, console pagination
    $totalJobs = $stuckJobs.Count
    $currentPage = 0
    $totalPages = [math]::Ceiling($totalJobs / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[⏳ Stuck Kubernetes Jobs - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        $msg = @(
            "🤖 Kubernetes Jobs should complete within a reasonable time.",
            "",
            "📌 This check identifies jobs that have been running too long and have not completed, failed, or succeeded.",
            "📌 Possible causes:",
            " - Stuck pods or unresponsive workloads",
            " - Misconfigured restart policies",
            " - Insufficient resources (CPU/Memory)",
            "",
            "⚠️ Investigate these jobs to determine the cause and resolve issues.",
            "",
            "⚠️ Total Stuck Jobs Found: $($stuckJobs.Count)"
        )
        if ($currentPage -eq 0) {
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 # first page only
        }

        $startIndex = $currentPage * $PageSize
        $endIndex = [math]::Min($startIndex + $PageSize, $totalJobs)

        $tableData = @()
        for ($i = $startIndex; $i -lt $endIndex; $i++) {
            $job = $stuckJobs[$i]
            $tableData += [PSCustomObject]@{
                Namespace = $job.metadata.namespace
                Job       = $job.metadata.name
                Age_Hours = ((New-TimeSpan -Start $job.status.startTime -End (Get-Date)).TotalHours) -as [int]
                Status    = "🟡 Stuck"
            }
        }

        if ($tableData) {
            $tableData | Format-Table Namespace, Job, Age_Hours, Status -AutoSize
        }

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage

    } while ($true)
}

function Show-FailedJobs {
    param(
        [int]$PageSize = 10,
        [switch]$Html
    )

    if (-not $Global:MakeReport -and -not $Html) { Clear-Host }
    Write-Host "`n[🔴 Failed Kubernetes Jobs]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching Job Data..." -ForegroundColor Yellow

    # Fetch jobs
    $kubectlOutput = kubectl get jobs --all-namespaces -o json 2>&1 | Out-String

    if (-not $Global:MakeReport -and -not $Html) { $thresholds = Get-KubeBuddyThresholds }
    else {
        $thresholds = Get-KubeBuddyThresholds -Silent
    }

    # Check for errors
    if ($kubectlOutput -match "error|not found|forbidden") {
        Write-Host "`r🤖 ❌ Error retrieving job data: $kubectlOutput" -ForegroundColor Red
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔴 Failed Kubernetes Jobs]`n"
            Write-ToReport "❌ Error retrieving job data: $kubectlOutput"
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>❌ Error retrieving job data: $kubectlOutput</strong></p>" }
        return
    }

    if ($kubectlOutput -match "^{") {
        $jobs = $kubectlOutput | ConvertFrom-Json | Select-Object -ExpandProperty items
    }
    else {
        Write-Host "`r🤖 ❌ Unexpected response from kubectl. No valid JSON received." -ForegroundColor Red
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔴 Failed Kubernetes Jobs]`n"
            Write-ToReport "❌ Unexpected response from kubectl. No valid JSON received."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>❌ Unexpected response from kubectl. No valid JSON received.</strong></p>" }
        return
    }

    if (-not $jobs -or $jobs.Count -eq 0) {
        Write-Host "`r🤖 ✅ No jobs found in the cluster." -ForegroundColor Green
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔴 Failed Kubernetes Jobs]`n"
            Write-ToReport "✅ No jobs found in the cluster."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>✅ No failed jobs found.</strong></p>" }
        return
    }

    Write-Host "`r🤖 ✅ Jobs fetched. (Total: $($jobs.Count))" -ForegroundColor Green
    Write-Host -NoNewline "`n🤖 Analyzing Failed Jobs..." -ForegroundColor Yellow

    # Filter failed jobs
    $failedJobs = $jobs | Where-Object { 
        $_.status.PSObject.Properties['failed'] -and $_.status.failed -gt 0 -and # Job has failed
        (-not $_.status.PSObject.Properties['succeeded'] -or $_.status.succeeded -eq 0) -and # Not succeeded
        $_.status.PSObject.Properties['startTime'] -and
        ((New-TimeSpan -Start $_.status.startTime -End (Get-Date)).TotalHours -gt $thresholds.failed_job_hours)
    }

    if (-not $failedJobs -or $failedJobs.Count -eq 0) {
        Write-Host "`r🤖 ✅ No failed jobs found." -ForegroundColor Green
        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[🔴 Failed Kubernetes Jobs]`n"
            Write-ToReport "✅ No failed jobs found."
        }
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) { return "<p><strong>✅ No failed jobs found.</strong></p>" }
        return
    }

    Write-Host "`r🤖 ✅ Job analysis complete. ($($failedJobs.Count) failed jobs detected)" -ForegroundColor Green

    # If -Html is specified, return an HTML table
    if ($Html) {
        $tableData = foreach ($job in $failedJobs) {
            [PSCustomObject]@{
                Namespace = $job.metadata.namespace
                Job       = $job.metadata.name
                Age_Hours = ((New-TimeSpan -Start $job.status.startTime -End (Get-Date)).TotalHours) -as [int]
                Failures  = if ($job.status.PSObject.Properties['failed']) { $job.status.failed } else { "Unknown" }
                Status    = "🔴 Failed"
            }
        }

        # Convert to HTML
        $htmlTable = $tableData |
        ConvertTo-Html -Fragment -Property Namespace, Job, Age_Hours, Failures, Status -PreContent "<h2>Failed Kubernetes Jobs</h2>" |
        Out-String

        # Insert note about total
        $htmlTable = "<p><strong>⚠️ Total Failed Jobs Found:</strong> $($failedJobs.Count)</p>" + $htmlTable
        return $htmlTable
    }

    # If in report mode (no -Html), do original ASCII approach
    if ($Global:MakeReport) {
        Write-ToReport "`n[🔴 Failed Kubernetes Jobs]`n"
        Write-ToReport "⚠️ Total Failed Jobs Found: $($failedJobs.Count)"
        Write-ToReport "---------------------------------------------"

        $tableData = @()
        foreach ($job in $failedJobs) {
            $ns = $job.metadata.namespace
            $jobName = $job.metadata.name
            $ageHours = ((New-TimeSpan -Start $job.status.startTime -End (Get-Date)).TotalHours) -as [int]
            $failCount = if ($job.status.PSObject.Properties['failed']) { $job.status.failed } else { "Unknown" }

            $tableData += [PSCustomObject]@{
                Namespace = $ns
                Job       = $jobName
                Age_Hours = $ageHours
                Failures  = $failCount
                Status    = "🔴 Failed"
            }
        }

        $tableString = $tableData | Format-Table Namespace, Job, Age_Hours, Failures, Status -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    # Otherwise, console pagination
    $totalJobs = $failedJobs.Count
    $currentPage = 0
    $totalPages = [math]::Ceiling($totalJobs / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[🔴 Failed Kubernetes Jobs - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        $msg = @(
            "🤖 Kubernetes Jobs should complete successfully.",
            "",
            "📌 This check identifies jobs that have encountered failures.",
            " - Jobs may fail due to insufficient resources, timeouts, or misconfigurations.",
            " - Review logs with 'kubectl logs job/<job-name>'",
            " - Investigate pod failures with 'kubectl describe job/<job-name>'",
            "",
            "⚠️ Consider re-running or debugging these jobs for resolution.",
            "",
            "⚠️ Total Failed Jobs Found: $($failedJobs.Count)"
        )
        if ($currentPage -eq 0) {
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 # first page only
        }

        $startIndex = $currentPage * $totalJobs / $PageSize
        $startIndex = $currentPage * $PageSize
        $endIndex = [math]::Min($startIndex + $PageSize, $totalJobs)

        $tableData = @()
        for ($i = $startIndex; $i -lt $endIndex; $i++) {
            $job = $failedJobs[$i]
            $tableData += [PSCustomObject]@{
                Namespace = $job.metadata.namespace
                Job       = $job.metadata.name
                Age_Hours = ((New-TimeSpan -Start $job.status.startTime -End (Get-Date)).TotalHours) -as [int]
                Failures  = if ($job.status.PSObject.Properties['failed']) { $job.status.failed } else { "Unknown" }
                Status    = "🔴 Failed"
            }
        }

        if ($tableData) {
            $tableData | Format-Table Namespace, Job, Age_Hours, Failures, Status -AutoSize
        }

        $newPage = Show-Pagination -currentPage $currentPage -totalPages $totalPages
        if ($newPage -eq -1) { break }
        $currentPage = $newPage

    } while ($true)
}