Common/Import-ControlRegistry.ps1

<#
.SYNOPSIS
    Loads the control registry and builds lookup tables for the report layer.
.DESCRIPTION
    Loads check data from the local controls/registry.json file (synced from
    CheckID via CI). Returns a hashtable keyed by CheckId with framework
    mappings and risk severity.
 
    Also builds a reverse lookup from CIS control IDs to CheckIds (stored
    under the special key '__cisReverseLookup') for backward compatibility
    with CSVs that still use the CisControl column.
.PARAMETER ControlsPath
    Path to the controls/ directory containing registry.json and
    risk-severity.json (local overlay).
.PARAMETER CisFrameworkId
    Framework ID for the active CIS benchmark version, used for the reverse
    lookup. Defaults to 'cis-m365-v6'.
.OUTPUTS
    [hashtable] - Keys are CheckIds, values are registry entry objects.
    Special key '__cisReverseLookup' maps CIS control IDs to CheckIds.
#>

function Import-ControlRegistry {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ControlsPath,

        [Parameter()]
        [string]$CisFrameworkId = 'cis-m365-v6'
    )

    $registryPath = Join-Path -Path $ControlsPath -ChildPath 'registry.json'
    if (-not (Test-Path -Path $registryPath)) {
        Write-Warning "Control registry not found: $registryPath"
        return @{}
    }

    $raw = Get-Content -Path $registryPath -Raw | ConvertFrom-Json
    $checks = @($raw.checks)
    Write-Verbose "Loaded $($checks.Count) checks from local registry.json"

    # Build hashtable keyed by CheckId
    $lookup = @{}
    $cisReverse = @{}

    foreach ($check in $checks) {
        $entry = @{
            checkId           = $check.checkId
            name              = $check.name
            category          = $check.category
            collector         = $check.collector
            hasAutomatedCheck = $check.hasAutomatedCheck
            licensing         = $check.licensing
            frameworks        = @{}
        }

        # Convert framework PSCustomObject properties to hashtable
        foreach ($prop in $check.frameworks.PSObject.Properties) {
            $entry.frameworks[$prop.Name] = $prop.Value
        }

        $entry.riskSeverity = 'Medium'  # default, overridden from risk-severity.json below
        $lookup[$check.checkId] = $entry

        # Build CIS reverse lookup (parameterized for version upgrades)
        $cisMapping = $check.frameworks.$CisFrameworkId
        if ($cisMapping -and $cisMapping.controlId) {
            $cisReverse[$cisMapping.controlId] = $check.checkId
        }
    }

    $lookup['__cisReverseLookup'] = $cisReverse

    # Load risk severity overlay (local to M365-Assess, not in CheckID)
    $severityPath = Join-Path -Path $ControlsPath -ChildPath 'risk-severity.json'
    if (Test-Path -Path $severityPath) {
        $severityData = Get-Content -Path $severityPath -Raw | ConvertFrom-Json
        foreach ($prop in $severityData.checks.PSObject.Properties) {
            if ($lookup.ContainsKey($prop.Name)) {
                $lookup[$prop.Name].riskSeverity = $prop.Value
            }
        }
    }

    return $lookup
}