Private/create-jsonReport.ps1

function Create-JsonReport {
    param (
        [string]$OutputPath,
        [object]$KubeData,
        [switch]$ExcludeNamespaces,
        [switch]$aks,
        [string]$SubscriptionId,
        [string]$ResourceGroup,
        [string]$ClusterName
    )

    # Get cluster metadata
    $clusterName = (kubectl config current-context)
    $versionInfo = kubectl version -o json | ConvertFrom-Json
    $k8sVersion = $versionInfo.serverVersion.gitVersion
    $generatedAt = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")

    # Initialize results structure
    $results = @{
        metadata = @{
            clusterName       = $clusterName
            kubernetesVersion = $k8sVersion
            generatedAt       = $generatedAt
        }
        checks   = @{}
    }

    # Run YAML-based checks
    $yamlCheckResults = Invoke-yamlChecks -Json -KubeData $KubeData -ExcludeNamespaces:$ExcludeNamespaces

    # Flatten YAML checks into checks map
    $checksMap = @{}
    foreach ($check in $yamlCheckResults.Items) {
        if ($check.ID) {
            $checksMap[$check.ID] = $check
        }
    }

    # Handle AKS checks if -aks switch is provided
    if ($aks -and $KubeData.AksCluster) {
        # Add filtered AKS metadata
        $results.metadata.aks = @{
            subscriptionId = $KubeData.AksCluster.subscriptionId
            resourceGroup  = $KubeData.AksCluster.resourceGroup
            clusterName    = $KubeData.AksCluster.clusterName
            # Add other relevant AKS metadata as needed
        }

        # Run AKS best practices checks
        try {
            $aksCheckResults = Invoke-AKSBestPractices -Json -KubeData $KubeData -SubscriptionId $SubscriptionId -ResourceGroup $ResourceGroup -ClusterName $ClusterName

            # Integrate AKS checks into checksMap
            if ($aksCheckResults -and $aksCheckResults.Items) {
                foreach ($aksCheck in $aksCheckResults.Items) {
                    if ($aksCheck.ID) {
                        $checksMap[$aksCheck.ID] = $aksCheck
                    }
                }
            }
            else {
                Write-Warning "Invoke-AKSBestPractices returned no valid check results."
                $checksMap['AKSBestPractices'] = @{
                    ID      = 'AKSBestPractices'
                    Name    = 'AKS Best Practices'
                    Message = 'No AKS best practices checks returned.'
                    Total   = 0
                    Items   = @()
                }
            }
        }
        catch {
            Write-Warning "Failed to run AKS best practices: $_"
            $checksMap['AKSBestPractices'] = @{
                ID      = 'AKSBestPractices'
                Name    = 'AKS Best Practices'
                Message = "Error running AKS checks: $_"
                Total   = 0
                Items   = @()
            }
        }
    }

    # Assign checks to results
    $results.checks = $checksMap

    # Calculate score from all checks
    $allChecks = $yamlCheckResults.Items

    $validChecks = $allChecks | Where-Object {
        $_.Weight -ne $null -and
        -not $_.Error -and
        $_.ID -ne $null
    }
    
    $results.metadata.score = Get-ClusterHealthScore -Checks $validChecks
    
    # ----- Add Prometheus Metrics to JSON -----
    if ($KubeData.PrometheusMetrics) {
        # Cluster-level averages
        $cpuValues = $KubeData.PrometheusMetrics.NodeCpuUsagePercent | ForEach-Object { $_.values | ForEach-Object { [double]$_[1] } }
        $memValues = $KubeData.PrometheusMetrics.NodeMemoryUsagePercent | ForEach-Object { $_.values | ForEach-Object { [double]$_[1] } }
        $clusterMetrics = [PSCustomObject]@{
            avgCpuPercent = [math]::Round(($cpuValues | Measure-Object -Average).Average, 2)
            avgMemPercent = [math]::Round(($memValues | Measure-Object -Average).Average, 2)
            cpuTimeSeries = (
                $KubeData.PrometheusMetrics.NodeCpuUsagePercent |
                ForEach-Object { $_.values | ForEach-Object { [PSCustomObject]@{ timestamp = [int64]($_[0] * 1000); value = [double]$_[1] } } } |
                Group-Object timestamp |
                ForEach-Object { [PSCustomObject]@{ timestamp = $_.Name; value = [math]::Round(($_.Group | Measure-Object -Property value -Average).Average, 2) } } |
                Sort-Object timestamp
            )
            memTimeSeries = (
                $KubeData.PrometheusMetrics.NodeMemoryUsagePercent |
                ForEach-Object { $_.values | ForEach-Object { [PSCustomObject]@{ timestamp = [int64]($_[0] * 1000); value = [double]$_[1] } } } |
                Group-Object timestamp |
                ForEach-Object { [PSCustomObject]@{ timestamp = $_.Name; value = [math]::Round(($_.Group | Measure-Object -Property value -Average).Average, 2) } } |
                Sort-Object timestamp
            )
        }

        # Node-level metrics
        $nodeMetricsList = @()
        foreach ($node in $KubeData.Nodes.items) {
            $name = $node.metadata.name
        
            $cpuMatch = $KubeData.PrometheusMetrics.NodeCpuUsagePercent | Where-Object { $_.metric.instance -match $name }
            $memMatch = $KubeData.PrometheusMetrics.NodeMemoryUsagePercent | Where-Object { $_.metric.instance -match $name }
            $diskMatch = $KubeData.PrometheusMetrics.NodeDiskUsagePercent | Where-Object { $_.metric.instance -match $name }
        
            $cpuSeries = if ($cpuMatch -and $cpuMatch.values) {
                $cpuMatch.values | ForEach-Object {
                    [PSCustomObject]@{ timestamp = [int64]($_[0] * 1000); value = [double]$_[1] }
                }
            } else { @() }
        
            $memSeries = if ($memMatch -and $memMatch.values) {
                $memMatch.values | ForEach-Object {
                    [PSCustomObject]@{ timestamp = [int64]($_[0] * 1000); value = [double]$_[1] }
                }
            } else { @() }
        
            $diskSeries = if ($diskMatch -and $diskMatch.values) {
                $diskMatch.values | ForEach-Object {
                    [PSCustomObject]@{ timestamp = [int64]($_[0] * 1000); value = [double]$_[1] }
                }
            } else { @() }
        
            $nodeMetricsList += [PSCustomObject]@{
                nodeName   = $name
                cpuAvg     = if ($cpuSeries.Count -gt 0) { [math]::Round(($cpuSeries.value | Measure-Object -Average).Average, 2) } else { 'N/A' }
                memAvg     = if ($memSeries.Count -gt 0) { [math]::Round(($memSeries.value | Measure-Object -Average).Average, 2) } else { 'N/A' }
                diskAvg    = if ($diskSeries.Count -gt 0) { [math]::Round(($diskSeries.value | Measure-Object -Average).Average, 2) } else { 'N/A' }
                cpuSeries  = $cpuSeries
                memSeries  = $memSeries
                diskSeries = $diskSeries
            }
        }

        $results.metrics = @{ cluster = $clusterMetrics; nodes = $nodeMetricsList }
    }

    # Write JSON
    $results | ConvertTo-Json -Depth 10 | Set-Content -Encoding UTF8 $OutputPath
}