Private/Logic/Format-SettingValue.ps1

# Copyright (c) 2026 Sandy Zeng. All rights reserved.
# Source-available. All rights reserved. See LICENSE file.

<#
    Format-SettingValue.ps1 — Returns a human-readable value string for a raw Intune policy setting value.
 
    Author: Sandy Zeng
    Project: IntuneDiff
 
    Version History:
    1.0.0 Initial release.
#>


function Format-SettingValue {
    <#
    .SYNOPSIS
        Returns the friendly/human-readable value for a raw setting value.
 
    .DESCRIPTION
        PowerShell port of backend/src/services/valueFormatterService.ts.
        Handles choice setting options, boolean-like strings, and CSP suffix patterns.
 
    .PARAMETER Value
        The raw value from the policy (string, number, bool, or null).
 
    .PARAMETER Definition
        Optional setting definition hashtable. If it has an `options` array, that
        is searched for a matching itemId first.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $false)]
        [AllowNull()]
        $Value,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        $Definition
    )

    if ($null -eq $Value) { return $null }

    $valueStr = [string]$Value

    if ($Definition -and $Definition.options) {
        $opts = @($Definition.options)

        # Exact match
        $match = $opts | Where-Object { $_.itemId -eq $valueStr } | Select-Object -First 1

        # Case-insensitive
        if (-not $match) {
            $match = $opts | Where-Object {
                $_.itemId -and ($_.itemId.ToString().ToLowerInvariant() -eq $valueStr.ToLowerInvariant())
            } | Select-Object -First 1
        }

        # CSP prefix match
        if (-not $match) {
            $match = $opts | Where-Object {
                $_.itemId -and $valueStr.StartsWith([string]$_.itemId)
            } | Select-Object -First 1
        }

        if ($match) {
            if ($match.displayName) { return [string]$match.displayName }
            if ($match.name)        { return [string]$match.name }
            if ($match.label)       { return [string]$match.label }
            return $valueStr
        }
    }

    return (Format-SettingValueSpecial -Value $valueStr)
}

function Format-SettingValueSpecial {
    [CmdletBinding()]
    [OutputType([string])]
    param([string]$Value)

    if ($null -eq $Value) { return $null }

    $lower = $Value.ToLowerInvariant()

    if ($Value -eq '0' -or $lower -eq 'false') { return 'Disabled' }
    if ($Value -eq '1' -or $lower -eq 'true')  { return 'Enabled' }

    if ($lower.EndsWith('_0'))     { return 'Disabled' }
    if ($lower.EndsWith('_1'))     { return 'Enabled' }
    if ($lower.EndsWith('_block')) { return 'Block' }
    if ($lower.EndsWith('_audit')) { return 'Audit' }
    if ($lower.EndsWith('_off'))   { return 'Off' }
    if ($lower.EndsWith('_warn'))  { return 'Warn' }

    return $Value
}

function Format-CollectionValue {
    <#
    .SYNOPSIS
        Formats a setting collection (simple or group) into a human-readable string.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param([object]$Collection)

    if (-not $Collection) { return '[Empty Collection]' }
    $arr = @($Collection)
    if ($arr.Count -eq 0) { return '[Empty Collection]' }

    $hasChildren = $arr | Where-Object {
        $_.children -and ($_.children -is [array]) -and $_.children.Count -gt 0
    } | Select-Object -First 1

    if ($hasChildren) {
        return (Format-GroupCollectionValue -Collection $arr)
    }

    $values = foreach ($item in $arr) {
        if ($item -is [string]) { $item; continue }
        if ($item -isnot [hashtable] -and $item -isnot [pscustomobject]) {
            [string]$item; continue
        }

        $v = $null
        if ($item.defaultValue -and $item.defaultValue.constantValue) { $v = $item.defaultValue.constantValue }
        elseif ($item.defaultValue -and $item.defaultValue.value)     { $v = $item.defaultValue.value }
        elseif ($item.value)                                          { $v = $item.value }
        elseif ($item.constantValue)                                  { $v = $item.constantValue }
        $v
    }

    $values = $values | Where-Object { $null -ne $_ }
    if (-not $values -or $values.Count -eq 0) { return '[Empty Collection]' }

    return "[$(($values) -join ', ')]"
}

function Format-GroupCollectionValue {
    [CmdletBinding()]
    [OutputType([string])]
    param([object]$Collection)

    $entries = New-Object System.Collections.Generic.List[string]

    foreach ($group in $Collection) {
        $children = @($group.children)
        $key = ''
        $value = ''

        foreach ($child in $children) {
            $childDefId = [string]$child.settingDefinitionId
            $extracted = ''

            if ($child.simpleSettingValueTemplate -and $child.simpleSettingValueTemplate.defaultValue) {
                if ($child.simpleSettingValueTemplate.defaultValue.constantValue) {
                    $extracted = [string]$child.simpleSettingValueTemplate.defaultValue.constantValue
                } elseif ($child.simpleSettingValueTemplate.defaultValue.value) {
                    $extracted = [string]$child.simpleSettingValueTemplate.defaultValue.value
                }
            } elseif ($child.simpleSettingValue -and $child.simpleSettingValue.value) {
                $extracted = [string]$child.simpleSettingValue.value
            }

            $lower = $childDefId.ToLowerInvariant()
            if ($lower.Contains('_key'))   { $key = $extracted }
            elseif ($lower.Contains('_value')) { $value = $extracted }
        }

        if ($key -and $value) { $entries.Add("$key = $value") }
        elseif ($key)        { $entries.Add($key) }
        elseif ($value)      { $entries.Add($value) }
    }

    if ($entries.Count -eq 0) { return '[Empty Collection]' }

    if ($entries.Count -gt 3) {
        $first3 = ($entries | Select-Object -First 3) -join '; '
        return "[$first3... ($($entries.Count) entries total)]"
    }

    return "[$(($entries) -join '; ')]"
}