html-export.ps1

# Function to get assignment information for the HTML report.
# Differs from the module's private Get-HtmlAssignmentInfo: aggregates all assignments
# (not just the first), understands raw Graph API assignment objects (target with
# @odata.type), and returns a combined Filter display string.
function Get-HtmlAssignmentInfo {
    param (
        [Parameter(Mandatory = $true)]
        [AllowNull()]
        [array]$Assignments
    )

    if ($null -eq $Assignments -or $Assignments.Count -eq 0) {
        return @{
            Type   = "None"
            Target = "Not Assigned"
        }
    }

    $types = @()
    $targets = @()
    $filterNames = @()
    $filterTypes = @()

    foreach ($assignment in $Assignments) {
        $type = "None"
        $target = "Not Assigned"
        $groupId = $null
        $filterId = $null
        $filterType = $null

        # Handle Graph API format (with target object)
        if ($assignment.target) {
            switch ($assignment.target.'@odata.type') {
                '#microsoft.graph.allLicensedUsersAssignmentTarget' {
                    $type = "All Users"
                    $target = "All Users"
                }
                '#microsoft.graph.allDevicesAssignmentTarget' {
                    $type = "All Devices"
                    $target = "All Devices"
                }
                '#microsoft.graph.groupAssignmentTarget' {
                    $type = "Group"
                    $groupId = $assignment.target.groupId
                }
                '#microsoft.graph.exclusionGroupAssignmentTarget' {
                    $type = "Exclude"
                    $groupId = $assignment.target.groupId
                }
            }
            $rawFilterId   = $assignment.target.deviceAndAppManagementAssignmentFilterId
            $rawFilterType = $assignment.target.deviceAndAppManagementAssignmentFilterType
            if ($rawFilterType -and $rawFilterType -ne 'none' -and $rawFilterId -and $rawFilterId -ne '00000000-0000-0000-0000-000000000000') {
                $filterId   = $rawFilterId
                $filterType = $rawFilterType
            }
        }
        # Handle standard format (with Reason and GroupId)
        else {
            $type = switch ($assignment.Reason) {
                "All Users" { "All Users"; break }
                "All Devices" { "All Devices"; break }
                "Group Assignment" { "Group"; break }
                "Exclude" { "Exclude"; break }
                default { "None" }
            }
            $groupId = $assignment.GroupId
            if ($assignment.FilterId -and $assignment.FilterType -and $assignment.FilterType -ne 'none') {
                $filterId   = $assignment.FilterId
                $filterType = $assignment.FilterType
            }
        }

        # Get group name if we have a group ID
        if ($groupId) {
            $groupInfo = Get-GroupInfo -GroupId $groupId
            $target = $groupInfo.DisplayName
        }

        $filterName = ''
        $filterTypeLabel = ''
        if ($filterId) {
            if ($script:AssignmentFilterLookup -and $script:AssignmentFilterLookup.ContainsKey($filterId)) {
                $filterName = $script:AssignmentFilterLookup[$filterId].Name
            }
            else {
                $filterName = "Unknown Filter ($filterId)"
            }
            $filterTypeLabel = switch ($filterType) {
                'include' { 'Include' }
                'exclude' { 'Exclude' }
                default   { $filterType }
            }
        }

        $types += $type
        $targets += $target
        $filterNames += $filterName
        $filterTypes += $filterTypeLabel
    }

    # Determine the primary type (prioritize All Users/Devices over Group)
    $primaryType = if ($types -contains "All Users") {
        "All Users"
    }
    elseif ($types -contains "All Devices") {
        "All Devices"
    }
    elseif ($types -contains "Group") {
        "Group"
    }
    elseif ($types -contains "Exclude") {
        "Exclude"
    }
    else {
        "None"
    }

    $filterDisplay = ($filterNames | Where-Object { $_ } | ForEach-Object -Begin { $i = 0 } -Process {
        $lbl = $filterTypes[$i]; $i++
        "$_ [$lbl]"
    }) -join "; "

    return @{
        Type       = $primaryType
        Target     = ($targets -join "; ")
        FilterName = ($filterNames -join "; ").TrimEnd(';', ' ')
        FilterType = ($filterTypes -join "; ").TrimEnd(';', ' ')
        Filter     = $filterDisplay
    }
}

# Intent template family enrichment ($script:IntentTemplateSubtypeToFamily,
# Get-IntentTemplateFamilyLookup, Add-IntentTemplateFamilyInfo) comes from the
# module scope, since this file is dot-sourced inside the module.

function Export-HTMLReport {
    param (
        [Parameter(Mandatory = $true)]
        [string]$FilePath
    )

    # HTML template with placeholders for $tabHeaders, $tabContent, summary stats, and chart
    $htmlTemplate = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Intune Assignment Report</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdn.datatables.net/1.13.7/css/dataTables.bootstrap5.min.css" rel="stylesheet">
    <link href="https://cdn.datatables.net/buttons/2.4.2/css/buttons.bootstrap5.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" rel="stylesheet">
    <style>
        :root {
            --bg-color: #f5f7fa;
            --text-color: #000;
            --card-bg: #fff;
            --table-bg: #fff;
            --hover-bg: #f8f9fa;
            --border-color: #dee2e6;
        }

        [data-theme="dark"] {
            --bg-color: #1a1a1a;
            --text-color: #fff;
            --card-bg: #2d2d2d;
            --table-bg: #2d2d2d;
            --hover-bg: #3d3d3d;
            --border-color: #404040;
        }

        body {
            padding: 20px;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            transition: background-color 0.3s ease, color 0.3s ease;
        }
        .card {
            margin-bottom: 20px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            border-radius: 10px;
            background-color: var(--card-bg);
            transition: transform 0.2s, background-color 0.3s ease;
            border-color: var(--border-color);
        }
        .card:hover {
            transform: translateY(-2px);
        }
        .badge-all-users {
            background-color: #28a745;
            color: white;
            padding: 5px 10px;
            border-radius: 15px;
        }
        .badge-all-devices {
            background-color: #17a2b8;
            color: white;
            padding: 5px 10px;
            border-radius: 15px;
        }
        .badge-group {
            background-color: #ffc107;
            color: black;
            padding: 5px 10px;
            border-radius: 15px;
        }
        .badge-none {
            background-color: #dc3545;
            color: white;
            padding: 5px 10px;
            border-radius: 15px;
        }
        .badge-exclude {
            background-color: #6c757d;
            color: white;
            padding: 5px 10px;
            border-radius: 15px;
        }
        .summary-card {
            background-color: #f8f9fa;
            border: none;
        }
        .table-container {
            margin-top: 20px;
            background: var(--table-bg);
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
            transition: background-color 0.3s ease;
        }

        .table {
            color: var(--text-color) !important;
        }

        .table thead th {
            color: var(--text-color) !important;
        }

        .table tbody tr:hover {
            background-color: var(--hover-bg) !important;
        }

        .dataTables_info, .dataTables_length, .dataTables_filter label {
            color: var(--text-color) !important;
        }
        .nav-tabs {
            margin-bottom: 20px;
            border-bottom: 2px solid var(--border-color);
        }
        .nav-tabs .nav-link {
            border: none;
            color: #6c757d;
            padding: 10px 20px;
            margin-right: 5px;
            border-radius: 5px 5px 0 0;
        }
        .nav-tabs .nav-link.active {
            color: #0d6efd;
            border-bottom: 2px solid #0d6efd;
            font-weight: 500;
        }
        .tab-content {
            padding: 20px;
            border: 1px solid var(--border-color);
            border-top: none;
            border-radius: 0 0 10px 10px;
            background-color: var(--card-bg);
        }
        .chart-container {
            margin: 10px 0;
            padding: 15px;
            background: var(--card-bg);
            border-radius: 10px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
            height: 300px;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .search-box {
            margin: 20px 0;
            padding: 15px;
            background: white;
            border-radius: 10px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
        }
        .policy-table {
            width: 100% !important;
        }
        .policy-table thead th {
            background-color: #f8f9fa;
            font-weight: 600;
        }
        .report-header {
            background: linear-gradient(135deg, #0d6efd 0%, #0099ff 100%);
            color: white;
            padding: 30px;
            border-radius: 10px;
            margin-bottom: 30px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            animation: fadeIn 0.5s ease-in-out;
            position: relative;
        }

        .theme-toggle {
            position: absolute;
            top: 20px;
            right: 20px;
            background: none;
            border: none;
            color: white;
            font-size: 1.5rem;
            cursor: pointer;
            transition: transform 0.3s ease;
        }

        .theme-toggle:hover {
            transform: scale(1.1);
        }

        @media print {
            body {
                background-color: white !important;
                color: black !important;
            }
            .card, .table-container, .tab-content {
                background-color: white !important;
                color: black !important;
                box-shadow: none !important;
            }
            .theme-toggle, .buttons-collection {
                display: none !important;
            }
            .table {
                color: black !important;
            }
            .table thead th {
                color: black !important;
                background-color: #f8f9fa !important;
            }
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(-20px); }
            to { opacity: 1; transform: translateY(0); }
        }
        .search-box {
            margin: 20px 0;
            padding: 20px;
            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
            border-radius: 10px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
        }
        .search-box input {
            border: 2px solid #dee2e6;
            transition: border-color 0.3s ease;
        }
        .search-box input:focus {
            border-color: #0d6efd;
            box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
        }
        .report-header h1 {
            margin: 0;
            font-weight: 300;
        }
        .report-header p {
            margin: 10px 0 0 0;
            opacity: 0.9;
        }
        .summary-stat {
            text-align: center;
            padding: 20px;
        }
        .summary-stat h3 {
            font-size: 2rem;
            font-weight: 300;
            margin: 10px 0;
            color: #0d6efd;
        }
        .summary-stat p {
            color: #6c757d;
            margin: 0;
        }
        #assignmentTypeFilter {
            border: 2px solid #dee2e6;
            border-radius: 5px;
            padding: 8px;
            transition: all 0.3s ease;
            background-color: var(--card-bg);
            color: var(--text-color);
        }
        #assignmentTypeFilter:focus {
            border-color: #0d6efd;
            box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
            outline: none;
        }
        .form-label {
            color: var(--text-color);
            margin-bottom: 0.5rem;
            font-weight: 500;
        }
    </style>
</head>
<body>
    <div class="container-fluid">
        <div class="report-header">
            <h1>Intune Assignment Report</h1>
            <p>Generated on $(Get-Date -Format "MMMM dd, yyyy HH:mm")</p>
        </div>

        <div class="row mb-4">
            <div class="col-md-12">
                <div class="card summary-card">
                    <div class="card-body">
                        <h5 class="card-title">Summary</h5>
                        <div class="row" id="summary-stats">
                            <!-- Summary stats will be inserted here -->
                        </div>
                    </div>
                </div>
                <!-- Policy overview chart placeholder -->
            </div>
        </div>

        <div class="search-box">
            <div class="row align-items-end mb-2">
                <div class="col-md-6">
                    <div class="form-group">
                        <label for="groupSearch">Search by Group Name:</label>
                        <input type="text" class="form-control" id="groupSearch" placeholder="Enter group name...">
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="form-group">
                        <label for="assignmentTypeFilter" class="form-label">Filter by Assignment Type:</label>
                        <select class="form-select" id="assignmentTypeFilter">
                            <option value="all">All Types</option>
                            <option value="All Users">All Users</option>
                            <option value="All Devices">All Devices</option>
                            <option value="Group">Group</option>
                            <option value="None">None</option>
                            <option value="Exclude">Exclude</option>
                        </select>
                    </div>
                </div>
            </div>
            <div class="row align-items-end">
                <div class="col-md-6">
                    <div class="form-group">
                        <label for="scopeTagFilter" class="form-label">Filter by Scope Tag:</label>
                        <select class="form-select" id="scopeTagFilter">
                            <option value="all">All Scope Tags</option>
                            <!-- Scope tag options will be inserted here -->
                        </select>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="form-group">
                        <label for="platformFilter" class="form-label">Filter by Platform:</label>
                        <select class="form-select" id="platformFilter">
                            <option value="all">All Platforms</option>
                            <!-- Platform options will be inserted here -->
                        </select>
                    </div>
                </div>
            </div>
        </div>

        <ul class="nav nav-tabs" id="assignmentTabs" role="tablist">
            <!-- Tab headers will be inserted here -->
        </ul>

        <div class="tab-content" id="assignmentTabContent">
            <!-- Tab content will be inserted here -->
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/1.11.5/js/dataTables.bootstrap5.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.2.2/js/dataTables.buttons.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.2.2/js/buttons.bootstrap5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.2.2/js/buttons.html5.min.js"></script>
    <script>
        jQuery(document).ready(function() {
            // Initialize DataTables
            var tables = jQuery('.policy-table').DataTable({
                dom: 'Blfrtip',
                buttons: [
                    'copyHtml5',
                    'excelHtml5',
                    'csvHtml5'
                ],
                pageLength: 10,
                lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "All"]],
                ordering: false,
                columnDefs: [
                    {
                        targets: '_all',
                        orderable: false
                    }
                ]
            });

            // Helper: find column index by header text
            function findColumnIndex(dataTable, headerText) {
                return dataTable.columns().header().toArray().findIndex(function(h) {
                    return h.textContent.trim() === headerText;
                });
            }

            // Assignment Type Filter
            jQuery('#assignmentTypeFilter').on('change', function() {
                const filterValue = jQuery(this).val();
                jQuery('.policy-table').each(function() {
                    const dataTable = jQuery(this).DataTable();
                    var colIdx = findColumnIndex(dataTable, 'Assignment Type');
                    if (colIdx >= 0) {
                        if (filterValue === 'all') {
                            dataTable.column(colIdx).search('').draw();
                        } else {
                            dataTable.column(colIdx).search(filterValue, false, false).draw();
                        }
                    }
                });
            });

            // Scope Tag Filter
            jQuery('#scopeTagFilter').on('change', function() {
                const filterValue = jQuery(this).val();
                jQuery('.policy-table').each(function() {
                    const dataTable = jQuery(this).DataTable();
                    var colIdx = findColumnIndex(dataTable, 'Scope Tags');
                    if (colIdx >= 0) {
                        if (filterValue === 'all') {
                            dataTable.column(colIdx).search('').draw();
                        } else {
                            var escaped = filterValue.replace(/[.*+?^`${}()|[\]\\]/g, '\\`$&');
                            dataTable.column(colIdx).search('(?:^|,\\s*)' + escaped + '(?:\\s*,|`$)', true, false).draw();
                        }
                    }
                });
            });

            // Platform Filter
            jQuery('#platformFilter').on('change', function() {
                const filterValue = jQuery(this).val();
                jQuery('.policy-table').each(function() {
                    const dataTable = jQuery(this).DataTable();
                    var colIdx = findColumnIndex(dataTable, 'Platform');
                    if (colIdx >= 0) {
                        if (filterValue === 'all') {
                            dataTable.column(colIdx).search('').draw();
                        } else {
                            var escaped = filterValue.replace(/[.*+?^`${}()|[\]\\]/g, '\\`$&');
                            dataTable.column(colIdx).search('^' + escaped + '`$', true, false).draw();
                        }
                    }
                });
            });

            jQuery('#groupSearch').on('keyup', function() {
                const searchTerm = this.value.toLowerCase();
                jQuery('.policy-table').each(function() {
                    jQuery(this).DataTable().search(searchTerm).draw();
                });
            });

            // Show the first tab by default
            const firstTab = document.querySelector('.nav-tabs .nav-link');
            const firstPane = document.querySelector('.tab-pane');
            if (firstTab) firstTab.classList.add('active');
            if (firstPane) firstPane.classList.add('show', 'active');
        });
    </script>
</body>
</html>
"@


    # Initialize collections
    $policies = @{
        DeviceConfigs             = @()
        SettingsCatalog           = @()
        CompliancePolicies        = @()
        AppProtectionPolicies     = @()
        AppConfigurationPolicies  = @()
        PlatformScripts           = @()
        HealthScripts             = @()
        RequiredApps              = @()
        AvailableApps             = @()
        UninstallApps             = @()
        DeploymentProfiles        = @()
        ESPProfiles              = @()
        AntivirusProfiles         = @()
        DiskEncryptionProfiles    = @()
        FirewallProfiles          = @()
        EndpointDetectionProfiles = @()
        AttackSurfaceProfiles     = @()
        AccountProtectionProfiles = @()
        CloudPCProvisioningPolicies = @()
        CloudPCUserSettings       = @()
    }

    # Fetch all policies
    Write-Host "Fetching Device Configurations..." -ForegroundColor Yellow
    $deviceConfigs = Get-IntuneEntities -EntityType "deviceConfigurations"
    foreach ($config in $deviceConfigs) {
        $assignments = Get-IntuneAssignments -EntityType "deviceConfigurations" -EntityId $config.id
        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignments
        $policies.DeviceConfigs += @{
            Name           = $config.displayName
            ID             = $config.id
            Type           = "Device Configuration"
            Platform       = Get-PolicyPlatform -Policy $config
            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $config.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
            AssignmentType = $assignmentInfo.Type
            AssignedTo     = $assignmentInfo.Target
            Filter         = $assignmentInfo.Filter
        }
    }

    Write-Host "Fetching Settings Catalog Policies..." -ForegroundColor Yellow
    $settingsCatalog = Get-IntuneEntities -EntityType "configurationPolicies"
    foreach ($policy in $settingsCatalog) {
        # Exclude Endpoint Security policies from this generic Settings Catalog fetch
        if ($policy.templateReference -and $policy.templateReference.templateFamily -like "endpointSecurity*") {
            continue
        }
        $assignments = Get-IntuneAssignments -EntityType "configurationPolicies" -EntityId $policy.id
        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignments
        $policies.SettingsCatalog += @{
            Name           = if (-not [string]::IsNullOrWhiteSpace($policy.displayName)) { $policy.displayName } else { $policy.name }
            ID             = $policy.id
            Type           = "Settings Catalog"
            Platform       = Get-PolicyPlatform -Policy $policy
            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $policy.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
            AssignmentType = $assignmentInfo.Type
            AssignedTo     = $assignmentInfo.Target
            Filter         = $assignmentInfo.Filter
        }
    }



    Write-Host "Fetching Compliance Policies..." -ForegroundColor Yellow
    $compliancePolicies = Get-IntuneEntities -EntityType "deviceCompliancePolicies"
    foreach ($policy in $compliancePolicies) {
        $assignments = Get-IntuneAssignments -EntityType "deviceCompliancePolicies" -EntityId $policy.id
        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignments
        $policies.CompliancePolicies += @{
            Name           = $policy.displayName
            ID             = $policy.id
            Type           = "Compliance Policy"
            Platform       = Get-PolicyPlatform -Policy $policy
            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $policy.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
            AssignmentType = $assignmentInfo.Type
            AssignedTo     = $assignmentInfo.Target
            Filter         = $assignmentInfo.Filter
        }
    }

    Write-Host "Fetching App Protection Policies..." -ForegroundColor Yellow
    $appProtectionPolicies = Get-IntuneEntities -EntityType "deviceAppManagement/managedAppPolicies"
    foreach ($policy in $appProtectionPolicies) {
        $policyType = $policy.'@odata.type'
        $assignmentsUri = Get-AppProtectionAssignmentUri -Policy $policy

        if ($assignmentsUri) {
            try {
                $assignmentResponse = Invoke-MgGraphRequest -Uri $assignmentsUri -Method Get
                # Pass the raw .value to Get-HtmlAssignmentInfo as it expects an array of assignment objects
                $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignmentResponse.value 

                $policies.AppProtectionPolicies += @{
                    Name           = $policy.displayName
                    ID             = $policy.id
                    Type           = "App Protection Policy ($($policyType.Split('.')[-1].Replace('ManagedAppProtection','')))"
                    Platform       = Get-PolicyPlatform -Policy $policy
                    ScopeTags      = Get-ScopeTagNames -ScopeTagIds $policy.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
                    AssignmentType = $assignmentInfo.Type
                    AssignedTo     = $assignmentInfo.Target
                    Filter         = $assignmentInfo.Filter
                }
            }
            catch {
                Write-Host "Error fetching assignments for App Protection policy $($policy.displayName): $($_.Exception.Message)" -ForegroundColor Red
            }
        }
    }

    # Get Platform Scripts
    Write-Host "Fetching Platform Scripts..." -ForegroundColor Yellow
    $platformScripts = Get-IntuneEntities -EntityType "deviceManagementScripts"
    foreach ($script in $platformScripts) {
        $assignments = Get-IntuneAssignments -EntityType "deviceManagementScripts" -EntityId $script.id
        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignments
        $policies.PlatformScripts += @{
            Name           = $script.displayName
            ID             = $script.id
            Type           = "PowerShell Script"
            Platform       = "Windows"
            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $script.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
            AssignmentType = $assignmentInfo.Type
            AssignedTo     = $assignmentInfo.Target
            Filter         = $assignmentInfo.Filter
        }
    }

    # Get Proactive Remediation Scripts
    Write-Host "Fetching Proactive Remediation Scripts..." -ForegroundColor Yellow
    $healthScripts = Get-IntuneEntities -EntityType "deviceHealthScripts"
    foreach ($script in $healthScripts) {
        $assignments = Get-IntuneAssignments -EntityType "deviceHealthScripts" -EntityId $script.id
        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignments
        $policies.HealthScripts += @{
            Name           = $script.displayName
            ID             = $script.id
            Type           = "Proactive Remediation Script"
            Platform       = "Windows"
            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $script.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
            AssignmentType = $assignmentInfo.Type
            AssignedTo     = $assignmentInfo.Target
            Filter         = $assignmentInfo.Filter
        }
    }

    # Get Autopilot Deployment Profiles
    Write-Host "Fetching Autopilot Deployment Profiles..." -ForegroundColor Yellow
    $autoProfiles = Get-IntuneEntities -EntityType "windowsAutopilotDeploymentProfiles"
    foreach ($profile in $autoProfiles) {
        $assignments = Get-IntuneAssignments -EntityType "windowsAutopilotDeploymentProfiles" -EntityId $profile.id
        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignments
        $policies.DeploymentProfiles += @{
            Name           = $profile.displayName
            ID             = $profile.id
            Type           = "Autopilot Deployment Profile"
            Platform       = "Windows"
            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $profile.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
            AssignmentType = $assignmentInfo.Type
            AssignedTo     = $assignmentInfo.Target
            Filter         = $assignmentInfo.Filter
        }
    }

    # Get Enrollment Status Page Profiles
    Write-Host "Fetching Enrollment Status Page Profiles..." -ForegroundColor Yellow
    $enrollmentConfigs = Get-IntuneEntities -EntityType "deviceEnrollmentConfigurations"
    $espProfiles = $enrollmentConfigs | Where-Object { $_.'@odata.type' -match 'EnrollmentCompletionPageConfiguration' }
    foreach ($esp in $espProfiles) {
        $assignments = Get-IntuneAssignments -EntityType "deviceEnrollmentConfigurations" -EntityId $esp.id
        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignments
        $policies.ESPProfiles += @{
            Name           = $esp.displayName
            ID             = $esp.id
            Type           = "Enrollment Status Page"
            Platform       = "Windows"
            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $esp.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
            AssignmentType = $assignmentInfo.Type
            AssignedTo     = $assignmentInfo.Target
            Filter         = $assignmentInfo.Filter
        }
    }

    # Get Windows 365 Cloud PC Provisioning Policies
    Write-Host "Fetching Windows 365 Cloud PC Provisioning Policies..." -ForegroundColor Yellow
    try {
        $cloudPCProvisioningPolicies = Get-IntuneEntities -EntityType "virtualEndpoint/provisioningPolicies"
        foreach ($policy in $cloudPCProvisioningPolicies) {
            $rawAssignments = Get-IntuneAssignments -EntityType "virtualEndpoint/provisioningPolicies" -EntityId $policy.id
            $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $rawAssignments
            $policies.CloudPCProvisioningPolicies += @{
                Name           = if (-not [string]::IsNullOrWhiteSpace($policy.displayName)) { $policy.displayName } else { $policy.name }
                ID             = $policy.id
                Type           = "Windows 365 Cloud PC Provisioning Policy"
                Platform       = "Windows"
                ScopeTags      = Get-ScopeTagNames -ScopeTagIds $policy.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
                AssignmentType = $assignmentInfo.Type
                AssignedTo     = $assignmentInfo.Target
                Filter         = $assignmentInfo.Filter
            }
        }
    }
    catch {
        Write-Warning "Unable to fetch Windows 365 Cloud PC Provisioning Policies: $($_.Exception.Message)"
    }

    # Get Windows 365 Cloud PC User Settings
    Write-Host "Fetching Windows 365 Cloud PC User Settings..." -ForegroundColor Yellow
    try {
        $cloudPCUserSettings = Get-IntuneEntities -EntityType "virtualEndpoint/userSettings"
        foreach ($setting in $cloudPCUserSettings) {
            $rawAssignments = Get-IntuneAssignments -EntityType "virtualEndpoint/userSettings" -EntityId $setting.id
            $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $rawAssignments
            $policies.CloudPCUserSettings += @{
                Name           = if (-not [string]::IsNullOrWhiteSpace($setting.displayName)) { $setting.displayName } else { $setting.name }
                ID             = $setting.id
                Type           = "Windows 365 Cloud PC User Setting"
                Platform       = "Windows"
                ScopeTags      = Get-ScopeTagNames -ScopeTagIds $setting.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
                AssignmentType = $assignmentInfo.Type
                AssignedTo     = $assignmentInfo.Target
                Filter         = $assignmentInfo.Filter
            }
        }
    }
    catch {
        Write-Warning "Unable to fetch Windows 365 Cloud PC User Settings: $($_.Exception.Message)"
    }

    # Endpoint Security Policies Fetching
    $endpointSecurityCategories = @(
        @{ Name = "Antivirus"; Key = "AntivirusProfiles"; TemplateFamily = "endpointSecurityAntivirus"; UserFriendlyType = "Antivirus Profile" },
        @{ Name = "Disk Encryption"; Key = "DiskEncryptionProfiles"; TemplateFamily = "endpointSecurityDiskEncryption"; UserFriendlyType = "Disk Encryption Profile" },
        @{ Name = "Firewall"; Key = "FirewallProfiles"; TemplateFamily = "endpointSecurityFirewall"; UserFriendlyType = "Firewall Profile" },
        @{ Name = "Endpoint Detection and Response"; Key = "EndpointDetectionProfiles"; TemplateFamily = "endpointSecurityEndpointDetectionAndResponse"; UserFriendlyType = "EDR Profile" },
        @{ Name = "Attack Surface Reduction"; Key = "AttackSurfaceProfiles"; TemplateFamily = "endpointSecurityAttackSurfaceReduction"; UserFriendlyType = "ASR Profile" },
        @{ Name = "Account Protection"; Key = "AccountProtectionProfiles"; TemplateFamily = "endpointSecurityAccountProtection"; UserFriendlyType = "Account Protection Profile" }
    )

    foreach ($esCategory in $endpointSecurityCategories) {
        Write-Host "Fetching Endpoint Security - $($esCategory.Name) Policies..." -ForegroundColor Yellow
        $processedIds = [System.Collections.Generic.HashSet[string]]::new()

        # 1. Check configurationPolicies (Settings Catalog)
        $allConfigPolicies = Get-IntuneEntities -EntityType "configurationPolicies"
        $configPolicies = $allConfigPolicies | Where-Object { $_.templateReference -and $_.templateReference.templateFamily -eq $esCategory.TemplateFamily }
        if ($configPolicies) {
            foreach ($policy in $configPolicies) {
                if ($processedIds.Add($policy.id)) {
                    $rawAssignments = Get-IntuneAssignments -EntityType "configurationPolicies" -EntityId $policy.id
                    $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $rawAssignments
                    $policies[$esCategory.Key] += @{
                        Name           = if (-not [string]::IsNullOrWhiteSpace($policy.displayName)) { $policy.displayName } else { $policy.name }
                        ID             = $policy.id
                        Type           = $esCategory.UserFriendlyType
                        Platform       = Get-PolicyPlatform -Policy $policy
                        ScopeTags      = Get-ScopeTagNames -ScopeTagIds $policy.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
                        AssignmentType = $assignmentInfo.Type
                        AssignedTo     = $assignmentInfo.Target
                        Filter         = $assignmentInfo.Filter
                    }
                }
            }
        }

        # 2. Check deviceManagement/intents (Templates)
        $allIntentPolicies = Get-IntuneEntities -EntityType "deviceManagement/intents"
        Add-IntentTemplateFamilyInfo -IntentPolicies $allIntentPolicies
        $intentPolicies = $allIntentPolicies | Where-Object { $_.templateReference -and $_.templateReference.templateFamily -eq $esCategory.TemplateFamily }
        if ($intentPolicies) {
            foreach ($policy in $intentPolicies) {
                if ($processedIds.Add($policy.id)) {
                    try {
                        $assignmentsResponse = Invoke-MgGraphRequest -Uri "$script:GraphEndpoint/beta/deviceManagement/intents/$($policy.id)/assignments" -Method Get
                        $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $assignmentsResponse.value # This expects an array
                        $policies[$esCategory.Key] += @{
                            Name           = if (-not [string]::IsNullOrWhiteSpace($policy.displayName)) { $policy.displayName } else { $policy.name }
                            ID             = $policy.id
                            Type           = $esCategory.UserFriendlyType
                            Platform       = Get-PolicyPlatform -Policy $policy
                            ScopeTags      = Get-ScopeTagNames -ScopeTagIds $policy.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
                            AssignmentType = $assignmentInfo.Type
                            AssignedTo     = $assignmentInfo.Target
                            Filter         = $assignmentInfo.Filter
                        }
                    } 
                    catch {
                        Write-Host "Error fetching assignments for $($esCategory.Name) intent $($policy.displayName): $($_.Exception.Message)" -ForegroundColor Red
                    }
                } 
            } 
        } 
    }

    # Get Apps
    Write-Host "Fetching Applications..." -ForegroundColor Yellow
    $appUri = "$script:GraphEndpoint/beta/deviceAppManagement/mobileApps?`$filter=isAssigned eq true"
    $allApps = [System.Collections.Generic.List[object]]::new()
    try {
        $appResponse = Invoke-MgGraphRequest -Uri $appUri -Method Get
        if ($appResponse.value) { $allApps.AddRange([object[]]$appResponse.value) }
        while ($appResponse.'@odata.nextLink') {
            $appResponse = Invoke-MgGraphRequest -Uri $appResponse.'@odata.nextLink' -Method Get
            if ($appResponse.value) { $allApps.AddRange([object[]]$appResponse.value) }
        }
    }
    catch {
        Write-Host "Error fetching applications: $($_.Exception.Message)" -ForegroundColor Red
        Write-Error -Message "Applications fetch failed: $($_.Exception.Message)"
    }

    foreach ($app in $allApps) {
        # Skip built-in and Microsoft apps
        if ($app.isFeatured -or $app.isBuiltIn) {
            continue
        }

        $appId = $app.id
        $assignmentsUri = "$script:GraphEndpoint/beta/deviceAppManagement/mobileApps('$appId')/assignments"
        try {
            $assignmentResponse = Invoke-MgGraphRequest -Uri $assignmentsUri -Method Get
        }
        catch {
            Write-Host "Error fetching assignments for app $($app.displayName): $($_.Exception.Message)" -ForegroundColor Red
            Write-Error -Message "Assignments fetch failed for '$($app.displayName)': $($_.Exception.Message)"
            continue
        }

        foreach ($assignment in $assignmentResponse.value) {
            # Get-HtmlAssignmentInfo expects an array of assignment objects.
            # Here, $assignment is a single assignment object from the loop.
            # We need to wrap it in an array for Get-HtmlAssignmentInfo.
            $currentAssignmentArray = @($assignment) # Ensure it's an array
            $assignmentInfo = Get-HtmlAssignmentInfo -Assignments $currentAssignmentArray
            
            $appInfo = @{
                Name           = $app.displayName
                ID             = $app.id
                Type           = "Application"
                Platform       = Get-PolicyPlatform -Policy $app
                ScopeTags      = Get-ScopeTagNames -ScopeTagIds $app.roleScopeTagIds -ScopeTagLookup $script:ScopeTagLookup
                AssignmentType = $assignmentInfo.Type
                AssignedTo     = $assignmentInfo.Target
                Filter         = $assignmentInfo.Filter
            }

            switch ($assignment.intent) {
                "required" { $policies.RequiredApps += $appInfo }
                "available" { $policies.AvailableApps += $appInfo }
                "uninstall" { $policies.UninstallApps += $appInfo }
            }
        }
    }

    # Collect unique scope tags across all policies
    $allScopeTags = [System.Collections.Generic.HashSet[string]]::new()
    foreach ($catKey in $policies.Keys) {
        foreach ($p in $policies[$catKey]) {
            if ($p.ScopeTags) {
                foreach ($tag in ($p.ScopeTags -split ',')) {
                    $trimmed = $tag.Trim()
                    if ($trimmed) { [void]$allScopeTags.Add($trimmed) }
                }
            }
        }
    }
    $scopeTagOptions = ($allScopeTags | Sort-Object | ForEach-Object {
        $escaped = $_ -replace '&', '&amp;' -replace "'", '&#39;' -replace '<', '&lt;' -replace '>', '&gt;'
        "<option value='$escaped'>$escaped</option>"
    }) -join "`n "

    # Collect unique platforms across all policies
    $allPlatforms = [System.Collections.Generic.HashSet[string]]::new()
    foreach ($catKey in $policies.Keys) {
        foreach ($p in $policies[$catKey]) {
            if ($p.Platform) {
                $trimmed = $p.Platform.Trim()
                if ($trimmed) { [void]$allPlatforms.Add($trimmed) }
            }
        }
    }
    $platformOptions = ($allPlatforms | Sort-Object | ForEach-Object {
        $escaped = $_ -replace '&', '&amp;' -replace "'", '&#39;' -replace '<', '&lt;' -replace '>', '&gt;'
        "<option value='$escaped'>$escaped</option>"
    }) -join "`n "

    # Generate summary statistics
    $summaryStats = @{
        TotalPolicies = 0
        AllUsers      = 0
        AllDevices    = 0
        GroupAssigned = 0
        Unassigned    = 0
    }

    $categories = @(
        @{ Key = 'all'; Name = 'All Policies & Apps' }, 
        @{ Key = 'DeviceConfigs'; Name = 'Device Configurations' },
        @{ Key = 'SettingsCatalog'; Name = 'Settings Catalog' },
        @{ Key = 'CompliancePolicies'; Name = 'Compliance Policies' },
        @{ Key = 'AppProtectionPolicies'; Name = 'App Protection Policies' },
        @{ Key = 'RequiredApps'; Name = 'Required Applications' },
        @{ Key = 'AvailableApps'; Name = 'Available Applications' },
        @{ Key = 'UninstallApps'; Name = 'Uninstall Applications' },
        @{ Key = 'PlatformScripts'; Name = 'Platform Scripts' },
        @{ Key = 'HealthScripts'; Name = 'Proactive Remediation Scripts' },
        @{ Key = 'DeploymentProfiles'; Name = 'Autopilot Deployment Profiles' },
        @{ Key = 'ESPProfiles'; Name = 'Enrollment Status Page Profiles' },
        @{ Key = 'CloudPCProvisioningPolicies'; Name = 'Windows 365 Cloud PC Provisioning Policies' },
        @{ Key = 'CloudPCUserSettings'; Name = 'Windows 365 Cloud PC User Settings' },
        @{ Key = 'AntivirusProfiles'; Name = 'Endpoint Security - Antivirus' },
        @{ Key = 'DiskEncryptionProfiles'; Name = 'Endpoint Security - Disk Encryption' },
        @{ Key = 'FirewallProfiles'; Name = 'Endpoint Security - Firewall' },
        @{ Key = 'EndpointDetectionProfiles'; Name = 'Endpoint Security - EDR' },
        @{ Key = 'AttackSurfaceProfiles'; Name = 'Endpoint Security - ASR' },
        @{ Key = 'AccountProtectionProfiles'; Name = 'Endpoint Security - Account Protection' }
    )

    # Recalculate summary stats for all defined categories in $policies
    foreach ($category in $categories | Where-Object { $_.Key -ne 'all' }) {
        if ($policies.ContainsKey($category.Key)) {
            $items = $policies[$category.Key]
            if ($null -ne $items) { 
                $summaryStats.TotalPolicies += $items.Count
                $summaryStats.AllUsers += ($items | Where-Object { $_.AssignmentType -eq "All Users" }).Count
                $summaryStats.AllDevices += ($items | Where-Object { $_.AssignmentType -eq "All Devices" }).Count
                $summaryStats.GroupAssigned += ($items | Where-Object { $_.AssignmentType -eq "Group" }).Count
                $summaryStats.Unassigned += ($items | Where-Object { $_.AssignmentType -eq "None" }).Count
            }
        }
    }
    
    # Build dynamic tab headers and tab content
    $tabHeaders = ""
    $tabContent = ""

    foreach ($category in $categories) {
        $isActive = ($category -eq $categories[0])
        $categoryId = $category.Key.ToLower()

        $tabHeaders += @"
<li class='nav-item' role='presentation'>
    <button class='nav-link$(if($isActive -and $category.Key -ne 'all'){ ' active' } else { '' })'
            id='$categoryId-tab'
            data-bs-toggle='tab'
            data-bs-target='#$categoryId'
            type='button'
            role='tab'
            aria-controls='$categoryId'
            aria-selected='$(if($isActive -and $category.Key -ne 'all'){ 'true' } else { 'false' })'>
        $($category.Name)
    </button>
</li>
"@


        if ($category.Key -eq 'all') {
            $allTableRows = foreach ($cat in $categories | Where-Object { $_.Key -ne 'all' }) {
                if ($policies.ContainsKey($cat.Key)) {
                    $categoryPolicies = $policies[$cat.Key]
                    if ($categoryPolicies) {
                        foreach ($p in $categoryPolicies) {
                            $badgeClass = switch ($p.AssignmentType) {
                                'All Users' { 'badge-all-users' }
                                'All Devices' { 'badge-all-devices' }
                                'Group' { 'badge-group' }
                                'Exclude' { 'badge-exclude' }
                                default { 'badge-none' }
                            }
                            "<tr>
                                <td>$([System.Net.WebUtility]::HtmlEncode($p.Name))</td>
                                <td>$([System.Net.WebUtility]::HtmlEncode($p.Platform))</td>
                                <td>$([System.Net.WebUtility]::HtmlEncode($p.ScopeTags))</td>
                                <td><span class='badge $badgeClass'>$([System.Net.WebUtility]::HtmlEncode($p.AssignmentType))</span></td>
                                <td>$([System.Net.WebUtility]::HtmlEncode($p.AssignedTo))</td>
                                <td>$([System.Net.WebUtility]::HtmlEncode($p.Filter))</td>
                            </tr>"

                        }
                    }
                }
            }
            $tabContent += @"
<div class='tab-pane fade$(if($isActive){ ' show active' } else { '' })'
     id='$categoryId'
     role='tabpanel'
     aria-labelledby='$categoryId-tab'>
    <div class='table-container'>
        <table class='table table-striped policy-table'>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Platform</th>
                    <th>Scope Tags</th>
                    <th>Assignment Type</th>
                    <th>Assigned To</th>
                    <th>Filter</th>
                </tr>
            </thead>
            <tbody>
                $($allTableRows -join "`n")
            </tbody>
        </table>
    </div>
</div>
"@

        }
        else {
            $tableRows = "" # Initialize to empty string
            if ($policies.ContainsKey($category.Key)) {
                $currentCategoryPolicies = $policies[$category.Key]
                if ($currentCategoryPolicies) {
                    $tableRows = foreach ($p in $currentCategoryPolicies) {
                        $badgeClass = switch ($p.AssignmentType) {
                            'All Users' { 'badge-all-users' }
                            'All Devices' { 'badge-all-devices' }
                            'Group' { 'badge-group' }
                            'Exclude' { 'badge-exclude' }
                            default { 'badge-none' }
                        }
                        "<tr>
                            <td>$([System.Net.WebUtility]::HtmlEncode($p.Name))</td>
                            <td>$([System.Net.WebUtility]::HtmlEncode($p.Platform))</td>
                            <td>$([System.Net.WebUtility]::HtmlEncode($p.ScopeTags))</td>
                            <td><span class='badge $badgeClass'>$([System.Net.WebUtility]::HtmlEncode($p.AssignmentType))</span></td>
                            <td>$([System.Net.WebUtility]::HtmlEncode($p.AssignedTo))</td>
                            <td>$([System.Net.WebUtility]::HtmlEncode($p.Filter))</td>
                        </tr>"

                    }
                }
            }
            $categoryHeaders = "<th>Name</th>
                    <th>Platform</th>
                    <th>Scope Tags</th>
                    <th>Assignment Type</th>
                    <th>Assigned To</th>
                    <th>Filter</th>"

            $tabContent += @"
<div class='tab-pane fade$(if($isActive -and $category.Key -ne 'all'){ ' show active' } else { '' })'
     id='$categoryId'
     role='tabpanel'
     aria-labelledby='$categoryId-tab'>
    <div class='table-container'>
        <table class='table table-striped policy-table'>
            <thead>
                <tr>
                    $categoryHeaders
                </tr>
            </thead>
            <tbody>
                $($tableRows -join "`n")
            </tbody>
        </table>
    </div>
</div>
"@

        }
    }

    # Summary cards
    $summaryCards = @"
<div class='col'>
    <div class='card text-center summary-card'>
        <div class='card-body'>
            <i class='fas fa-layer-group mb-3' style='font-size:2rem;color:#0d6efd;'></i>
            <h5 class='card-title'>Total Policies</h5>
            <h3 class='card-text'>$($summaryStats.TotalPolicies)</h3>
            <p class='text-muted small'>Total configured policies</p>
        </div>
    </div>
</div>
<div class='col'>
    <div class='card text-center summary-card'>
        <div class='card-body'>
            <i class='fas fa-users mb-3' style='font-size:2rem;color:#28a745;'></i>
            <h5 class='card-title'>All Users</h5>
            <h3 class='card-text'>$($summaryStats.AllUsers)</h3>
            <p class='text-muted small'>Assigned to all users</p>
        </div>
    </div>
</div>
<div class='col'>
    <div class='card text-center summary-card'>
        <div class='card-body'>
            <i class='fas fa-laptop mb-3' style='font-size:2rem;color:#17a2b8;'></i>
            <h5 class='card-title'>All Devices</h5>
            <h3 class='card-text'>$($summaryStats.AllDevices)</h3>
            <p class='text-muted small'>Assigned to all devices</p>
        </div>
    </div>
</div>
<div class='col'>
    <div class='card text-center summary-card'>
        <div class='card-body'>
            <i class='fas fa-object-group mb-3' style='font-size:2rem;color:#ffc107;'></i>
            <h5 class='card-title'>Group Assigned</h5>
            <h3 class='card-text'>$($summaryStats.GroupAssigned)</h3>
            <p class='text-muted small'>Assigned to specific groups</p>
        </div>
    </div>
</div>
<div class='col'>
    <div class='card text-center summary-card'>
        <div class='card-body'>
            <i class='fas fa-exclamation-triangle mb-3' style='font-size:2rem;color:#dc3545;'></i>
            <h5 class='card-title'>Unassigned</h5>
            <h3 class='card-text'>$($summaryStats.Unassigned)</h3>
            <p class='text-muted small'>Not assigned to any target</p>
        </div>
    </div>
</div>
"@


    # Insert chart container + Chart.js script
    $chartBlock = @"
<div class='row'>
    <div class='col-md-6'>
        <div class='chart-container'>
            <canvas id='policyDistributionChart'></canvas>
        </div>
    </div>
    <div class='col-md-6'>
        <div class='chart-container'>
            <canvas id='policyTypesChart'></canvas>
        </div>
    </div>
</div>
<script src='https://cdn.jsdelivr.net/npm/chart.js'></script>
<script>
    // Policy Distribution Pie Chart
    var ctx1 = document.getElementById('policyDistributionChart').getContext('2d');
    var policyDistributionChart = new Chart(ctx1, {
        type: 'pie',
        data: {
            labels: ['All Users', 'All Devices', 'Group Assigned', 'Unassigned'],
            datasets: [{
                data: [$($summaryStats.AllUsers), $($summaryStats.AllDevices), $($summaryStats.GroupAssigned), $($summaryStats.Unassigned)],
                backgroundColor: ['#28a745', '#17a2b8', '#ffc107', '#dc3545'],
                hoverOffset: 4
            }]
        },
        options: {
            responsive: true,
            maintainAspectRatio: true,
            plugins: {
                legend: {
                    position: 'bottom',
                    labels: { font: { size: 10 } }
                },
                title: {
                    display: true,
                    text: 'Policy Assignment Distribution',
                    font: { size: 14 }
                }
            }
        }
    });

    // Policy Types Bar Chart
    var ctx2 = document.getElementById('policyTypesChart').getContext('2d');
    var policyTypesChart = new Chart(ctx2, {
        type: 'bar',
        data: {
            labels: ['Device Configs', 'Settings Catalog', 'Compliance', 'App Protection', 'Autopilot Profiles', 'ESP Profiles', 'Windows 365 Provisioning', 'Windows 365 User Settings', 'Scripts', 'Antivirus', 'Disk Encryption', 'Firewall', 'EDR', 'ASR', 'Account Protection'],
            datasets: [{
                label: 'Number of Policies',
                data: [
                    $($policies.DeviceConfigs.Count),
                    $($policies.SettingsCatalog.Count),
                    $($policies.CompliancePolicies.Count),
                    $($policies.AppProtectionPolicies.Count),
                    $($policies.DeploymentProfiles.Count),
                    $($policies.ESPProfiles.Count),
                    $($policies.CloudPCProvisioningPolicies.Count),
                    $($policies.CloudPCUserSettings.Count),
                    ($($policies.PlatformScripts.Count) + $($policies.HealthScripts.Count)),
                    $($policies.AntivirusProfiles.Count),
                    $($policies.DiskEncryptionProfiles.Count),
                    $($policies.FirewallProfiles.Count),
                    $($policies.EndpointDetectionProfiles.Count),
                    $($policies.AttackSurfaceProfiles.Count),
                    $($policies.AccountProtectionProfiles.Count)
                ],
                backgroundColor: [
                    '#4e73df', '#1cc88a', '#36b9cc', '#f6c23e', '#e74a3b', '#6f42c1', '#20c997',
                    '#17a2b8', '#fd7e14', '#858796', '#5a5c69', '#f8f9fc', '#dddfeb', '#d1d3e2', '#b4b6c2', '#6610f2'
                ]
            }]
        },
        options: {
            responsive: true,
            maintainAspectRatio: true,
            plugins: {
                legend: {
                    display: false
                },
                title: {
                    display: true,
                    text: 'Policy Types Distribution',
                    font: { size: 14 }
                }
            },
            scales: {
                y: {
                    beginAtZero: true,
                    ticks: { font: { size: 10 } }
                },
                x: {
                    ticks: { font: { size: 10 } }
                }
            }
        }
    });
</script>
"@


    # Final HTML
    $htmlContent = $htmlTemplate `
        -replace '<!-- Tab headers will be inserted here -->', $tabHeaders `
        -replace '<!-- Tab content will be inserted here -->', $tabContent `
        -replace '<!-- Summary stats will be inserted here -->', $summaryCards `
        -replace '<!-- Policy overview chart placeholder -->', $chartBlock `
        -replace '<!-- Scope tag options will be inserted here -->', $scopeTagOptions `
        -replace '<!-- Platform options will be inserted here -->', $platformOptions

    # Output file
    $htmlContent | Out-File -FilePath $FilePath -Encoding UTF8
    Write-Host "HTML report exported to: $FilePath" -ForegroundColor Green
}