Views/ConfigurationProfiles.ps1

function Get-InTUIConfigPolicyPlatform {
    <#
    .SYNOPSIS
        Maps a configurationPolicies platforms value to a friendly label.
    #>

    param([string]$Platforms)

    switch -Wildcard ($Platforms) {
        '*windows*' { return 'Windows' }
        '*iOS*'     { return 'iOS' }
        '*macOS*'   { return 'macOS' }
        '*android*' { return 'Android' }
        '*linux*'   { return 'Linux' }
        default     { return $Platforms ?? 'Unknown' }
    }
}

function Get-InTUIConfigPolicyTechnology {
    <#
    .SYNOPSIS
        Maps a configurationPolicies technologies value to a friendly label.
    #>

    param([string]$Technologies)

    switch -Wildcard ($Technologies) {
        '*mdm*'            { return 'MDM' }
        '*configManager*'  { return 'Config Manager' }
        '*microsoftSense*' { return 'Defender' }
        default            { return $Technologies ?? 'Unknown' }
    }
}

function Show-InTUIConfigProfilesView {
    <#
    .SYNOPSIS
        Displays the Configuration Profiles view with platform filtering and search.
    #>

    [CmdletBinding()]
    param()

    $exitView = $false

    while (-not $exitView) {
        Clear-Host
        Show-InTUIHeader
        Show-InTUIBreadcrumb -Path @('Home', 'Configuration Profiles')

        $choices = @(
            'All Profiles',
            'Windows Profiles',
            'iOS/iPadOS Profiles',
            'macOS Profiles',
            'Android Profiles',
            'Search Profiles',
            '─────────────',
            'Back to Home'
        )

        $selection = Show-InTUIMenu -Title "[cyan]Configuration Profiles[/]" -Choices $choices

        Write-InTUILog -Message "Configuration Profiles view selection" -Context @{ Selection = $selection }

        switch ($selection) {
            'All Profiles' {
                Show-InTUIConfigProfileList
            }
            'Windows Profiles' {
                Show-InTUIConfigProfileList -PlatformFilter 'Windows'
            }
            'iOS/iPadOS Profiles' {
                Show-InTUIConfigProfileList -PlatformFilter 'iOS'
            }
            'macOS Profiles' {
                Show-InTUIConfigProfileList -PlatformFilter 'macOS'
            }
            'Android Profiles' {
                Show-InTUIConfigProfileList -PlatformFilter 'Android'
            }
            'Search Profiles' {
                $searchTerm = Read-InTUITextInput -Message "[cyan]Search profiles by name[/]"
                if ($searchTerm) {
                    Write-InTUILog -Message "Searching configuration profiles" -Context @{ SearchTerm = $searchTerm }
                    Show-InTUIConfigProfileList -SearchTerm $searchTerm
                }
            }
            'Back to Home' {
                $exitView = $true
            }
            default {
                continue
            }
        }
    }
}

function Show-InTUIConfigProfileList {
    <#
    .SYNOPSIS
        Displays a merged list of legacy device configurations and Settings Catalog policies.
    #>

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

        [Parameter()]
        [string]$SearchTerm
    )

    $exitList = $false

    while (-not $exitList) {
        Clear-Host
        Show-InTUIHeader

        $breadcrumb = @('Home', 'Configuration Profiles')
        if ($PlatformFilter) { $breadcrumb += "$PlatformFilter Profiles" }
        elseif ($SearchTerm) { $breadcrumb += "Search: $SearchTerm" }
        else { $breadcrumb += 'All Profiles' }
        Show-InTUIBreadcrumb -Path $breadcrumb

        $allProfiles = Show-InTUILoading -Title "[cyan]Loading configuration profiles...[/]" -ScriptBlock {
            $normalized = [System.Collections.Generic.List[PSCustomObject]]::new()

            # Legacy device configurations - fetch all pages
            $legacyUri = '/deviceManagement/deviceConfigurations'
            if ($SearchTerm) {
                $safe = ConvertTo-InTUISafeFilterValue -Value $SearchTerm
                $legacyUri += "?`$filter=contains(displayName,'$safe')"
            }
            $legacy = Invoke-InTUIGraphRequest -Uri $legacyUri -Beta -All

            foreach ($p in @($legacy)) {
                $typeInfo = Get-InTUIConfigProfileType -ODataType $p.'@odata.type'
                $normalized.Add([PSCustomObject]@{
                    Id       = $p.id
                    Name     = $p.displayName
                    Platform = $typeInfo.Platform
                    Type     = $typeInfo.FriendlyName
                    Modified = $p.lastModifiedDateTime
                    Source   = 'Legacy'
                })
            }

            # Settings Catalog policies - fetch all pages
            $catalogUri = '/deviceManagement/configurationPolicies'
            if ($SearchTerm) {
                $safe = ConvertTo-InTUISafeFilterValue -Value $SearchTerm
                $catalogUri += "?`$filter=contains(name,'$safe')"
            }
            $catalog = Invoke-InTUIGraphRequest -Uri $catalogUri -Beta -All

            foreach ($p in @($catalog)) {
                $normalized.Add([PSCustomObject]@{
                    Id       = $p.id
                    Name     = $p.name
                    Platform = Get-InTUIConfigPolicyPlatform -Platforms $p.platforms
                    Type     = Get-InTUIConfigPolicyTechnology -Technologies $p.technologies
                    Modified = $p.lastModifiedDateTime
                    Source   = 'Catalog'
                })
            }

            $normalized
        }

        if ($null -eq $allProfiles -or @($allProfiles).Count -eq 0) {
            Show-InTUIWarning "No configuration profiles found."
            Read-InTUIKey
            $exitList = $true
            continue
        }

        $filteredResults = @($allProfiles)
        if ($PlatformFilter) {
            $filteredResults = @($allProfiles | Where-Object { $_.Platform -eq $PlatformFilter })

            if ($filteredResults.Count -eq 0) {
                Show-InTUIWarning "No $PlatformFilter configuration profiles found."
                Read-InTUIKey
                $exitList = $true
                continue
            }
        }

        $profileChoices = @()
        foreach ($configProfile in $filteredResults) {
            $modified = Format-InTUIDate -DateString $configProfile.Modified
            $sourceTag = if ($configProfile.Source -eq 'Catalog') { '[cyan]SC[/]' } else { '[grey]DC[/]' }

            $displayName = "$sourceTag [white]$($configProfile.Name)[/] [grey]| $($configProfile.Platform) | $($configProfile.Type) | $modified[/]"
            $profileChoices += $displayName
        }

        $choiceMap = Get-InTUIChoiceMap -Choices $profileChoices
        $menuChoices = @($choiceMap.Choices + '─────────────' + 'Back')

        Show-InTUIStatusBar -Total $filteredResults.Count -Showing $filteredResults.Count -FilterText ($PlatformFilter ?? $SearchTerm)

        $selection = Show-InTUIMenu -Title "[cyan]Select a profile[/]" -Choices $menuChoices

        if ($selection -eq 'Back') {
            $exitList = $true
        }
        elseif ($selection -ne '─────────────') {
            $idx = $choiceMap.IndexMap[$selection]
            if ($null -ne $idx -and $idx -lt $filteredResults.Count) {
                $selected = $filteredResults[$idx]
                if ($selected.Source -eq 'Catalog') {
                    Show-InTUICatalogProfileDetail -ProfileId $selected.Id
                }
                else {
                    Show-InTUILegacyProfileDetail -ProfileId $selected.Id
                }
            }
        }
    }
}

# region Settings Catalog (configurationPolicies)

function Show-InTUICatalogProfileDetail {
    <#
    .SYNOPSIS
        Displays detailed information about a Settings Catalog configuration policy.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ProfileId
    )

    $exitDetail = $false

    while (-not $exitDetail) {
        Clear-Host
        Show-InTUIHeader

        $detailData = Show-InTUILoading -Title "[cyan]Loading profile details...[/]" -ScriptBlock {
            $prof = Invoke-InTUIGraphRequest -Uri "/deviceManagement/configurationPolicies/$ProfileId" -Beta
            $assign = Invoke-InTUIGraphRequest -Uri "/deviceManagement/configurationPolicies/$ProfileId/assignments" -Beta

            @{
                Profile     = $prof
                Assignments = $assign
            }
        }

        $configProfile = $detailData.Profile
        $assignments = $detailData.Assignments

        if ($null -eq $configProfile) {
            Show-InTUIError "Failed to load profile details."
            Read-InTUIKey
            return
        }

        Show-InTUIBreadcrumb -Path @('Home', 'Configuration Profiles', $configProfile.name)

        Add-InTUIHistoryEntry -ViewType 'CatalogProfile' -ViewId $ProfileId -DisplayName $configProfile.name

        $platform = Get-InTUIConfigPolicyPlatform -Platforms $configProfile.platforms
        $tech = Get-InTUIConfigPolicyTechnology -Technologies $configProfile.technologies
        $templateName = $configProfile.templateReference.templateDisplayName

        $propsContent = @"
[bold white]$($configProfile.name)[/]

[grey]Source:[/] [cyan]Settings Catalog[/]
[grey]Platform:[/] $platform
[grey]Technology:[/] $tech
[grey]Description:[/] $(if ($configProfile.description) { $configProfile.description.Substring(0, [Math]::Min(200, $configProfile.description.Length)) } else { 'N/A' })
[grey]Template:[/] $(if ($templateName) { $templateName } else { 'None' })
[grey]Settings Count:[/] $($configProfile.settingCount ?? 0)
[grey]Is Assigned:[/] $($configProfile.isAssigned ?? $false)
[grey]Created:[/] $(Format-InTUIDate -DateString $configProfile.createdDateTime)
[grey]Last Modified:[/] $(Format-InTUIDate -DateString $configProfile.lastModifiedDateTime)
"@


        Show-InTUIPanel -Title "[cyan]Profile Properties[/]" -Content $propsContent -BorderColor Cyan1

        $assignmentCount = if ($assignments.value) { @($assignments.value).Count } else { 0 }
        $assignContent = "[grey]Total Assignments:[/] [white]$assignmentCount[/]"

        if ($assignments.value) {
            $assignContent += "`n"
            foreach ($assignment in $assignments.value) {
                $targetType = switch ($assignment.target.'@odata.type') {
                    '#microsoft.graph.allLicensedUsersAssignmentTarget' { '[blue]All Users[/]' }
                    '#microsoft.graph.allDevicesAssignmentTarget'       { '[blue]All Devices[/]' }
                    '#microsoft.graph.groupAssignmentTarget'            { "Group: $($assignment.target.groupId)" }
                    '#microsoft.graph.exclusionGroupAssignmentTarget'   { "[red]Exclude:[/] $($assignment.target.groupId)" }
                    default { $assignment.target.'@odata.type' -replace '#microsoft\.graph\.', '' }
                }
                $assignContent += "`n $targetType"
            }
        }

        Show-InTUIPanel -Title "[cyan]Assignments[/]" -Content $assignContent -BorderColor Cyan1

        $actionChoices = @(
            'View Settings',
            '─────────────',
            'Back to Profiles'
        )

        $action = Show-InTUIMenu -Title "[cyan]Profile Actions[/]" -Choices $actionChoices

        Write-InTUILog -Message "Config profile detail action" -Context @{ ProfileId = $ProfileId; ProfileName = $configProfile.name; Action = $action }

        switch ($action) {
            'View Settings' {
                Show-InTUICatalogProfileSettings -ProfileId $ProfileId -ProfileName $configProfile.name
            }
            'Back to Profiles' {
                $exitDetail = $true
            }
            default {
                continue
            }
        }
    }
}

function Show-InTUICatalogProfileSettings {
    <#
    .SYNOPSIS
        Displays configured settings for a Settings Catalog policy.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ProfileId,

        [Parameter()]
        [string]$ProfileName
    )

    Clear-Host
    Show-InTUIHeader
    Show-InTUIBreadcrumb -Path @('Home', 'Configuration Profiles', $ProfileName, 'Settings')

    $settings = Show-InTUILoading -Title "[cyan]Loading settings...[/]" -ScriptBlock {
        Invoke-InTUIGraphRequest -Uri "/deviceManagement/configurationPolicies/$ProfileId/settings?`$expand=settingDefinitions&`$top=100" -Beta
    }

    if (-not $settings.value) {
        Show-InTUIWarning "No settings data available for this profile."
        Read-InTUIKey
        return
    }

    $rows = @()
    foreach ($setting in $settings.value) {
        $defId = $setting.settingInstance.settingDefinitionId
        $settingName = if ($setting.settingDefinitions -and $setting.settingDefinitions.Count -gt 0) {
            $setting.settingDefinitions[0].displayName
        }
        else {
            $segments = $defId -split '_'
            $segments[-1]
        }

        $instance = $setting.settingInstance
        $value = switch -Wildcard ($instance.'@odata.type') {
            '*ChoiceSettingInstance' {
                $raw = $instance.choiceSettingValue.value
                ($raw -split '_')[-1]
            }
            '*SimpleSettingInstance' {
                "$($instance.simpleSettingValue.value)"
            }
            '*SimpleSettingCollectionInstance' {
                "$(@($instance.simpleSettingCollectionValue).Count) values"
            }
            '*GroupSettingCollectionInstance' {
                "$(@($instance.groupSettingCollectionValue).Count) groups"
            }
            default { 'Complex' }
        }

        $rows += , @($settingName, ($value ?? 'N/A'))
    }

    Show-InTUITable -Title "Profile Settings" -Columns @('Setting', 'Value') -Rows $rows
    Read-InTUIKey
}

# endregion

# region Legacy (deviceConfigurations)

function Show-InTUILegacyProfileDetail {
    <#
    .SYNOPSIS
        Displays detailed information about a legacy device configuration profile.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ProfileId
    )

    $exitDetail = $false

    while (-not $exitDetail) {
        Clear-Host
        Show-InTUIHeader

        $detailData = Show-InTUILoading -Title "[cyan]Loading profile details...[/]" -ScriptBlock {
            $prof = Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ProfileId" -Beta
            $assign = Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ProfileId/assignments" -Beta
            $statuses = Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ProfileId/deviceStatuses?`$top=200" -Beta

            @{
                Profile     = $prof
                Assignments = $assign
                Statuses    = $statuses
            }
        }

        $configProfile = $detailData.Profile
        $assignments = $detailData.Assignments
        $statuses = $detailData.Statuses

        if ($null -eq $configProfile) {
            Show-InTUIError "Failed to load profile details."
            Read-InTUIKey
            return
        }

        Show-InTUIBreadcrumb -Path @('Home', 'Configuration Profiles', $configProfile.displayName)

        Add-InTUIHistoryEntry -ViewType 'ConfigProfile' -ViewId $ProfileId -DisplayName $configProfile.displayName

        $typeInfo = Get-InTUIConfigProfileType -ODataType $configProfile.'@odata.type'

        $propsContent = @"
[bold white]$($configProfile.displayName)[/]

[grey]Source:[/] [grey]Device Configuration[/]
[grey]Type:[/] $($typeInfo.FriendlyName)
[grey]Platform:[/] $($typeInfo.Platform)
[grey]Description:[/] $(if ($configProfile.description) { $configProfile.description.Substring(0, [Math]::Min(200, $configProfile.description.Length)) } else { 'N/A' })
[grey]Created:[/] $(Format-InTUIDate -DateString $configProfile.createdDateTime)
[grey]Last Modified:[/] $(Format-InTUIDate -DateString $configProfile.lastModifiedDateTime)
[grey]Version:[/] $($configProfile.version ?? 'N/A')
"@


        Show-InTUIPanel -Title "[cyan]Profile Properties[/]" -Content $propsContent -BorderColor Cyan1

        $assignmentCount = if ($assignments.value) { @($assignments.value).Count } else { 0 }
        $assignContent = "[grey]Total Assignments:[/] [white]$assignmentCount[/]"

        if ($assignments.value) {
            $assignContent += "`n"
            foreach ($assignment in $assignments.value) {
                $targetType = switch ($assignment.target.'@odata.type') {
                    '#microsoft.graph.allLicensedUsersAssignmentTarget' { '[blue]All Users[/]' }
                    '#microsoft.graph.allDevicesAssignmentTarget'       { '[blue]All Devices[/]' }
                    '#microsoft.graph.groupAssignmentTarget'            { "Group: $($assignment.target.groupId)" }
                    '#microsoft.graph.exclusionGroupAssignmentTarget'   { "[red]Exclude:[/] $($assignment.target.groupId)" }
                    default { $assignment.target.'@odata.type' -replace '#microsoft\.graph\.', '' }
                }
                $assignContent += "`n $targetType"
            }
        }

        Show-InTUIPanel -Title "[cyan]Assignments[/]" -Content $assignContent -BorderColor Cyan1

        $statusList = if ($statuses.value) { @($statuses.value) } else { @() }
        $succeeded = @($statusList | Where-Object { $_.status -eq 'succeeded' }).Count
        $failed = @($statusList | Where-Object { $_.status -eq 'failed' }).Count
        $pending = @($statusList | Where-Object { $_.status -eq 'pending' -or $_.status -eq 'notApplicable' }).Count
        $other = $statusList.Count - $succeeded - $failed - $pending

        $statusContent = @"
[grey]Total Devices:[/] [white]$($statusList.Count)[/]
[green]Succeeded:[/] $succeeded
[red]Failed:[/] $failed
[yellow]Pending:[/] $pending
"@

        if ($other -gt 0) {
            $statusContent += "`n[grey]Other:[/] $other"
        }

        Show-InTUIPanel -Title "[cyan]Device Status Summary[/]" -Content $statusContent -BorderColor Cyan1

        # Check for conflicts
        $conflictCount = @($statusList | Where-Object { $_.status -eq 'conflict' }).Count
        if ($conflictCount -gt 0) {
            $conflictContent = "[red]$conflictCount device(s) have configuration conflicts![/]"
            Show-InTUIPanel -Title "[orange1]Conflicts Detected[/]" -Content $conflictContent -BorderColor Orange1
        }

        $actionChoices = @(
            'View Device Statuses',
            'View Conflicts',
            '─────────────',
            'Back to Profiles'
        )

        $action = Show-InTUIMenu -Title "[cyan]Profile Actions[/]" -Choices $actionChoices

        Write-InTUILog -Message "Config profile detail action" -Context @{ ProfileId = $ProfileId; ProfileName = $configProfile.displayName; Action = $action }

        switch ($action) {
            'View Device Statuses' {
                Show-InTUILegacyProfileDeviceStatuses -ProfileId $ProfileId -ProfileName $configProfile.displayName
            }
            'View Conflicts' {
                Show-InTUIProfileConflicts -ProfileId $ProfileId -ProfileName $configProfile.displayName
            }
            'Back to Profiles' {
                $exitDetail = $true
            }
            default {
                continue
            }
        }
    }
}

function Show-InTUIProfileConflicts {
    <#
    .SYNOPSIS
        Displays devices with configuration conflicts for a profile.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ProfileId,

        [Parameter()]
        [string]$ProfileName
    )

    Clear-Host
    Show-InTUIHeader
    Show-InTUIBreadcrumb -Path @('Home', 'Configuration Profiles', $ProfileName, 'Conflicts')

    $conflicts = Show-InTUILoading -Title "[orange1]Loading conflict data...[/]" -ScriptBlock {
        $allStatuses = Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ProfileId/deviceStatuses" -Beta -All
        @($allStatuses | Where-Object { $_.status -eq 'conflict' })
    }

    if (-not $conflicts -or $conflicts.Count -eq 0) {
        Show-InTUISuccess "No conflicts found for this profile."
        Read-InTUIKey
        return
    }

    Write-InTUILog -Message "Profile conflicts found" -Context @{ ProfileName = $ProfileName; ConflictCount = $conflicts.Count }

    $rows = @()
    foreach ($status in $conflicts) {
        $rows += , @(
            ($status.deviceDisplayName ?? 'N/A'),
            "[orange1]conflict[/]",
            ($status.userName ?? 'N/A'),
            (Format-InTUIDate -DateString $status.lastReportedDateTime)
        )
    }

    Show-InTUITable -Title "Configuration Conflicts - $ProfileName" -Columns @('Device', 'Status', 'User', 'Last Reported') -Rows $rows -BorderColor Orange1

    Write-InTUIText ""
    Write-InTUIText "[grey]Conflicts occur when multiple policies target the same setting with different values.[/]"
    Write-InTUIText "[grey]Review assigned policies to resolve conflicts.[/]"

    Read-InTUIKey
}

function Show-InTUILegacyProfileDeviceStatuses {
    <#
    .SYNOPSIS
        Displays device status table for a legacy device configuration profile.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ProfileId,

        [Parameter()]
        [string]$ProfileName
    )

    Clear-Host
    Show-InTUIHeader
    Show-InTUIBreadcrumb -Path @('Home', 'Configuration Profiles', $ProfileName, 'Device Statuses')

    $statuses = Show-InTUILoading -Title "[cyan]Loading device statuses...[/]" -ScriptBlock {
        Invoke-InTUIGraphRequest -Uri "/deviceManagement/deviceConfigurations/$ProfileId/deviceStatuses?`$top=50" -Beta
    }

    if (-not $statuses.value) {
        Show-InTUIWarning "No device status data available for this profile."
        Read-InTUIKey
        return
    }

    $rows = @()
    foreach ($status in $statuses.value) {
        $stateColor = switch ($status.status) {
            'succeeded'     { 'green' }
            'failed'        { 'red' }
            'error'         { 'red' }
            'pending'       { 'yellow' }
            'notApplicable' { 'grey' }
            default         { 'yellow' }
        }

        $rows += , @(
            ($status.deviceDisplayName ?? 'N/A'),
            "[$stateColor]$($status.status)[/]",
            ($status.userName ?? 'N/A'),
            (Format-InTUIDate -DateString $status.lastReportedDateTime)
        )
    }

    Show-InTUITable -Title "Device Statuses" -Columns @('Device', 'Status', 'User', 'Last Reported') -Rows $rows
    Read-InTUIKey
}

# endregion