Modules/Public/New-S2DDiagram.ps1

function New-S2DDiagram {
    <#
    .SYNOPSIS
        Generates SVG diagrams for capacity waterfall, disk-node map, pool layout, and resiliency.

    .DESCRIPTION
        Accepts an S2DClusterData object and renders SVG diagrams to the output directory.

        Diagram types:
          Waterfall — 8-stage capacity waterfall horizontal bar diagram
          DiskNodeMap — Node boxes with disks color-coded by role and health
          PoolLayout — Storage pool allocation pie chart with reserve and infra segments
          Resiliency — Per-volume resiliency type, footprint, and efficiency
          HealthCard — Traffic-light health scorecard
          TiBTBReference — Common NVMe drive sizes in both TiB and TB

        Output files are written to OutputDirectory (default: C:\S2DCartographer).

    .PARAMETER InputObject
        S2DClusterData object from Invoke-S2DCartographer -PassThru. Accepts pipeline input.

    .PARAMETER DiagramType
        One or more diagram types to generate, or 'All'.

    .PARAMETER OutputDirectory
        Destination folder for SVG files. Created if it does not exist.

    .EXAMPLE
        Invoke-S2DCartographer -ClusterName tplabs-clus01 -PassThru | New-S2DDiagram

    .EXAMPLE
        New-S2DDiagram -InputObject $data -DiagramType Waterfall, DiskNodeMap

    .OUTPUTS
        string[] — paths to generated SVG files
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(ValueFromPipeline, Mandatory)]
        [object] $InputObject,

        [Parameter()]
        [ValidateSet('Waterfall','DiskNodeMap','PoolLayout','Resiliency','HealthCard','TiBTBReference','All')]
        [string[]] $DiagramType = 'All',

        [Parameter()]
        [string] $OutputDirectory = 'C:\S2DCartographer'
    )

    process {
        if ($InputObject -isnot [S2DClusterData]) {
            throw "InputObject must be an S2DClusterData object. Use Invoke-S2DCartographer -PassThru to obtain one."
        }

        $effectiveTypes = if ('All' -in $DiagramType) {
            @('Waterfall','DiskNodeMap','PoolLayout','Resiliency','HealthCard','TiBTBReference')
        } else { $DiagramType }

        if (-not (Test-Path $OutputDirectory)) {
            New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
        }

        $cn    = $InputObject.ClusterName -replace '[^\w\-]', '_'
        $stamp = Get-Date -Format 'yyyyMMdd-HHmm'
        $nc    = $InputObject.NodeCount

        $outputFiles = @()

        foreach ($type in $effectiveTypes) {
            $path = Join-Path $OutputDirectory "${cn}_${type}_${stamp}.svg"
            Write-Verbose "Generating $type diagram → $path"

            try {
                $svg = switch ($type) {
                    'Waterfall' {
                        if ($InputObject.CapacityWaterfall) {
                            New-S2DWaterfallSvg -Waterfall $InputObject.CapacityWaterfall
                        } else { Write-Warning "Waterfall: no CapacityWaterfall data."; $null }
                    }
                    'DiskNodeMap' {
                        if ($InputObject.PhysicalDisks) {
                            New-S2DDiskNodeMapSvg -PhysicalDisks $InputObject.PhysicalDisks
                        } else { Write-Warning "DiskNodeMap: no PhysicalDisk data."; $null }
                    }
                    'PoolLayout' {
                        if ($InputObject.StoragePool -and $InputObject.CapacityWaterfall) {
                            New-S2DPoolLayoutSvg -Pool $InputObject.StoragePool -Waterfall $InputObject.CapacityWaterfall -Volumes $InputObject.Volumes
                        } else { Write-Warning "PoolLayout: pool or waterfall data missing."; $null }
                    }
                    'Resiliency' {
                        if ($InputObject.Volumes) {
                            New-S2DVolumeResiliencySvg -Volumes $InputObject.Volumes -NodeCount $nc
                        } else { Write-Warning "Resiliency: no volume data."; $null }
                    }
                    'HealthCard' {
                        if ($InputObject.HealthChecks) {
                            New-S2DHealthScorecardSvg -HealthChecks $InputObject.HealthChecks -OverallHealth $InputObject.OverallHealth
                        } else { Write-Warning "HealthCard: no health check data."; $null }
                    }
                    'TiBTBReference' {
                        New-S2DTiBTBReferenceSvg
                    }
                }

                if ($svg) {
                    $svg | Set-Content -Path $path -Encoding UTF8 -Force
                    $outputFiles += $path
                }
            }
            catch {
                Write-Warning "$type diagram failed: $_"
            }
        }

        $outputFiles
    }
}