Private/Import-InforcerSettingsCatalog.ps1

function Import-InforcerSettingsCatalog {
    <#
    .SYNOPSIS
        Loads the Settings Catalog definition data into a session-scoped cache.
    .DESCRIPTION
        Resolves settings.json via Get-InforcerSettingsCatalogPath (which auto-downloads and
        caches from the IntuneSettingsCatalogData GitHub repository) and populates
        $script:InforcerSettingsCatalog as a hashtable keyed by settingDefinitionId.
        Each entry contains DisplayName, Description, and an Options hashtable (itemId -> label).

        The catalog is loaded once per session. Subsequent calls are no-ops unless -Force is used.
    .PARAMETER Path
        Explicit path to settings.json. When omitted, uses the cache strategy in
        Get-InforcerSettingsCatalogPath (auto-download from GitHub with 24h TTL).
    .PARAMETER Force
        Reload the catalog even if it is already cached.
    .EXAMPLE
        Import-InforcerSettingsCatalog
        # Auto-resolves settings.json via cache strategy the first time; no-op on subsequent calls.
    .EXAMPLE
        Import-InforcerSettingsCatalog -Force
        # Reloads the catalog even if already cached.
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$Path,

        [Parameter()]
        [switch]$Force
    )

    # Guard: already loaded and -Force not requested
    if (-not $Force -and $null -ne $script:InforcerSettingsCatalog) { return }

    # Resolve path via cache strategy if not explicit
    if ([string]::IsNullOrEmpty($Path)) {
        $Path = Get-InforcerSettingsCatalogPath
    } else {
        $Path = Get-InforcerSettingsCatalogPath -ExplicitPath $Path
    }

    if ([string]::IsNullOrEmpty($Path)) {
        # No catalog available -- caller proceeds without resolution
        return
    }

    Write-Host ' Loading Settings Catalog...' -ForegroundColor Gray
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $raw = Get-Content -Path $Path -Raw -Encoding UTF8
    $entries = $raw | ConvertFrom-Json -AsHashtable -Depth 100

    # Load categories.json and build full category chains (parentCategoryId walk)
    $categoryLookup = @{}
    $categoriesPath = Join-Path (Split-Path $Path -Parent) 'categories.json'
    if (Test-Path -LiteralPath $categoriesPath) {
        Write-Verbose "Loading categories from $categoriesPath..."
        $catRaw = Get-Content -Path $categoriesPath -Raw -Encoding UTF8
        $catEntries = $catRaw | ConvertFrom-Json -Depth 10
        # First pass: build raw lookup
        $catById = @{}
        foreach ($cat in $catEntries) {
            if ($cat.id) { $catById[$cat.id] = $cat }
        }
        # Second pass: build full chains by walking parentCategoryId
        foreach ($cat in $catEntries) {
            if (-not $cat.id) { continue }
            $chain = [System.Collections.Generic.List[string]]::new()
            $current = $cat
            $visited = [System.Collections.Generic.HashSet[string]]::new()
            while ($current) {
                if (-not $visited.Add($current.id)) { break }  # cycle protection
                if (-not [string]::IsNullOrWhiteSpace($current.displayName)) {
                    $chain.Insert(0, $current.displayName)
                }
                if ($current.parentCategoryId -and $catById.ContainsKey($current.parentCategoryId)) {
                    $current = $catById[$current.parentCategoryId]
                } else {
                    $current = $null
                }
            }
            $categoryLookup[$cat.id] = ($chain -join ' > ')
        }
        Write-Verbose "Categories loaded: $($categoryLookup.Count) entries with full chains"
    }

    $catalog = @{}
    foreach ($entry in $entries) {
        $id = $entry['id']
        if ([string]::IsNullOrEmpty($id)) { continue }

        $options = @{}
        $entryOptions = $entry['options']
        if ($null -ne $entryOptions) {
            foreach ($opt in @($entryOptions)) {
                if ($opt -and $opt['itemId']) { $options[$opt['itemId']] = $opt['displayName'] }
            }
        }

        # Resolve category name for disambiguation (e.g., "Trusted Sites Zone", "Domain Profile")
        $catName = ''
        if ($entry.categoryId -and $categoryLookup.ContainsKey($entry.categoryId)) {
            $catName = $categoryLookup[$entry.categoryId]
        }

        $catalog[$id] = @{
            DisplayName  = $entry['displayName']
            Description  = $entry['description']
            Options      = $options
            CategoryName = $catName
        }
    }

    $sw.Stop()
    $script:InforcerSettingsCatalog = $catalog
    Write-Host " Settings Catalog loaded: $($catalog.Count) entries ($([math]::Round($sw.Elapsed.TotalSeconds, 1))s)" -ForegroundColor Gray
}