Modules/Private/Export-S2DWhatIfHtmlReport.ps1

# What-if HTML report — before/after capacity waterfall comparison

function Export-S2DWhatIfHtmlReport {
    param(
        [Parameter(Mandatory)] [object] $Result,
        [Parameter(Mandatory)] [string] $OutputPath
    )

    $bwf  = $Result.BaselineWaterfall
    $pwf  = $Result.ProjectedWaterfall
    $ds   = @($Result.DeltaStages)
    $gen  = (Get-Date -Format 'yyyy-MM-dd HH:mm')
    $scen = $Result.ScenarioLabel

    # Chart data
    $bLabels  = ($bwf.Stages | ForEach-Object { "'$($_.Name)'" }) -join ','
    $bValues  = ($bwf.Stages | ForEach-Object { if ($_.Size) { [math]::Round($_.Size.TiB,2) } else { 0 } }) -join ','
    $pValues  = ($pwf.Stages | ForEach-Object { if ($_.Size) { [math]::Round($_.Size.TiB,2) } else { 0 } }) -join ','

    # Delta rows
    $deltaRows = ($ds | ForEach-Object {
        $sign  = if ($_.DeltaTiB -gt 0) { '+' } elseif ($_.DeltaTiB -lt 0) { '' } else { '±' }
        $color = if ($_.DeltaTiB -gt 0) { '#107c10' } elseif ($_.DeltaTiB -lt 0) { '#d13438' } else { '#605e5c' }
        "<tr><td style='font-weight:700;color:#0078d4'>$($_.Stage)</td><td style='font-weight:600'>$($_.Name)</td><td style='text-align:right'>$($_.BaselineTiB) TiB</td><td style='text-align:right'>$($_.ProjectedTiB) TiB</td><td style='text-align:right;font-weight:700;color:$color'>$sign$($_.DeltaTiB) TiB</td></tr>"
    }) -join "`n"

    $deltaUsableSign  = if ($Result.DeltaUsableTiB -gt 0) { '+' } elseif ($Result.DeltaUsableTiB -lt 0) { '' } else { '±' }
    $deltaUsableColor = if ($Result.DeltaUsableTiB -gt 0) { '#107c10' } elseif ($Result.DeltaUsableTiB -lt 0) { '#d13438' } else { '#605e5c' }

    $html = @"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>S2D Cartographer — What-If Analysis</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<style>
:root{--blue:#0078d4;--green:#107c10;--red:#d13438;--amber:#e8a218;--bg:#faf9f8;--card:#fff;--border:#edebe9;--text:#201f1e;--muted:#605e5c}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',Arial,sans-serif;background:var(--bg);color:var(--text);font-size:14px}
header{background:var(--blue);color:white;padding:20px 32px;display:flex;justify-content:space-between;align-items:center}
header h1{font-size:22px;font-weight:600}
header .meta{font-size:12px;opacity:.85;text-align:right}
.container{max-width:1200px;margin:0 auto;padding:24px}
.section{background:var(--card);border:1px solid var(--border);border-radius:6px;margin-bottom:20px;padding:20px}
.section h2{font-size:16px;font-weight:600;margin-bottom:12px;padding-bottom:8px;border-bottom:2px solid var(--blue);color:var(--blue)}
.kpi-row{display:flex;gap:16px;flex-wrap:wrap;margin-bottom:16px}
.kpi{background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:14px 20px;text-align:center;min-width:140px}
.kpi .val{font-size:22px;font-weight:700;color:var(--blue)}.kpi .lbl{font-size:11px;color:var(--muted);margin-top:4px}
.kpi.positive .val{color:var(--green)}.kpi.negative .val{color:var(--red)}
.scenario-badge{display:inline-block;background:#eff6fc;color:#0078d4;border:1px solid #c7e0f4;border-radius:4px;padding:4px 12px;font-size:13px;font-weight:600;margin-bottom:16px}
.chart-grid{display:grid;grid-template-columns:1fr 1fr;gap:20px}
.chart-wrap{position:relative;height:320px}
.chart-label{font-size:12px;font-weight:600;color:var(--muted);margin-bottom:8px;text-transform:uppercase;letter-spacing:.5px}
table{width:100%;border-collapse:collapse;font-size:13px}
th{background:#f3f2f1;text-align:left;padding:8px 10px;font-weight:600;border-bottom:2px solid var(--border)}
td{padding:7px 10px;border-bottom:1px solid var(--border)}
tr:hover{background:#f3f2f1}
.delta-summary{background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:16px;margin-bottom:16px;display:flex;gap:32px;align-items:center}
</style>
</head>
<body>
<header>
  <div>
    <h1>S2D Cartographer — What-If Analysis</h1>
    <div style="font-size:13px;opacity:.9">Capacity impact modeling</div>
  </div>
  <div class="meta">Generated: $gen<br>Nodes: $($Result.BaselineNodeCount) → $($Result.ProjectedNodeCount)</div>
</header>
<div class="container">

<div class="section">
  <h2>Scenario</h2>
  <div class="scenario-badge">$scen</div>
  <div class="kpi-row">
    <div class="kpi"><div class="val">$($bwf.UsableCapacity.TiB) TiB</div><div class="lbl">Baseline Usable</div></div>
    <div class="kpi"><div class="val">$($pwf.UsableCapacity.TiB) TiB</div><div class="lbl">Projected Usable</div></div>
    <div class="kpi $(if($Result.DeltaUsableTiB -gt 0){'positive'}elseif($Result.DeltaUsableTiB -lt 0){'negative'}else{''})">
      <div class="val">$deltaUsableSign$($Result.DeltaUsableTiB) TiB</div><div class="lbl">Delta Usable</div>
    </div>
    <div class="kpi"><div class="val">$($bwf.ReserveStatus)</div><div class="lbl">Baseline Reserve</div></div>
    <div class="kpi"><div class="val">$($pwf.ReserveStatus)</div><div class="lbl">Projected Reserve</div></div>
    <div class="kpi"><div class="val">$($bwf.BlendedEfficiencyPercent)%</div><div class="lbl">Baseline Efficiency</div></div>
    <div class="kpi"><div class="val">$($pwf.BlendedEfficiencyPercent)%</div><div class="lbl">Projected Efficiency</div></div>
  </div>
</div>

<div class="section">
  <h2>Capacity Waterfall — Before vs After</h2>
  <div class="chart-grid">
    <div>
      <div class="chart-label">Baseline</div>
      <div class="chart-wrap"><canvas id="baseChart"></canvas></div>
    </div>
    <div>
      <div class="chart-label">Projected ($scen)</div>
      <div class="chart-wrap"><canvas id="projChart"></canvas></div>
    </div>
  </div>
</div>

<div class="section">
  <h2>Stage-by-Stage Delta</h2>
  <table>
    <thead><tr><th>#</th><th>Stage</th><th style="text-align:right">Baseline</th><th style="text-align:right">Projected</th><th style="text-align:right">Delta</th></tr></thead>
    <tbody>$deltaRows</tbody>
  </table>
</div>

</div>
<script>
const labels = [$bLabels];
const bVals = [$bValues];
const pVals = [$pValues];
const colors = ['#0078d4','#005a9e','#106ebe','#e8a218','#d47a00','#107c10','#0e6e0e','#054b05'];

function makeChart(id, vals) {
  new Chart(document.getElementById(id).getContext('2d'), {
    type: 'bar',
    data: { labels, datasets: [{ data: vals, backgroundColor: colors, borderRadius: 4 }] },
    options: {
      responsive: true, maintainAspectRatio: false, indexAxis: 'y',
      plugins: { legend: { display: false }, tooltip: { callbacks: { label: c => c.raw + ' TiB' } } },
      scales: { x: { beginAtZero: true, title: { display: true, text: 'TiB' } } }
    }
  });
}
makeChart('baseChart', bVals);
makeChart('projChart', pVals);
</script>
</body>
</html>
"@


    $dir = Split-Path $OutputPath -Parent
    if ($dir -and -not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
    $html | Set-Content -Path $OutputPath -Encoding UTF8 -Force
    $OutputPath
}