src/Private/Measure-WastedSpend.ps1

function Measure-WastedSpend {
    <#
    .SYNOPSIS
        Prices the reclaimable licenses and rolls them up into the wasted-spend totals.
    .DESCRIPTION
        Takes the raw candidate records from Join-AccountLicense, resolves each SKU to a friendly name
        and monthly list price, and returns:
          - PricedReclaimable : each candidate with a priced Skus array and a MonthlyWasteUsd subtotal
          - Summary : TotalMonthlyUsd, TotalAnnualUsd, account/license counts, and a BySku rollup
        The BySku rollup is what the FREE report shows; PricedReclaimable (the named list) is gated to FULL.
    .OUTPUTS
        PSCustomObject with PricedReclaimable and Summary members.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $Reclaimable,
        [Parameter(Mandatory)] [hashtable] $PriceList
    )

    $pricedReclaimable = New-Object System.Collections.Generic.List[object]
    $skuAccumulator = @{}   # skuId -> rollup record
    $totalMonthly = 0.0
    $licenseCount = 0
    $unknownPriceCount = 0

    foreach ($candidate in $Reclaimable) {
        $pricedSkus = New-Object System.Collections.Generic.List[object]
        $accountMonthly = 0.0

        foreach ($skuId in $candidate.SkuIds) {
            $price = Resolve-SkuPrice -SkuId $skuId -PriceList $PriceList
            $monthly = [double]$price.MonthlyListUsd
            $accountMonthly += $monthly
            $totalMonthly += $monthly
            $licenseCount++
            if ($price.PSObject.Properties.Name -contains 'PriceKnown' -and -not $price.PriceKnown) {
                $unknownPriceCount++
            }

            $pricedSkus.Add([pscustomobject]@{
                SkuId          = $price.SkuId
                Name           = $price.Name
                PartNumber     = $price.PartNumber
                MonthlyListUsd = $monthly
            })

            $key = $price.SkuId.ToLowerInvariant()
            if (-not $skuAccumulator.ContainsKey($key)) {
                $skuAccumulator[$key] = [pscustomobject]@{
                    SkuId           = $price.SkuId
                    Name            = $price.Name
                    PartNumber      = $price.PartNumber
                    Count           = 0
                    MonthlyEachUsd  = $monthly
                    MonthlyTotalUsd = 0.0
                }
            }
            $skuAccumulator[$key].Count++
            $skuAccumulator[$key].MonthlyTotalUsd += $monthly
        }

        $clone = $candidate | Select-Object * -ExcludeProperty SkuIds
        $pricedReclaimable.Add(($clone | Add-Member -NotePropertyMembers @{
            Skus            = $pricedSkus.ToArray()
            MonthlyWasteUsd = [math]::Round($accountMonthly, 2)
        } -PassThru))
    }

    $bySku = @($skuAccumulator.Values | Sort-Object -Property MonthlyTotalUsd -Descending)

    $summary = [pscustomobject]@{
        TotalMonthlyUsd         = [math]::Round($totalMonthly, 2)
        TotalAnnualUsd          = [math]::Round($totalMonthly * 12, 2)
        ReclaimableAccountCount = $pricedReclaimable.Count
        ReclaimableLicenseCount = $licenseCount
        UnknownPriceSkuCount    = $unknownPriceCount
        BySku                   = $bySku
    }

    [pscustomobject]@{
        PricedReclaimable = $pricedReclaimable.ToArray()
        Summary     = $summary
    }
}