src/Private/New-ReclaimReportHtml.ps1

function New-ReclaimReportHtml {
    <#
    .SYNOPSIS
        Renders the scan report to a self-contained HTML file.
    .DESCRIPTION
        Free mode shows the headline number and the by-SKU rollup, then a locked call-to-action
        in place of the named accounts. Full mode renders the full per-account remediation table.
        Styling is inline and deliberately enterprise-restrained (navy + muted teal) to match the
        Optera AI vendor site - no gradient/AI-slop look.
    .OUTPUTS
        The path written.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [object] $Report,
        [Parameter(Mandatory)] [string] $Path
    )

    function _enc([object]$v) { [System.Net.WebUtility]::HtmlEncode([string]$v) }

    $s = $Report.Summary
    $generated = '{0:yyyy-MM-dd HH:mm} UTC' -f $Report.GeneratedAt.ToUniversalTime()

    $skuRows = ($s.BySku | ForEach-Object {
        '<tr><td>{0}</td><td class="num">{1}</td><td class="num">${2:N2}</td><td class="num">${3:N2}</td></tr>' -f `
            (_enc $_.Name), $_.Count, $_.MonthlyEachUsd, $_.MonthlyTotalUsd
    }) -join "`n"

    # Owned-vs-reclaimable table when tenant inventory (subscribedSkus) is available; otherwise the
    # plain by-type rollup. Both are aggregate (no per-account data) so they show in Free and Full.
    if ($Report.Inventory -and @($Report.Inventory).Count) {
        $invRows = ($Report.Inventory | ForEach-Object {
            $owned    = if ($null -ne $_.PrepaidUnits)  { '{0:N0}' -f $_.PrepaidUnits }  else { '&mdash;' }
            $assigned = if ($null -ne $_.ConsumedUnits) { '{0:N0}' -f $_.ConsumedUnits } else { '&mdash;' }
            '<tr><td>{0}</td><td class="num">{1}</td><td class="num">{2}</td><td class="num">{3}</td><td class="num">${4:N2}</td></tr>' -f `
                (_enc $_.Name), $owned, $assigned, $_.ReclaimableUnits, $_.ReclaimableMonthlyUsd
        }) -join "`n"
        $byTypeSection = @"
  <h2>Reclaimable licenses by type</h2>
  <p class="note">A breakdown of the reclaimable licenses above. <strong>Owned</strong> and <strong>Assigned</strong> are your tenant's prepaid and consumed seats for each license; <strong>On dead/stale</strong> are the seats still assigned to accounts that are disabled or inactive on-premises.</p>
  <table class="grid">
    <thead><tr><th>License</th><th class="num">Owned</th><th class="num">Assigned</th><th class="num">On dead/stale</th><th class="num">`$/mo</th></tr></thead>
    <tbody>
$invRows
    </tbody>
  </table>
"@

    }
    else {
        $byTypeSection = @"
  <h2>By license type</h2>
  <table class="grid">
    <thead><tr><th>License</th><th class="num">Count</th><th class="num">List `$/mo each</th><th class="num">`$/mo total</th></tr></thead>
    <tbody>
$skuRows
    </tbody>
  </table>
"@

    }

    if ($Report.Mode -eq 'Full') {
        $accountRows = ($Report.Reclaimable | Sort-Object MonthlyWasteUsd -Descending | ForEach-Object {
            $skus = ($_.Skus | ForEach-Object { _enc $_.Name }) -join ', '
            '<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td class="num">${5:N2}</td></tr>' -f `
                (_enc $_.DisplayName), (_enc $_.UserPrincipalName), (_enc $_.Reason), (_enc $_.OrgUnit), (_enc $skus), $_.MonthlyWasteUsd
        }) -join "`n"
        $accountSection = @"
    <h2>Accounts to remediate ($($s.ReclaimableAccountCount))</h2>
    <table class="grid">
      <thead><tr><th>Name</th><th>UPN</th><th>Why</th><th>OU</th><th>Licenses</th><th class="num">$/mo</th></tr></thead>
      <tbody>
$accountRows
      </tbody>
    </table>
"@

    }
    else {
        $accountSection = @"
    <div class="locked">
      <h2>$($s.ReclaimableAccountCount) accounts are named in the full report</h2>
      <p>The free report shows what you are losing. The <strong>`$79 unlock</strong> reveals exactly
         <em>which</em> accounts to deprovision (name, UPN, OU, licenses) and exports the remediation CSV.</p>
      <p><a href="https://opteraai.com/products">Unlock the remediation list &rarr;</a></p>
    </div>
"@

    }

    $unknownNote = if ($s.UnknownPriceSkuCount -gt 0) {
        "<p class=`"note`">$($s.UnknownPriceSkuCount) license(s) had no price in the list and were counted as `$0. Supply a custom price list to include them.</p>"
    } else { '' }

    $html = @"
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>License Reclaim Report &middot; Optera</title>
<style>
  :root { --navy:#0f2740; --teal:#2f7e7a; --ink:#1c2733; --muted:#6b7785; --line:#e3e8ee; --bg:#f7f9fb; }
  * { box-sizing:border-box; }
  body { font-family:Georgia,'Times New Roman',serif; color:var(--ink); background:var(--bg); margin:0; padding:0; }
  .wrap { max-width:960px; margin:0 auto; padding:40px 28px 64px; }
  header { border-bottom:3px solid var(--navy); padding-bottom:14px; margin-bottom:8px; }
  .brand { font-family:'Segoe UI',Arial,sans-serif; letter-spacing:.04em; color:var(--navy); font-weight:600; font-size:14px; text-transform:uppercase; }
  .meta { font-family:'Segoe UI',Arial,sans-serif; color:var(--muted); font-size:13px; margin-top:4px; }
  .headline { background:var(--navy); color:#fff; border-radius:8px; padding:28px 30px; margin:24px 0; }
  .headline .money { font-family:'Segoe UI',Arial,sans-serif; font-size:42px; font-weight:700; line-height:1; }
  .headline .sub { font-family:'Segoe UI',Arial,sans-serif; color:#b9c6d6; margin-top:10px; font-size:15px; }
  h2 { font-family:'Segoe UI',Arial,sans-serif; color:var(--navy); font-size:18px; margin:32px 0 12px; }
  table.grid { width:100%; border-collapse:collapse; font-family:'Segoe UI',Arial,sans-serif; font-size:14px; }
  table.grid th { text-align:left; background:#eef2f6; color:var(--navy); padding:9px 12px; border-bottom:2px solid var(--line); }
  table.grid td { padding:8px 12px; border-bottom:1px solid var(--line); }
  table.grid td.num, table.grid th.num { text-align:right; font-variant-numeric:tabular-nums; }
  .locked { border:1px dashed var(--teal); background:#f0f7f6; border-radius:8px; padding:24px 28px; margin-top:18px; font-family:'Segoe UI',Arial,sans-serif; }
  .locked a { color:var(--teal); font-weight:600; text-decoration:none; }
  .note { font-family:'Segoe UI',Arial,sans-serif; color:var(--muted); font-size:13px; }
  footer { margin-top:40px; padding-top:16px; border-top:1px solid var(--line); font-family:'Segoe UI',Arial,sans-serif; color:var(--muted); font-size:12px; }
</style></head>
<body><div class="wrap">
  <header>
    <div class="brand">Optera &middot; LicenseReclaim</div>
    <div class="meta">Tenant $(_enc $Report.TenantId) &nbsp;&bull;&nbsp; Generated $generated &nbsp;&bull;&nbsp; Stale threshold $($Report.StaleDays) days &nbsp;&bull;&nbsp; $($Report.Mode) report</div>
  </header>

  <div class="headline">
    <div class="money">`$$('{0:N0}' -f $s.TotalMonthlyUsd)/mo &nbsp;<span style="font-size:22px;font-weight:400">(`$$('{0:N0}' -f $s.TotalAnnualUsd)/yr)</span></div>
    <div class="sub">$($s.ReclaimableAccountCount) dead or stale accounts still holding $($s.ReclaimableLicenseCount) paid Microsoft 365 licenses &mdash; <strong>review candidates</strong>, verify before reclaiming.</div>
  </div>

$byTypeSection

$accountSection
$unknownNote

  <h2>Before you act</h2>
  <ul class="note">
    <li><strong>"Stale" is an on-premises signal.</strong> It measures AD logon age, not Microsoft 365 sign-in, so a cloud-active user who rarely authenticates on-prem can appear here. Confirm against Entra sign-in activity before reclaiming.</li>
    <li><strong>Disabled + licensed can be deliberate.</strong> Litigation-hold and shared/forwarding mailboxes are sometimes licensed on purpose. Suppress those OUs with <code>-ExcludeOu</code>.</li>
    <li><strong>Matching can undercount.</strong> Accounts with alternate UPN suffixes or no synced anchor may be missed &mdash; a reclaim candidate can be omitted, but a false positive is never invented.</li>
  </ul>

  <footer>
    $(_enc $Report.Disclaimer) These figures are review candidates, not confirmed waste.<br>
    A product of Optera AI LLC &middot; https://opteraai.com
  </footer>
</div></body></html>
"@


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