Views/AppProtection.ps1

function Show-InTUIAppProtectionView {
    <#
    .SYNOPSIS
        Displays the App Protection management view for MAM policies, VPP tokens, and Win32 dependencies.
    #>

    [CmdletBinding()]
    param()

    $exitView = $false

    while (-not $exitView) {
        Clear-Host
        Show-InTUIHeader
        Show-InTUIBreadcrumb -Path @('Home', 'Apps', 'App Protection')

        $choices = @(
            'iOS App Protection Policies',
            'Android App Protection Policies',
            'Windows App Protection Policies',
            'VPP Tokens',
            '─────────────',
            'Back to Apps'
        )

        $selection = Show-InTUIMenu -Title "[green]App Protection[/]" -Choices $choices

        Write-InTUILog -Message "App Protection view selection" -Context @{ Selection = $selection }

        switch ($selection) {
            'iOS App Protection Policies' {
                Show-InTUIAppProtectionPolicyList -Platform 'ios'
            }
            'Android App Protection Policies' {
                Show-InTUIAppProtectionPolicyList -Platform 'android'
            }
            'Windows App Protection Policies' {
                Show-InTUIAppProtectionPolicyList -Platform 'windows'
            }
            'VPP Tokens' {
                Show-InTUIVppTokenList
            }
            'Back to Apps' {
                $exitView = $true
            }
            default {
                continue
            }
        }
    }
}

function Show-InTUIAppProtectionPolicyList {
    <#
    .SYNOPSIS
        Displays a list of app protection policies for a given platform.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('ios', 'android', 'windows')]
        [string]$Platform
    )

    $exitList = $false

    $platformUri = switch ($Platform) {
        'ios'     { '/deviceAppManagement/iosManagedAppProtections' }
        'android' { '/deviceAppManagement/androidManagedAppProtections' }
        'windows' { '/deviceAppManagement/windowsInformationProtectionPolicies' }
    }

    $platformLabel = switch ($Platform) {
        'ios'     { 'iOS' }
        'android' { 'Android' }
        'windows' { 'Windows' }
    }

    while (-not $exitList) {
        Clear-Host
        Show-InTUIHeader
        Show-InTUIBreadcrumb -Path @('Home', 'Apps', 'App Protection', "$platformLabel Policies")

        $params = @{
            Uri      = $platformUri
            Beta     = $true
            PageSize = 25
            Select   = 'id,displayName,description,lastModifiedDateTime,createdDateTime'
        }

        $policies = Show-InTUILoading -Title "[green]Loading $platformLabel app protection policies...[/]" -ScriptBlock {
            Get-InTUIPagedResults @params
        }

        if ($null -eq $policies -or $policies.Results.Count -eq 0) {
            Show-InTUIWarning "No $platformLabel app protection policies found."
            Read-InTUIKey
            $exitList = $true
            continue
        }

        $policyChoices = @()
        foreach ($policy in $policies.Results) {
            $modified = Format-InTUIDate -DateString $policy.lastModifiedDateTime
            $displayName = "[white]$(ConvertTo-InTUISafeMarkup -Text $policy.displayName)[/] [grey]| $modified[/]"
            $policyChoices += $displayName
        }

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

        Show-InTUIStatusBar -Total $policies.TotalCount -Showing $policies.Results.Count

        $selection = Show-InTUIMenu -Title "[green]Select a policy[/]" -Choices $menuChoices

        if ($selection -eq 'Back') {
            $exitList = $true
        }
        elseif ($selection -ne '─────────────') {
            $idx = $choiceMap.IndexMap[$selection]
            if ($null -ne $idx -and $idx -lt $policies.Results.Count) {
                Show-InTUIAppProtectionPolicyDetail -PolicyId $policies.Results[$idx].id -Platform $Platform
            }
        }
    }
}

function Show-InTUIAppProtectionPolicyDetail {
    <#
    .SYNOPSIS
        Displays detailed information about a specific app protection policy.
    #>

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

        [Parameter(Mandatory)]
        [ValidateSet('ios', 'android', 'windows')]
        [string]$Platform
    )

    $exitDetail = $false

    $platformUri = switch ($Platform) {
        'ios'     { '/deviceAppManagement/iosManagedAppProtections' }
        'android' { '/deviceAppManagement/androidManagedAppProtections' }
        'windows' { '/deviceAppManagement/windowsInformationProtectionPolicies' }
    }

    $platformLabel = switch ($Platform) {
        'ios'     { 'iOS' }
        'android' { 'Android' }
        'windows' { 'Windows' }
    }

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

        $detailData = Show-InTUILoading -Title "[green]Loading policy details...[/]" -ScriptBlock {
            $pol = Invoke-InTUIGraphRequest -Uri "$platformUri/$PolicyId" -Beta
            $assign = Invoke-InTUIGraphRequest -Uri "$platformUri/$PolicyId/assignments" -Beta

            @{
                Policy      = $pol
                Assignments = $assign
            }
        }

        $policy = $detailData.Policy
        $assignments = $detailData.Assignments

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

        Show-InTUIBreadcrumb -Path @('Home', 'Apps', 'App Protection', "$platformLabel Policies", $policy.displayName)

        Write-InTUILog -Message "Viewing app protection policy detail" -Context @{ PolicyId = $PolicyId; Platform = $Platform; PolicyName = $policy.displayName }

        # Panel 1: Properties
        $propsContent = @"
[bold white]$(ConvertTo-InTUISafeMarkup -Text $policy.displayName)[/]

[grey]Platform:[/] $platformLabel
[grey]Description:[/] $(if ($policy.description) { $policy.description.Substring(0, [Math]::Min(200, $policy.description.Length)) } else { 'N/A' })
[grey]Created:[/] $(Format-InTUIDate -DateString $policy.createdDateTime)
[grey]Last Modified:[/] $(Format-InTUIDate -DateString $policy.lastModifiedDateTime)
"@


        Show-InTUIPanel -Title "[green]Policy Properties[/]" -Content $propsContent -BorderColor Green

        # Panel 2: Key policy settings (platform-specific)
        if ($Platform -eq 'ios' -or $Platform -eq 'android') {
            $storageLocations = if ($policy.allowedDataStorageLocations) {
                ($policy.allowedDataStorageLocations -join ', ')
            } else { 'N/A' }

            $settingsContent = @"
[grey]Allowed Data Storage:[/] $storageLocations
[grey]Org Credentials Required:[/] $($policy.organizationalCredentialsRequired ?? 'N/A')
[grey]PIN Required:[/] $($policy.pinRequired ?? 'N/A')
[grey]Managed Browser:[/] $($policy.managedBrowser ?? 'N/A')
[grey]Minimum OS Version:[/] $($policy.minimumRequiredOsVersion ?? $policy.minimumOsVersion ?? 'N/A')
[grey]Maximum OS Version:[/] $($policy.maximumRequiredOsVersion ?? $policy.maximumOsVersion ?? 'N/A')
[grey]Contact Sync Blocked:[/] $($policy.contactSyncBlocked ?? 'N/A')
"@

        }
        else {
            # Windows
            $settingsContent = @"
[grey]Enforcement Level:[/] $($policy.enforcementLevel ?? 'N/A')
[grey]RMS Template ID:[/] $($policy.rightsManagementServicesTemplateId ?? 'N/A')
"@

        }

        Show-InTUIPanel -Title "[green]Policy Settings[/]" -Content $settingsContent -BorderColor Green

        # Panel 3: Assignments
        $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 "[green]Assignments[/]" -Content $assignContent -BorderColor Green

        $actionChoices = @(
            '─────────────',
            'Back'
        )

        $action = Show-InTUIMenu -Title "[green]Policy Actions[/]" -Choices $actionChoices

        Write-InTUILog -Message "App protection policy detail action" -Context @{ PolicyId = $PolicyId; PolicyName = $policy.displayName; Action = $action }

        switch ($action) {
            'Back' {
                $exitDetail = $true
            }
            default {
                continue
            }
        }
    }
}

function Show-InTUIVppTokenList {
    <#
    .SYNOPSIS
        Displays a list of Apple VPP tokens.
    #>

    [CmdletBinding()]
    param()

    $exitList = $false

    while (-not $exitList) {
        Clear-Host
        Show-InTUIHeader
        Show-InTUIBreadcrumb -Path @('Home', 'Apps', 'App Protection', 'VPP Tokens')

        $params = @{
            Uri      = '/deviceAppManagement/vppTokens'
            Beta     = $true
            PageSize = 25
            Select   = 'id,organizationName,appleId,state,tokenActionStatus,lastSyncDateTime,expirationDateTime,lastModifiedDateTime'
        }

        $tokens = Show-InTUILoading -Title "[green]Loading VPP tokens...[/]" -ScriptBlock {
            Get-InTUIPagedResults @params
        }

        if ($null -eq $tokens -or $tokens.Results.Count -eq 0) {
            Show-InTUIWarning "No VPP tokens found."
            Read-InTUIKey
            $exitList = $true
            continue
        }

        $tokenChoices = @()
        foreach ($token in $tokens.Results) {
            $stateColor = switch ($token.state) {
                'valid'   { 'green' }
                'expired' { 'red' }
                default   { 'yellow' }
            }

            $expiresDisplay = Format-InTUIDate -DateString $token.expirationDateTime
            $expiresColor = 'grey'
            if ($token.expirationDateTime) {
                try {
                    $expDate = [DateTime]::Parse($token.expirationDateTime)
                    $daysUntilExpiry = ($expDate - [DateTime]::UtcNow).TotalDays
                    if ($daysUntilExpiry -le 30) {
                        $expiresColor = 'red'
                    }
                }
                catch { $null = $_ }
            }

            $orgName = $token.organizationName ?? 'Unknown'
            $displayName = "[white]$orgName[/] [grey]| $($token.appleId) |[/] [$stateColor]$($token.state)[/] [grey]| Expires:[/] [$expiresColor]$expiresDisplay[/]"
            $tokenChoices += $displayName
        }

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

        Show-InTUIStatusBar -Total $tokens.TotalCount -Showing $tokens.Results.Count

        $selection = Show-InTUIMenu -Title "[green]Select a VPP token[/]" -Choices $menuChoices

        if ($selection -eq 'Back') {
            $exitList = $true
        }
        elseif ($selection -ne '─────────────') {
            $idx = $choiceMap.IndexMap[$selection]
            if ($null -ne $idx -and $idx -lt $tokens.Results.Count) {
                Show-InTUIVppTokenDetail -TokenId $tokens.Results[$idx].id
            }
        }
    }
}

function Show-InTUIVppTokenDetail {
    <#
    .SYNOPSIS
        Displays detailed information about a specific VPP token.
    #>

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

    $exitDetail = $false

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

        $token = Show-InTUILoading -Title "[green]Loading VPP token details...[/]" -ScriptBlock {
            Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/vppTokens/$TokenId" -Beta
        }

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

        Show-InTUIBreadcrumb -Path @('Home', 'Apps', 'App Protection', 'VPP Tokens', ($token.organizationName ?? 'Unknown'))

        Write-InTUILog -Message "Viewing VPP token detail" -Context @{ TokenId = $TokenId; OrgName = $token.organizationName }

        $stateColor = switch ($token.state) {
            'valid'   { 'green' }
            'expired' { 'red' }
            default   { 'yellow' }
        }

        # Check expiration warning
        $expirationWarning = ''
        if ($token.expirationDateTime) {
            try {
                $expDate = [DateTime]::Parse($token.expirationDateTime)
                $daysUntilExpiry = ($expDate - [DateTime]::UtcNow).TotalDays
                if ($daysUntilExpiry -le 30 -and $daysUntilExpiry -gt 0) {
                    $expirationWarning = "`n[red]WARNING: Token expires in $([math]::Floor($daysUntilExpiry)) days![/]"
                }
                elseif ($daysUntilExpiry -le 0) {
                    $expirationWarning = "`n[red]WARNING: Token has expired![/]"
                }
            }
            catch { $null = $_ }
        }

        $propsContent = @"
[bold white]$($token.organizationName ?? 'Unknown')[/]

[grey]Apple ID:[/] $($token.appleId ?? 'N/A')
[grey]State:[/] [$stateColor]$($token.state)[/]
[grey]Token Action Status:[/] $($token.tokenActionStatus ?? 'N/A')
[grey]Country/Region:[/] $($token.countryOrRegion ?? 'N/A')
[grey]Auto Update Apps:[/] $($token.automaticallyUpdateApps ?? 'N/A')
[grey]Last Sync:[/] $(Format-InTUIDate -DateString $token.lastSyncDateTime)
[grey]Last Sync Status:[/] $($token.lastSyncStatus ?? 'N/A')
[grey]Expiration:[/] $(Format-InTUIDate -DateString $token.expirationDateTime)
[grey]Last Modified:[/] $(Format-InTUIDate -DateString $token.lastModifiedDateTime)$expirationWarning
"@


        Show-InTUIPanel -Title "[green]VPP Token Properties[/]" -Content $propsContent -BorderColor Green

        $actionChoices = @(
            '─────────────',
            'Back'
        )

        $action = Show-InTUIMenu -Title "[green]Token Actions[/]" -Choices $actionChoices

        Write-InTUILog -Message "VPP token detail action" -Context @{ TokenId = $TokenId; OrgName = $token.organizationName; Action = $action }

        switch ($action) {
            'Back' {
                $exitDetail = $true
            }
            default {
                continue
            }
        }
    }
}

function Show-InTUIWin32AppDependencies {
    <#
    .SYNOPSIS
        Displays Win32 app dependency and supersedence relationships.
    #>

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

        [Parameter()]
        [string]$AppName
    )

    Clear-Host
    Show-InTUIHeader
    Show-InTUIBreadcrumb -Path @('Home', 'Apps', ($AppName ?? 'App'), 'Dependencies')

    Write-InTUILog -Message "Loading Win32 app dependencies" -Context @{ AppId = $AppId; AppName = $AppName }

    $relationships = Show-InTUILoading -Title "[green]Loading app relationships...[/]" -ScriptBlock {
        Invoke-InTUIGraphRequest -Uri "/deviceAppManagement/mobileApps/$AppId/relationships" -Beta
    }

    if (-not $relationships.value -or @($relationships.value).Count -eq 0) {
        Show-InTUIWarning "No dependencies or supersedence configured for this app."
        Read-InTUIKey
        return
    }

    $rows = @()
    foreach ($rel in $relationships.value) {
        $targetAppName = $rel.targetDisplayName ?? $rel.targetId ?? 'N/A'

        $relType = switch ($rel.targetType) {
            'child'  { 'Dependency' }
            'parent' { 'Supersedence' }
            default  { $rel.targetType ?? 'N/A' }
        }

        $depType = switch ($rel.dependencyType) {
            'autoInstall' { 'Auto Install' }
            'detect'      { 'Detect' }
            default       { $rel.dependencyType ?? 'N/A' }
        }

        $rows += , @($targetAppName, $relType, $depType)
    }

    Show-InTUITable -Title "App Relationships" -Columns @('Target App', 'Relationship Type', 'Dependency Type') -Rows $rows

    Write-InTUILog -Message "Displayed Win32 app dependencies" -Context @{ AppId = $AppId; RelationshipCount = @($relationships.value).Count }

    Read-InTUIKey
}