modules/Devolutions.CIEM.PSU/Pages/New-CIEMConfigPage.ps1

function New-CIEMAuthenticationProfileFieldControls {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Provider,

        [Parameter(Mandatory)]
        [string]$Method,

        [string]$SelectedProfileId
    )

    $ErrorActionPreference = 'Stop'

    New-UDGrid -Container -Spacing 2 -Content {
        $currentSelectedProfile = if ($SelectedProfileId) {
            @(Devolutions.CIEM\Get-CIEMAuthenticationProfile -Id $SelectedProfileId) | Select-Object -First 1
        }
        $schema = @(Devolutions.CIEM\Get-CIEMAuthenticationProfileFieldSchema -Provider $Provider -Method $Method | Select-Object -First 1)
        $schemaProvider = [string]$schema.provider
        $schemaMethod = [string]$schema.method
        foreach ($field in @($schema.fields)) {
            $fieldName = [string]$field.name
            $inputId = "authProfileField_${schemaProvider}_${schemaMethod}_$fieldName"
            $existingValue = if ($currentSelectedProfile -and $currentSelectedProfile.Provider -eq $schemaProvider -and $currentSelectedProfile.Method -eq $schemaMethod -and $field.kind -eq 'setting') { $currentSelectedProfile.Settings.$fieldName } else { $null }
            $renderedValue = if ($null -ne $existingValue) { $existingValue } else { '' }
            $fieldType = if ($field.inputType -eq 'password') { 'password' } elseif ($field.inputType -eq 'number') { 'number' } else { 'text' }

            New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 6 -Content {
                if ($field.inputType -eq 'select') {
                    New-UDSelect -Id $inputId -Label $field.label -DefaultValue $renderedValue -FullWidth -Option {
                        foreach ($option in @($field.options)) {
                            New-UDSelectOption -Name $option -Value $option
                        }
                    }
                }
                elseif ($field.inputType -eq 'upload') {
                    New-UDUpload -Id $inputId -Text $field.label -Accept '.pfx,.p12' -OnUpload {
                        $upload = $Body | ConvertFrom-Json -ErrorAction Stop
                        if ([string]::IsNullOrWhiteSpace([string]$upload.data)) {
                            throw 'Authentication profile upload did not include file data.'
                        }
                        $Page:UploadedAuthProfileSecretFiles[$fieldName] = [PSCustomObject]@{
                            Name = [string]$upload.name
                            Data = [string]$upload.data
                        }
                        Sync-UDElement -Id 'authProfileUploadStatus'
                    }
                }
                elseif ($field.kind -eq 'setting') {
                    New-UDTextbox -Id $inputId -Label $field.label -Value $renderedValue -Type $fieldType -FullWidth
                }
                else {
                    $value = if ($currentSelectedProfile -and $currentSelectedProfile.Provider -eq $schemaProvider -and $currentSelectedProfile.Method -eq $schemaMethod -and $field.kind -eq 'secret' -and $currentSelectedProfile.SecretRefs.$fieldName) { '********' } else { '' }
                    New-UDTextbox -Id $inputId -Label $field.label -Value $value -Type $fieldType -FullWidth
                }
            }
        }

        New-UDGrid -Item -ExtraSmallSize 12 -Content {
            New-UDDynamic -Id 'authProfileUploadStatus' -Content {
                foreach ($fileKey in @($Page:UploadedAuthProfileSecretFiles.Keys)) {
                    New-UDChip -Label "$($fileKey): $($Page:UploadedAuthProfileSecretFiles[$fileKey].Name)" -Size 'small'
                }
            }
        }
    }
}

function Set-CIEMAuthenticationProfileFormContent {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$Provider,

        [Parameter(Mandatory)]
        [string]$Method
    )

    $ErrorActionPreference = 'Stop'

    $nameElement = Get-UDElement -Id 'authProfileName'
    if ($null -eq $nameElement) {
        throw 'Authentication profile form did not render the name field.'
    }

    $Page:AuthenticationProfileName = [string]$nameElement.value
    $Page:AuthenticationProfileProvider = $Provider
    $Page:AuthenticationProfileMethod = $Method

    Set-UDElement -Id 'authenticationProfileForm' -Content {
        New-CIEMAuthenticationProfileFormContent `
            -Provider ([string]$Page:AuthenticationProfileProvider) `
            -Method ([string]$Page:AuthenticationProfileMethod) `
            -Name ([string]$Page:AuthenticationProfileName) `
            -SelectedProfileId $Page:SelectedAuthenticationProfileId
    }
}

function New-CIEMAuthenticationProfileFormContent {
    [CmdletBinding()]
    param(
        [string]$Provider = '',

        [string]$Method = '',

        [string]$Name = '',

        [string]$SelectedProfileId
    )

    $ErrorActionPreference = 'Stop'

    $providerSelected = -not [string]::IsNullOrWhiteSpace($Provider)
    $methodSelected = -not [string]::IsNullOrWhiteSpace($Method)
    if ($providerSelected) {
        $providerSchemas = @(Devolutions.CIEM\Get-CIEMAuthenticationProfileFieldSchema -Provider $Provider)
        if ($providerSchemas.Count -eq 0) {
            throw "No authentication profile methods are configured for provider '$Provider'."
        }
        if ($methodSelected -and @($providerSchemas.method) -notcontains $Method) {
            $Method = [string]$providerSchemas[0].method
        }
    }

    $Page:AuthenticationProfileProvider = $Provider
    $Page:AuthenticationProfileMethod = $Method
    $Page:AuthenticationProfileName = $Name

    New-UDGrid -Container -Spacing 2 -Content {
        New-UDGrid -Item -ExtraSmallSize 12 -Content {
            New-UDTextbox -Id 'authProfileName' -Label 'Name' -Value $Name -FullWidth -OnChange {
                $Page:AuthenticationProfileName = [string]$EventData
            }
        }
        New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 6 -Content {
            New-UDTypography -Text 'Provider' -Variant 'caption'
            New-UDElement -Tag 'input' -Id 'authProfileProvider' -Attributes @{ type = 'hidden'; value = $Provider }
            New-UDElement -Tag 'div' -Attributes @{ style = @{ display = 'flex'; gap = '8px'; flexWrap = 'wrap' } } -Content {
                $awsProviderButtonVariant = if ('AWS' -eq $Provider) { 'contained' } else { 'outlined' }
                New-UDButton -Id 'authProfileProviderOption_AWS' -Text 'AWS' -Variant $awsProviderButtonVariant -OnClick {
                    Set-CIEMAuthenticationProfileFormContent -Provider 'AWS' -Method 'CurrentProfile'
                }
                $azureProviderButtonVariant = if ('Azure' -eq $Provider) { 'contained' } else { 'outlined' }
                New-UDButton -Id 'authProfileProviderOption_Azure' -Text 'Azure' -Variant $azureProviderButtonVariant -OnClick {
                    Set-CIEMAuthenticationProfileFormContent -Provider 'Azure' -Method 'ServicePrincipalSecret'
                }
                $emailProviderButtonVariant = if ('Email' -eq $Provider) { 'contained' } else { 'outlined' }
                New-UDButton -Id 'authProfileProviderOption_Email' -Text 'Email' -Variant $emailProviderButtonVariant -OnClick {
                    Set-CIEMAuthenticationProfileFormContent -Provider 'Email' -Method 'SmtpAnonymous'
                }
            }
        }
        if ($providerSelected -and $methodSelected) {
            New-UDGrid -Item -ExtraSmallSize 12 -Content {
                New-UDGrid -Container -Spacing 2 -Content {
                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 6 -Content {
                        New-UDTypography -Text 'Method' -Variant 'caption'
                        New-UDElement -Tag 'input' -Id 'authProfileMethod' -Attributes @{ type = 'hidden'; value = $Method }
                        New-UDElement -Tag 'div' -Attributes @{ style = @{ display = 'flex'; gap = '8px'; flexWrap = 'wrap' } } -Content {
                            if ($Provider -eq 'Azure') {
                                $spSecretVariant = if ('ServicePrincipalSecret' -eq $Method) { 'contained' } else { 'outlined' }
                                $spCertificateVariant = if ('ServicePrincipalCertificate' -eq $Method) { 'contained' } else { 'outlined' }
                                $managedIdentityVariant = if ('ManagedIdentity' -eq $Method) { 'contained' } else { 'outlined' }
                                New-UDButton -Id 'authProfileMethodOption_ServicePrincipalSecret' -Text 'Service Principal (Secret)' -Variant $spSecretVariant -OnClick {
                                    Set-CIEMAuthenticationProfileFormContent -Provider 'Azure' -Method 'ServicePrincipalSecret'
                                }
                                New-UDButton -Id 'authProfileMethodOption_ServicePrincipalCertificate' -Text 'Service Principal (Certificate)' -Variant $spCertificateVariant -OnClick {
                                    Set-CIEMAuthenticationProfileFormContent -Provider 'Azure' -Method 'ServicePrincipalCertificate'
                                }
                                New-UDButton -Id 'authProfileMethodOption_ManagedIdentity' -Text 'Managed Identity' -Variant $managedIdentityVariant -OnClick {
                                    Set-CIEMAuthenticationProfileFormContent -Provider 'Azure' -Method 'ManagedIdentity'
                                }
                            }
                            elseif ($Provider -eq 'AWS') {
                                $currentProfileVariant = if ('CurrentProfile' -eq $Method) { 'contained' } else { 'outlined' }
                                $accessKeyVariant = if ('AccessKey' -eq $Method) { 'contained' } else { 'outlined' }
                                New-UDButton -Id 'authProfileMethodOption_CurrentProfile' -Text 'Current Profile' -Variant $currentProfileVariant -OnClick {
                                    Set-CIEMAuthenticationProfileFormContent -Provider 'AWS' -Method 'CurrentProfile'
                                }
                                New-UDButton -Id 'authProfileMethodOption_AccessKey' -Text 'Access Key' -Variant $accessKeyVariant -OnClick {
                                    Set-CIEMAuthenticationProfileFormContent -Provider 'AWS' -Method 'AccessKey'
                                }
                            }
                            elseif ($Provider -eq 'Email') {
                                $smtpAnonymousVariant = if ('SmtpAnonymous' -eq $Method) { 'contained' } else { 'outlined' }
                                $smtpBasicVariant = if ('SmtpBasic' -eq $Method) { 'contained' } else { 'outlined' }
                                New-UDButton -Id 'authProfileMethodOption_SmtpAnonymous' -Text 'SMTP Anonymous' -Variant $smtpAnonymousVariant -OnClick {
                                    Set-CIEMAuthenticationProfileFormContent -Provider 'Email' -Method 'SmtpAnonymous'
                                }
                                New-UDButton -Id 'authProfileMethodOption_SmtpBasic' -Text 'SMTP Basic' -Variant $smtpBasicVariant -OnClick {
                                    Set-CIEMAuthenticationProfileFormContent -Provider 'Email' -Method 'SmtpBasic'
                                }
                            }
                            else {
                                throw "No authentication profile method buttons are configured for provider '$Provider'."
                            }
                        }
                    }
                    New-UDGrid -Item -ExtraSmallSize 12 -Content {
                        New-CIEMAuthenticationProfileFieldControls -Provider $Provider -Method $Method -SelectedProfileId $SelectedProfileId
                    }
                }
            }
        }
    }
    if ($providerSelected -and $methodSelected) {
        New-UDButton -Id 'saveAuthenticationProfileBtn' -Text 'Save Profile' -Variant 'contained' -OnClick {
            try {
                $profileId = if ($Page:SelectedAuthenticationProfileId) { [string]$Page:SelectedAuthenticationProfileId } else { [guid]::NewGuid().ToString() }
                $profileName = [string](Get-UDElement -Id 'authProfileName').value
                $provider = [string]$Page:AuthenticationProfileProvider
                $method = [string]$Page:AuthenticationProfileMethod
                if ([string]::IsNullOrWhiteSpace($provider)) {
                    throw 'Authentication profile form did not submit a provider value.'
                }
                if ([string]::IsNullOrWhiteSpace($method)) {
                    throw 'Authentication profile form did not submit a method value.'
                }
                $fieldSchema = @(Devolutions.CIEM\Get-CIEMAuthenticationProfileFieldSchema -Provider $provider -Method $method | Select-Object -First 1)
                $existingProfile = if ($Page:SelectedAuthenticationProfileId) {
                    @(Devolutions.CIEM\Get-CIEMAuthenticationProfile -Id $profileId) | Select-Object -First 1
                }

                $settings = @{}
                $secretRefs = @{}

                foreach ($field in @($fieldSchema.fields)) {
                    $fieldName = [string]$field.name
                    $inputId = "authProfileField_${provider}_${method}_$fieldName"
                    if ($field.kind -eq 'setting') {
                        $fieldElement = Get-UDElement -Id $inputId
                        if ($null -eq $fieldElement) {
                            throw "Authentication profile form did not render field '$inputId'."
                        }
                        $value = $fieldElement.value
                        if (-not [string]::IsNullOrWhiteSpace([string]$value)) {
                            $settings[$fieldName] = $value
                        }
                    }
                    elseif ($field.kind -eq 'secret') {
                        $secretName = "CIEM_AuthProfile_${profileId}_$fieldName"
                        $existingSecretRef = if ($existingProfile -and $existingProfile.Provider -eq $provider -and $existingProfile.Method -eq $method -and $existingProfile.SecretRefs.PSObject.Properties[$fieldName]) {
                            [string]$existingProfile.SecretRefs.$fieldName
                        }
                        if ($field.inputType -eq 'upload') {
                            if ($Page:UploadedAuthProfileSecretFiles.ContainsKey($fieldName)) {
                                Devolutions.CIEM\Set-CIEMSecret $secretName ([string]$Page:UploadedAuthProfileSecretFiles[$fieldName].Data)
                                $secretRefs[$fieldName] = $secretName
                            }
                            elseif (-not [string]::IsNullOrWhiteSpace($existingSecretRef)) {
                                $secretRefs[$fieldName] = $existingSecretRef
                            }
                        }
                        else {
                            $secretValue = [string](Get-UDElement -Id $inputId).value
                            if (-not [string]::IsNullOrWhiteSpace($secretValue) -and $secretValue -ne '********') {
                                Devolutions.CIEM\Set-CIEMSecret $secretName $secretValue
                                $secretRefs[$fieldName] = $secretName
                            }
                            elseif (-not [string]::IsNullOrWhiteSpace($existingSecretRef)) {
                                $secretRefs[$fieldName] = $existingSecretRef
                            }
                        }
                    }
                    else {
                        throw "Unsupported authentication profile field kind '$($field.kind)'."
                    }
                }

                Devolutions.CIEM\Save-CIEMAuthenticationProfile -Id $profileId -Name $profileName -Provider $provider -Method $method -Settings $settings -SecretRefs $secretRefs | Out-Null
                $Page:SelectedAuthenticationProfileId = $profileId
                $Page:UploadedAuthProfileSecretFiles = @{}
                Hide-UDModal
                Sync-UDElement -Id 'authenticationProfilesTableRegion'
                Show-UDToast -Message 'Authentication profile saved.' -Duration 5000 -BackgroundColor '#4caf50'
            }
            catch {
                Devolutions.CIEM\Write-CIEMLog -Message "Authentication profile save failed: $($_.Exception.Message)" -Severity ERROR -Component 'PSU-ConfigPage'
                Show-UDToast -Message "Authentication profile save failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
            }
        }
    }
}

function Show-CIEMAuthenticationProfileDetailsModal {
    [CmdletBinding()]
    param(
        [string]$SelectedProfileId
    )

    $ErrorActionPreference = 'Stop'

    $selectedProfile = if ($SelectedProfileId) {
        @(Devolutions.CIEM\Get-CIEMAuthenticationProfile -Id $SelectedProfileId) | Select-Object -First 1
    }

    if ($selectedProfile) {
        $Page:SelectedAuthenticationProfileId = [string]$selectedProfile.Id
        $Page:AuthenticationProfileName = [string]$selectedProfile.Name
        $Page:AuthenticationProfileProvider = [string]$selectedProfile.Provider
        $Page:AuthenticationProfileMethod = [string]$selectedProfile.Method
    }
    else {
        $Page:SelectedAuthenticationProfileId = $null
        $Page:AuthenticationProfileName = $null
        $Page:AuthenticationProfileProvider = ''
        $Page:AuthenticationProfileMethod = ''
    }
    $Page:UploadedAuthProfileSecretFiles = @{}

    Show-UDModal -Header {
        New-UDTypography -Text 'Authentication Profile Details' -Variant 'h6'
    } -Content {
        $selectedProvider = if ($null -ne $Page:AuthenticationProfileProvider) { [string]$Page:AuthenticationProfileProvider } else { [string]$selectedProfile.Provider }
        $selectedMethod = if ($null -ne $Page:AuthenticationProfileMethod) { [string]$Page:AuthenticationProfileMethod } else { [string]$selectedProfile.Method }
        $selectedName = if ($Page:AuthenticationProfileName) { [string]$Page:AuthenticationProfileName } elseif ($selectedProfile) { [string]$selectedProfile.Name } else { '' }
        if (-not [string]::IsNullOrWhiteSpace($selectedProvider)) {
            $providerSchemas = @(Devolutions.CIEM\Get-CIEMAuthenticationProfileFieldSchema -Provider $selectedProvider)
            if (-not [string]::IsNullOrWhiteSpace($selectedMethod) -and @($providerSchemas.method) -notcontains $selectedMethod) {
                $selectedMethod = [string]$providerSchemas[0].method
            }
        }
        $Page:AuthenticationProfileProvider = $selectedProvider
        $Page:AuthenticationProfileMethod = $selectedMethod
        $Page:AuthenticationProfileName = $selectedName

        New-UDElement -Tag 'div' -Id 'authenticationProfileForm' -Content {
            New-CIEMAuthenticationProfileFormContent `
                -Provider $selectedProvider `
                -Method $selectedMethod `
                -Name $selectedName `
                -SelectedProfileId $Page:SelectedAuthenticationProfileId
        }

        if ($selectedProfile -and $selectedProfile.Provider -in @('Azure', 'AWS')) {
            New-UDElement -Tag 'div' -Attributes @{ style = @{ marginTop = '24px' } } -Content {
                New-UDButton -Id 'testAuthenticationProfileBtn' -Text 'Test Authentication' -Variant 'outlined' -ShowLoading -OnClick {
                    try {
                        $testProfile = @(Devolutions.CIEM\Get-CIEMAuthenticationProfile -Id $selectedProfile.Id -ResolveSecrets) | Select-Object -First 1
                        Devolutions.CIEM\Connect-CIEM -Provider $testProfile.Provider -AuthenticationProfile $testProfile -Force | Out-Null
                        Show-UDToast -Message "Authentication test succeeded for $($selectedProfile.Name)." -Duration 5000 -BackgroundColor '#4caf50'
                    }
                    catch {
                        Show-UDToast -Message "Authentication test failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
                    }
                }
            }
        }

        if ($selectedProfile) {
            New-UDElement -Tag 'div' -Attributes @{ style = @{ marginTop = '24px' } } -Content {
                New-UDTypography -Text 'Assignments' -Variant 'h6'
                New-UDStack -Direction 'row' -Spacing 1 -Content {
                    if ($selectedProfile.Provider -in @('Azure', 'AWS')) {
                        New-UDButton -Id 'assignProviderDiscoveryBtn' -Text 'Assign to Provider Discovery' -Variant 'outlined' -OnClick {
                            try {
                                Devolutions.CIEM\Set-CIEMAuthenticationProfileAssignment -UsageType 'ProviderDiscovery' -UsageId $selectedProfile.Provider -AuthenticationProfileId $selectedProfile.Id | Out-Null
                                Sync-UDElement -Id 'authenticationProfilesTableRegion'
                                Show-UDToast -Message 'Assignment saved.' -Duration 5000 -BackgroundColor '#4caf50'
                            }
                            catch {
                                Show-UDToast -Message "Assignment failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
                            }
                        }
                    }
                    if ($selectedProfile.Provider -eq 'Email') {
                        New-UDButton -Id 'assignEmailNotificationBtn' -Text 'Assign to Email Notifications' -Variant 'outlined' -OnClick {
                            try {
                                Devolutions.CIEM\Set-CIEMAuthenticationProfileAssignment -UsageType 'NotificationChannel' -UsageId 'email-default' -AuthenticationProfileId $selectedProfile.Id | Out-Null
                                Sync-UDElement -Id 'authenticationProfilesTableRegion'
                                Show-UDToast -Message 'Assignment saved.' -Duration 5000 -BackgroundColor '#4caf50'
                            }
                            catch {
                                Show-UDToast -Message "Assignment failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
                            }
                        }
                    }
                    New-UDButton -Id 'removeAuthenticationProfileBtn' -Text 'Remove' -Variant 'outlined' -Color 'secondary' -OnClick {
                        try {
                            Devolutions.CIEM\Remove-CIEMAuthenticationProfile -Id $selectedProfile.Id
                            Hide-UDModal
                            Sync-UDElement -Id 'authenticationProfilesTableRegion'
                            Show-UDToast -Message 'Authentication profile removed.' -Duration 5000 -BackgroundColor '#4caf50'
                        }
                        catch {
                            Show-UDToast -Message "Remove failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
                        }
                    }
                }
            }
        }
    } -Footer {
        New-UDButton -Id 'cancelAuthenticationProfileEditBtn' -Text 'Cancel' -Variant 'outlined' -OnClick {
            Hide-UDModal
        }
    } -FullWidth -MaxWidth 'md' -Persistent
}

function New-CIEMConfigPage {
    <#
    .SYNOPSIS
        Creates the CIEM Configuration page.
    .PARAMETER Navigation
        Array of UDListItem components for sidebar navigation.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object[]]$Navigation
    )

    $ErrorActionPreference = 'Stop'

    New-UDPage -Name 'Configuration' -Url '/ciem/config' -Content {
        New-UDTypography -Text 'Configuration' -Variant 'h4' -Style @{ marginBottom = '20px'; marginTop = '10px' }
        New-UDTypography -Text 'Configure authentication profiles, scheduled discovery, and outbound notifications' -Variant 'subtitle1' -Style @{ marginBottom = '30px'; color = '#666' }

        New-UDCard -Title 'Authentication Profiles' -Content {
            New-UDStack -Direction 'row' -Spacing 1 -AlignItems 'center' -Content {
                New-UDTextbox -Id 'authProfileSearch' -Label 'Search' -FullWidth -OnChange {
                    Sync-UDElement -Id 'authenticationProfilesTableRegion'
                }
                New-UDButton -Id 'newAuthenticationProfileBtn' -Text 'Add Profile' -Variant 'outlined' -Color 'primary' -OnClick {
                    Show-CIEMAuthenticationProfileDetailsModal
                }
            }

            New-UDDynamic -Id 'authenticationProfilesTableRegion' -Content {
                $profiles = @(Devolutions.CIEM\Get-CIEMAuthenticationProfile)
                $searchElement = Get-UDElement -Id 'authProfileSearch'
                $search = if ($searchElement) { [string]$searchElement.value } else { '' }
                if (-not [string]::IsNullOrWhiteSpace($search)) {
                    $profiles = @($profiles | Where-Object {
                        $_.Name -like "*$search*" -or $_.Provider -like "*$search*" -or $_.Method -like "*$search*"
                    })
                }

                $profileRows = @(foreach ($profile in $profiles) {
                    [PSCustomObject]@{
                        Id        = [string]$profile.Id
                        Name      = [string]$profile.Name
                        Provider  = [string]$profile.Provider
                        Method    = [string]$profile.Method
                        AppliesTo = (@($profile.AppliesTo) -join ', ')
                        UpdatedAt = [string]$profile.UpdatedAt
                    }
                })

                New-UDTable -Id 'authenticationProfilesTable' -Data $profileRows -Columns @(
                    New-UDTableColumn -Property 'Name' -Title 'Name'
                    New-UDTableColumn -Property 'Provider' -Title 'Provider'
                    New-UDTableColumn -Property 'Method' -Title 'Method'
                    New-UDTableColumn -Property 'AppliesTo' -Title 'Assignments'
                    New-UDTableColumn -Property 'Actions' -Title 'Actions' -Render {
                        New-UDButton -Id "editAuthenticationProfile_$($EventData.Id)" -Text 'Edit' -Variant 'outlined' -OnClick {
                            Show-CIEMAuthenticationProfileDetailsModal -SelectedProfileId ([string]$EventData.Id)
                        }
                    }
                ) -Dense -OnRowExpand {
                    New-UDStack -Spacing 1 -Content {
                        New-UDTypography -Text "Id: $($EventData.Id)" -Variant 'caption'
                        New-UDTypography -Text "Provider: $($EventData.Provider)" -Variant 'caption'
                        New-UDTypography -Text "Method: $($EventData.Method)" -Variant 'caption'
                        New-UDTypography -Text "Assignments: $($EventData.AppliesTo)" -Variant 'caption'
                        New-UDTypography -Text "Updated: $($EventData.UpdatedAt)" -Variant 'caption'
                    }
                }
            }
        } -Style @{ marginTop = '24px' }

        New-UDElement -Tag 'div' -Id 'scheduledDiscoveryWrapper' -Content {
            New-UDCard -Title 'Scheduled Discovery' -Content {
                $scheduleRows = @(Devolutions.CIEM\Get-CIEMAzureDiscoverySchedule)
                $schedule = $scheduleRows | Select-Object -First 1
                $selectedScope = if ($schedule) { [string]$schedule.Scope } else { 'All' }
                $selectedCadence = if (-not $schedule -or $schedule.Cron -eq '0 2 * * *') {
                    'daily'
                }
                elseif ($schedule.Cron -eq '0 2 * * 1') {
                    'weekly'
                }
                else {
                    throw "Unsupported scheduled discovery cron '$($schedule.Cron)'."
                }
                $scheduleEnabled = if ($schedule) { [bool]$schedule.Enabled } else { $false }

                New-UDGrid -Container -Spacing 2 -Content {
                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 4 -Content {
                        New-UDSelect -Id 'azureDiscoveryScheduleCadence' -Label 'Cadence' -DefaultValue $selectedCadence -FullWidth -Option {
                            New-UDSelectOption -Name 'Daily' -Value 'daily'
                            New-UDSelectOption -Name 'Weekly' -Value 'weekly'
                        }
                    }
                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 4 -Content {
                        New-UDSelect -Id 'azureDiscoveryScheduleScope' -Label 'Scope' -DefaultValue $selectedScope -FullWidth -Option {
                            New-UDSelectOption -Name 'All' -Value 'All'
                            New-UDSelectOption -Name 'ARM' -Value 'ARM'
                            New-UDSelectOption -Name 'Entra' -Value 'Entra'
                        }
                    }
                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 2 -Content {
                        New-UDSwitch -Id 'azureDiscoveryScheduleEnabled' -Label 'Enabled' -Checked $scheduleEnabled
                    }
                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 2 -Content {
                        New-UDButton -Id 'saveAzureDiscoveryScheduleBtn' -Text 'Save' -Variant 'contained' -Color 'primary' -ShowLoading -OnClick {
                            try {
                                $cadence = [string](Get-UDElement -Id 'azureDiscoveryScheduleCadence').value
                                $scope = [string](Get-UDElement -Id 'azureDiscoveryScheduleScope').value
                                $enabled = [bool](Get-UDElement -Id 'azureDiscoveryScheduleEnabled').checked

                                $cron = switch ($cadence) {
                                    'daily' { '0 2 * * *' }
                                    'weekly' { '0 2 * * 1' }
                                    default { throw "Unsupported scheduled discovery cadence '$cadence'." }
                                }

                                Devolutions.CIEM\Set-CIEMAzureDiscoverySchedule -Scope $scope -Cron $cron -Enabled $enabled | Out-Null
                                Sync-UDElement -Id 'azureDiscoveryScheduleStatus'
                                Show-UDToast -Message 'Scheduled discovery saved.' -Duration 5000 -BackgroundColor '#4caf50'
                            }
                            catch {
                                Devolutions.CIEM\Write-CIEMLog -Message "Save scheduled discovery failed: $($_.Exception.Message)" -Severity ERROR -Component 'PSU-ConfigPage'
                                Show-UDToast -Message "Scheduled discovery save failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
                            }
                        }
                    }
                }

                New-UDDynamic -Id 'azureDiscoveryScheduleStatus' -Content {
                    $currentSchedule = @(Devolutions.CIEM\Get-CIEMAzureDiscoverySchedule) | Select-Object -First 1
                    if ($currentSchedule) {
                        $state = if ($currentSchedule.Enabled) { 'Enabled' } else { 'Disabled' }
                        $lastStatus = if ($currentSchedule.LastStatus) { $currentSchedule.LastStatus } else { 'No scheduled run recorded' }
                        New-UDTypography -Text "$state - $($currentSchedule.Scope) - $($currentSchedule.Cron) - $lastStatus" -Variant 'caption' -Style @{ color = '#666' }
                    }
                    else {
                        New-UDTypography -Text 'Disabled - no schedule configured' -Variant 'caption' -Style @{ color = '#666' }
                    }
                }
            }
        }

        New-UDCard -Title 'Notification Channels' -Content {
            New-UDElement -Tag 'div' -Id 'availableNotificationChannelTypes' -Attributes @{ style = @{ marginBottom = '18px' } } -Content {
                New-UDTypography -Text 'Available Channel Types' -Variant 'subtitle2' -Style @{ marginBottom = '8px'; color = '#555' }
                New-UDStack -Direction 'row' -Spacing 1 -Content {
                    New-UDButton -Id 'addEmailNotificationChannelBtn' -Text 'Add Email Channel' -Variant 'outlined' -Color 'primary' -OnClick {
                        $notificationChannel = @(Devolutions.CIEM\Get-CIEMNotificationChannel -Id 'email-default') | Select-Object -First 1

                        Set-UDElement -Id 'notificationChannelEditorPane' -Content {
                            New-UDElement -Tag 'div' -Attributes @{ style = @{ border = '1px solid #d7dde5'; borderRadius = '6px'; padding = '16px'; marginBottom = '18px'; backgroundColor = '#fafbfc' } } -Content {
                                New-UDTypography -Text 'Email Channel' -Variant 'h6' -Style @{ marginBottom = '12px' }
                                New-UDGrid -Container -Spacing 2 -Content {
                                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 6 -Content {
                                        New-UDTextbox -Id 'notificationFromAddress' -Label 'From Address' -Value $notificationChannel.FromAddress -FullWidth -Placeholder 'ciem@example.com'
                                    }
                                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 6 -Content {
                                        New-UDTextbox -Id 'notificationToRecipients' -Label 'To Recipients' -Value (@($notificationChannel.ToRecipients) -join ', ') -FullWidth -Placeholder 'security@example.com, it@example.com'
                                    }
                                }
                                New-UDStack -Direction 'row' -Spacing 2 -Content {
                                    New-UDButton -Id 'saveNotificationsBtn' -Text 'Save' -Variant 'contained' -Color 'primary' -ShowLoading -OnClick {
                                        try {
                                            $parseRecipients = {
                                                param([string]$RecipientText)
                                                if ([string]::IsNullOrWhiteSpace($RecipientText)) { return }
                                                foreach ($recipient in ($RecipientText -split ',')) {
                                                    $trimmedRecipient = $recipient.Trim()
                                                    if (-not [string]::IsNullOrWhiteSpace($trimmedRecipient)) { $trimmedRecipient }
                                                }
                                            }

                                            $toRecipients = [string[]]@(& $parseRecipients ([string](Get-UDElement -Id 'notificationToRecipients').value))

                                            Devolutions.CIEM\Set-CIEMNotificationChannel `
                                                -Enabled $true `
                                                -FromAddress ([string](Get-UDElement -Id 'notificationFromAddress').value) `
                                                -ToRecipients $toRecipients | Out-Null

                                            Set-UDElement -Id 'notificationChannelEditorPane' -Content {}
                                            Sync-UDElement -Id 'notificationChannelsTableRegion'
                                            Show-UDToast -Message 'Notifications saved.' -Duration 5000 -BackgroundColor '#4caf50'
                                        }
                                        catch {
                                            Devolutions.CIEM\Write-CIEMLog -Message "Save notifications failed: $($_.Exception.Message)" -Severity ERROR -Component 'PSU-ConfigPage'
                                            Show-UDToast -Message "Notification save failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
                                        }
                                    }
                                    New-UDButton -Id 'cancelNotificationChannelEditBtn' -Text 'Cancel' -Variant 'outlined' -OnClick {
                                        Set-UDElement -Id 'notificationChannelEditorPane' -Content {}
                                    }
                                }
                            }
                        }
                    }
                }
            }

            New-UDElement -Tag 'div' -Id 'notificationChannelEditorPane'

            New-UDDynamic -Id 'notificationChannelsTableRegion' -Content {
                $channels = @(Devolutions.CIEM\Get-CIEMNotificationChannel)
                $channelRows = @(foreach ($channel in $channels) {
                    [PSCustomObject]@{
                        Id            = [string]$channel.Id
                        Name          = [string]$channel.Name
                        Type          = [string]$channel.Type
                        Enabled       = [bool]$channel.Enabled
                        Status        = if ($channel.Enabled) { 'Enabled' } else { 'Disabled' }
                        From          = [string]$channel.FromAddress
                        Recipients    = (@($channel.ToRecipients) -join ', ')
                        CcRecipients  = (@($channel.CcRecipients) -join ', ')
                        BccRecipients = (@($channel.BccRecipients) -join ', ')
                        UpdatedAt     = [string]$channel.UpdatedAt
                    }
                })

                New-UDTable -Id 'notificationChannelsTable' -Data $channelRows -Columns @(
                    New-UDTableColumn -Property 'Name' -Title 'Channel'
                    New-UDTableColumn -Property 'Type' -Title 'Type'
                    New-UDTableColumn -Property 'Status' -Title 'Status'
                    New-UDTableColumn -Property 'From' -Title 'From'
                    New-UDTableColumn -Property 'Recipients' -Title 'Recipients'
                    New-UDTableColumn -Property 'Actions' -Title 'Actions' -Render {
                        New-UDButton -Id "editNotificationChannel_$($EventData.Id)" -Text 'Edit' -Variant 'outlined' -OnClick {
                            $notificationChannel = @(Devolutions.CIEM\Get-CIEMNotificationChannel -Id $EventData.Id) | Select-Object -First 1

                            Show-UDModal -Header {
                                New-UDTypography -Text 'Notification Channel Details' -Variant 'h6'
                            } -Content {
                                New-UDGrid -Container -Spacing 2 -Content {
                                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 3 -Content {
                                        New-UDSwitch -Id 'notificationChannelEnabled' -Label 'Enabled' -Checked ([bool]$notificationChannel.Enabled)
                                    }
                                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 9 -Content {
                                        New-UDTextbox -Id 'notificationFromAddress' -Label 'From Address' -Value $notificationChannel.FromAddress -FullWidth -Placeholder 'ciem@example.com'
                                    }
                                    New-UDGrid -Item -ExtraSmallSize 12 -Content {
                                        New-UDTextbox -Id 'notificationToRecipients' -Label 'To Recipients' -Value (@($notificationChannel.ToRecipients) -join ', ') -FullWidth -Placeholder 'security@example.com, it@example.com'
                                    }
                                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 6 -Content {
                                        New-UDTextbox -Id 'notificationCcRecipients' -Label 'Cc Recipients' -Value (@($notificationChannel.CcRecipients) -join ', ') -FullWidth -Placeholder 'manager@example.com'
                                    }
                                    New-UDGrid -Item -ExtraSmallSize 12 -MediumSize 6 -Content {
                                        New-UDTextbox -Id 'notificationBccRecipients' -Label 'Bcc Recipients' -Value (@($notificationChannel.BccRecipients) -join ', ') -FullWidth -Placeholder 'audit@example.com'
                                    }
                                }
                            } -Footer {
                                New-UDStack -Direction 'row' -Spacing 2 -Content {
                                    New-UDButton -Id 'saveNotificationsBtn' -Text 'Save' -Variant 'contained' -Color 'primary' -ShowLoading -OnClick {
                                        try {
                                            $parseRecipients = {
                                                param([string]$RecipientText)
                                                if ([string]::IsNullOrWhiteSpace($RecipientText)) { return }
                                                foreach ($recipient in ($RecipientText -split ',')) {
                                                    $trimmedRecipient = $recipient.Trim()
                                                    if (-not [string]::IsNullOrWhiteSpace($trimmedRecipient)) { $trimmedRecipient }
                                                }
                                            }

                                            $toRecipients = [string[]]@(& $parseRecipients ([string](Get-UDElement -Id 'notificationToRecipients').value))
                                            $ccRecipients = [string[]]@(& $parseRecipients ([string](Get-UDElement -Id 'notificationCcRecipients').value))
                                            $bccRecipients = [string[]]@(& $parseRecipients ([string](Get-UDElement -Id 'notificationBccRecipients').value))

                                            Devolutions.CIEM\Set-CIEMNotificationChannel `
                                                -Enabled ([bool](Get-UDElement -Id 'notificationChannelEnabled').checked) `
                                                -FromAddress ([string](Get-UDElement -Id 'notificationFromAddress').value) `
                                                -ToRecipients $toRecipients `
                                                -CcRecipients $ccRecipients `
                                                -BccRecipients $bccRecipients | Out-Null

                                            Hide-UDModal
                                            Sync-UDElement -Id 'notificationChannelsTableRegion'
                                            Show-UDToast -Message 'Notifications saved.' -Duration 5000 -BackgroundColor '#4caf50'
                                        }
                                        catch {
                                            Devolutions.CIEM\Write-CIEMLog -Message "Save notifications failed: $($_.Exception.Message)" -Severity ERROR -Component 'PSU-ConfigPage'
                                            Show-UDToast -Message "Notification save failed: $($_.Exception.Message)" -Duration 10000 -BackgroundColor '#f44336'
                                        }
                                    }
                                    New-UDButton -Id 'cancelNotificationChannelEditBtn' -Text 'Cancel' -Variant 'outlined' -OnClick {
                                        Hide-UDModal
                                    }
                                }
                            } -FullWidth -MaxWidth 'md' -Persistent
                        }
                    }
                ) -Dense -OnRowExpand {
                    New-UDStack -Spacing 1 -Content {
                        New-UDTypography -Text "Status: $($EventData.Status)" -Variant 'caption'
                        New-UDTypography -Text "Cc: $($EventData.CcRecipients)" -Variant 'caption'
                        New-UDTypography -Text "Bcc: $($EventData.BccRecipients)" -Variant 'caption'
                        New-UDTypography -Text "Updated: $($EventData.UpdatedAt)" -Variant 'caption'
                    }
                }
            }
        } -Style @{ marginTop = '24px' }
    } -Navigation $Navigation -NavigationLayout permanent
}