Private/namespaces-functions.ps1

function Show-EmptyNamespaces {
    param(
        [int]$PageSize = 10, # Number of namespaces per page
        [switch]$Html, # If specified, return an HTML table
        [object]$kubeData,
        [switch]$json,
        [switch]$ExcludeNamespaces
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $json) { Clear-Host }
    Write-Host "`n[📂 Empty Namespaces]" -ForegroundColor Cyan
    Write-Host -NoNewline "`n🤖 Fetching Namespace Data..." -ForegroundColor Yellow

    # Fetch full namespace objects first
    if ($KubeData -and $KubeData.Namespaces) {
        $allNamespaces = $KubeData.Namespaces
    } else {
        $allNamespaces = kubectl get namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
    }
    
    # Apply exclusion early, while they’re still objects
    if ($ExcludeNamespaces) {
        $allNamespaces = Exclude-Namespaces -items $allNamespaces
    }
    
    # Now extract the namespace names to check
    $allNsNames = $allNamespaces | ForEach-Object { $_.metadata.name.Trim() }
    
    # Get pod namespaces
    if ($KubeData -and $KubeData.Pods) {
        $pods = $KubeData.Pods.items |
            Where-Object { $_.metadata.namespace -and $_.metadata.namespace.Trim() -ne "" } |
            Group-Object { $_.metadata.namespace.Trim() }
    } else {
        $pods = kubectl get pods --all-namespaces -o json | ConvertFrom-Json |
            Select-Object -ExpandProperty items |
            Where-Object { $_.metadata.namespace } |
            Group-Object { $_.metadata.namespace }
    }
    
    $namespacesWithPods = $pods | ForEach-Object { $_.Name.Trim() }
    
    # Calculate empty namespaces
    $emptyNamespaces = $allNsNames | Where-Object { $_ -notin $namespacesWithPods }


    # Force split into an array if it's a multiline string
    if ($emptyNamespaces -is [string]) {
        $emptyNamespaces = $emptyNamespaces -split "`n" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
    } else {
        $emptyNamespaces = @($emptyNamespaces)
    }


    $totalNamespaces = $emptyNamespaces.Count

    if ($totalNamespaces -eq 0) {
        Write-Host "`r🤖 ✅ No empty namespaces found." -ForegroundColor Green

        if ($Json) {
            return [pscustomobject]@{
                TotalEmptyNamespaces = 0
                Namespaces           = @()
            }
        }

        if ($Global:MakeReport -and -not $Html) {
            Write-ToReport "`n[📂 Empty Namespaces]`n"
            Write-ToReport "✅ No empty namespaces found."
        }

        # If not in report mode or HTML mode, prompt to continue
        if (-not $Global:MakeReport -and -not $Html) {
            Read-Host "🤖 Press Enter to return to the menu"
        }
        if ($Html) {
            return "<p><strong>✅ No empty namespaces found.</strong></p>"
        }
        return
    }

    Write-Host "`r🤖 ✅ Namespaces fetched. ($totalNamespaces empty namespaces detected)" -ForegroundColor Green


    if ($Json) {
        return [pscustomobject]@{
            TotalEmptyNamespaces = $totalNamespaces
            Namespaces           = $emptyNamespaces
        }
    }

    # ----- HTML SWITCH -----
    if ($Html) {
        # Build an HTML table. Each row => one namespace
        # Convert the array into PSCustomObjects first
        $namespacesData = $emptyNamespaces | ForEach-Object {
            [PSCustomObject]@{
                "Namespace" = $_
            }
        }

        # Convert to HTML
        $htmlTable = $namespacesData |
        ConvertTo-Html -Fragment -Property "Namespace" |
        Out-String

        # Insert a note about total empty
        $htmlTable = "<p><strong>⚠️ Total Empty Namespaces:</strong> $totalNamespaces</p>" + $htmlTable

        return $htmlTable
    }
    # ----- END HTML SWITCH -----

    # ----- If in report mode, but no -Html switch, do original ascii printing -----
    if ($Global:MakeReport) {
        Write-ToReport "`n[📂 Empty Namespaces]`n"
        Write-ToReport "⚠️ Total Empty Namespaces: $totalNamespaces"
        Write-ToReport "---------------------------------"
        foreach ($namespace in $emptyNamespaces) {
            Write-ToReport "$namespace"
        }
        return
    }

    # ----- Otherwise, do console pagination as before -----
    $currentPage = 0
    $totalPages = [math]::Ceiling($totalNamespaces / $PageSize)

    do {
        Clear-Host
        Write-Host "`n[📂 Empty Namespaces - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        # Speech bubble
        $msg = @(
            "🤖 Empty namespaces exist but contain no running pods.",
            "",
            "📌 These may be unused namespaces that can be cleaned up.",
            "📌 If needed, verify if they contain other resources (Secrets, PVCs).",
            "📌 Deleting an empty namespace will remove all associated resources.",
            "",
            "⚠️ Total Empty Namespaces: $totalNamespaces"
        )

        
        if ($currentPage -eq 0) {
            Write-SpeechBubble -msg $msg -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50 # first page only
        }

        # Display current page
        $startIndex = $currentPage * $PageSize
        $endIndex = [math]::Min($startIndex + $PageSize, $totalNamespaces)
        
        $tableData = $emptyNamespaces | Select-Object -Skip $startIndex -First ($endIndex - $startIndex) | ForEach-Object {
            [PSCustomObject]@{ Namespace = $_ }
        }
        
        if ($tableData) {
            $tableData | Format-Table Namespace -AutoSize | Out-Host
        }
        

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

function Check-ResourceQuotas {
    param(
        [object]$KubeData,
        [string]$Namespace = "",
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$Json,
        [switch]$ExcludeNamespaces
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[📊 Missing or Weak ResourceQuotas]" -ForegroundColor Cyan
    if (-not $Global:MakeReport -and -not $Html -and -not $Json) {
        Write-Host -NoNewline "`n🤖 Fetching ResourceQuota data..." -ForegroundColor Yellow
    }

    try {
        $quotas = if ($KubeData -and $KubeData.ResourceQuotas) {
            $KubeData.ResourceQuotas
        } else {
            $raw = if ($Namespace) {
                kubectl get resourcequotas -n $Namespace -o json 2>&1
            } else {
                kubectl get resourcequotas --all-namespaces -o json 2>&1
            }
            if ($raw -match "No resources found") {
                $quotas = @()
            } else {
                ($raw | ConvertFrom-Json).items
            }
        }

        $namespaces = if ($KubeData -and $KubeData.Namespaces) {
            $KubeData.Namespaces
        } else {
            kubectl get namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    } catch {
        Write-Host "`r🤖 ❌ Error retrieving ResourceQuota data: $_" -ForegroundColor Red
        if ($Html) { return "<p><strong>❌ Error retrieving ResourceQuota data.</strong></p>" }
        if ($Json) { return @{ Error = "$_" } }
        return
    }

    if ($ExcludeNamespaces) {
        $namespaces = Exclude-Namespaces -items $namespaces
        $quotaNamespaces = $namespaces | ForEach-Object { $_.metadata.name }
        $quotas = $quotas | Where-Object { $_.metadata.namespace -in $quotaNamespaces }
    }    

    $results = @()
    foreach ($ns in $namespaces) {
        $nsName = $ns.metadata.name
        $nsQuotas = $quotas | Where-Object { $_.metadata.namespace -eq $nsName }

        if (-not $nsQuotas) {
            $results += [PSCustomObject]@{
                Namespace = $nsName
                Issue     = "❌ No ResourceQuota defined"
            }
        } else {
            $hasCPU = $false
            $hasMemory = $false
            $hasPods = $false

            foreach ($quota in $nsQuotas) {
                $scopes = $quota.status.hard.PSObject.Properties.Name
                if ($scopes -contains "requests.cpu" -or $scopes -contains "limits.cpu") { $hasCPU = $true }
                if ($scopes -contains "requests.memory" -or $scopes -contains "limits.memory") { $hasMemory = $true }
                if ($scopes -contains "pods") { $hasPods = $true }
            }

            if (-not ($hasCPU -and $hasMemory -and $hasPods)) {
                $missing = @()
                if (-not $hasCPU) { $missing += "CPU" }
                if (-not $hasMemory) { $missing += "Memory" }
                if (-not $hasPods) { $missing += "Pods" }
                $results += [PSCustomObject]@{
                    Namespace = $nsName
                    Issue     = "⚠️ Missing: $($missing -join ', ')"
                }
            }
        }
    }

    $total = $results.Count
    if ($total -eq 0) {
        if ($Html) { return "<p><strong>✅ All namespaces have strong ResourceQuotas.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        Write-Host "`r🤖 ✅ All namespaces have strong ResourceQuotas." -ForegroundColor Green
        return
    }

    Write-Host "`r🤖 ✅ ResourceQuota issues found. ($total affected namespaces)" -ForegroundColor Green

    if ($Json) { return @{ Total = $total; Items = $results } }

    if ($Html) {
        $htmlTable = $results | Sort-Object Namespace |
            ConvertTo-Html -Fragment -Property Namespace, Issue | Out-String
        return "<p><strong>⚠️ Namespaces with ResourceQuota issues:</strong> $total</p>" + $htmlTable
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[📊 Missing or Weak ResourceQuotas]`n"
        Write-ToReport "⚠️ Total Issues: $total"
        $tableString = $results | Format-Table Namespace, Issue -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $currentPage = 0
    $totalPages = [math]::Ceiling($total / $PageSize)
    do {
        Clear-Host
        Write-Host "`n[📊 ResourceQuota Issues - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            Write-SpeechBubble -msg @(
                "🤖 These namespaces lack full ResourceQuota enforcement.",
                "",
                "📌 Why this matters:",
                " - Quotas protect the cluster from resource abuse.",
                " - Helps prevent noisy neighbor issues.",
                "",
                "⚠️ Total Issues: $total"
            ) -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $paged = $results | Select-Object -Skip ($currentPage * $PageSize) -First $PageSize
        if ($paged) {
            $paged | Format-Table Namespace, Issue -AutoSize | Out-Host
        }

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

function Check-NamespaceLimitRanges {
    param(
        [object]$KubeData,
        [int]$PageSize = 10,
        [switch]$Html,
        [switch]$Json,
        [switch]$ExcludeNamespaces
    )

    if (-not $Global:MakeReport -and -not $Html -and -not $Json) { Clear-Host }
    Write-Host "`n[📐 Missing LimitRanges]" -ForegroundColor Cyan
    if (-not $Global:MakeReport -and -not $Html -and -not $Json) {
        Write-Host -NoNewline "`n🤖 Fetching LimitRange data..." -ForegroundColor Yellow
    }

    try {
        $limitRanges = if ($KubeData -and $KubeData.LimitRanges) {
            $KubeData.LimitRanges
        } else {
            kubectl get limitranges --all-namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }

        $namespaces = if ($KubeData -and $KubeData.Namespaces) {
            $KubeData.Namespaces
        } else {
            kubectl get namespaces -o json | ConvertFrom-Json | Select-Object -ExpandProperty items
        }
    } catch {
        Write-Host "`r🤖 ❌ Error fetching LimitRanges: $_" -ForegroundColor Red
        if ($Html) { return "<p><strong>❌ Error retrieving LimitRange data.</strong></p>" }
        if ($Json) { return @{ Error = "$_" } }
        return
    }

    if ($ExcludeNamespaces) {
        $namespaces = Exclude-Namespaces -items $namespaces
        $validNamespaces = $namespaces | ForEach-Object { $_.metadata.name }
        $limitRanges = $limitRanges | Where-Object { $_.metadata.namespace -in $validNamespaces }
    }

    $results = @()

    foreach ($ns in $namespaces) {
        $nsName = $ns.metadata.name
        $hasLimitRange = $limitRanges | Where-Object { $_.metadata.namespace -eq $nsName }

        if (-not $hasLimitRange) {
            $results += [PSCustomObject]@{
                Namespace = $nsName
                Issue     = "❌ No LimitRange defined"
            }
        }
    }

    $total = $results.Count
    if ($total -eq 0) {
        if ($Html) { return "<p><strong>✅ All namespaces have a LimitRange.</strong></p>" }
        if ($Json) { return @{ Total = 0; Items = @() } }
        Write-Host "`r🤖 ✅ All namespaces have a LimitRange." -ForegroundColor Green
        return
    }

    Write-Host "`r🤖 ✅ LimitRange issues found. ($total namespaces affected)" -ForegroundColor Green

    if ($Json) {
        return @{ Total = $total; Items = $results }
    }

    if ($Html) {
        $htmlTable = $results | Sort-Object Namespace |
            ConvertTo-Html -Fragment -Property Namespace, Issue | Out-String
        return "<p><strong>⚠️ Namespaces missing LimitRanges:</strong> $total</p>" + $htmlTable
    }

    if ($Global:MakeReport) {
        Write-ToReport "`n[📐 Missing LimitRanges]`n⚠️ Total: $total"
        $tableString = $results | Format-Table Namespace, Issue -AutoSize | Out-String
        Write-ToReport $tableString
        return
    }

    $currentPage = 0
    $totalPages = [math]::Ceiling($total / $PageSize)
    do {
        Clear-Host
        Write-Host "`n[📐 LimitRange Issues - Page $($currentPage + 1) of $totalPages]" -ForegroundColor Cyan

        if ($currentPage -eq 0) {
            Write-SpeechBubble -msg @(
                "🤖 LimitRanges define default and max CPU/memory limits for containers.",
                "",
                "📌 Why this matters:",
                " - Prevents runaway pods from consuming unbounded resources.",
                " - Sets defaults if workloads don’t define them explicitly.",
                "",
                "⚠️ Total affected namespaces: $total"
            ) -color "Cyan" -icon "🤖" -lastColor "Red" -delay 50
        }

        $paged = $results | Select-Object -Skip ($currentPage * $PageSize) -First $PageSize
        if ($paged) {
            $paged | Format-Table Namespace, Issue -AutoSize | Out-Host
        }

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