Generate-M365LicenseAssignmentReport.ps1

<#PSScriptInfo
 
.VERSION 2.5
 
.GUID d23c77c4-d7c2-4b50-8518-9c9ee7718d43
 
.AUTHOR Roy Klooster
 
.COMPANYNAME RK Solutions
 
.COPYRIGHT
 
.TAGS
    RKSolutions
    Microsoft365
    MicrosoftEntraID
    MicrosoftGraph
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
    Initial release - Comprehensive Microsoft 365 license assignment report with HTML output
    v1.0 -2.3 - Test phase
    v2.4 - Added function Test-MgGraphConnection to check for existing connections with the proper permissions
    V2.5 - removed the need of "Microsoft.Graph.Beta.Identity.DirectoryManagement","Microsoft.Graph.Beta.Users", "Microsoft.Graph.Beta.Groups".
    v2.5 - Powershell 7 as requirements have been fixed + faster
                       
.PRIVATEDATA
 
#>


<#
 
.DESCRIPTION
    This script connects to Microsoft 365 services and collects detailed information about
    license assignments across the tenant. It produces a formatted report showing which
    users have been assigned which licenses, including license details and assignment status.
    The report can be exported to various formats for analysis and compliance purposes.
#>
 

param ()

function New-HTMLReport {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Organization,
        
        [Parameter(Mandatory = $true)]
        [array]$Report,
        
        [Parameter(Mandatory = $true)]
        [array]$SubscriptionOverview,
        
        [Parameter(Mandatory = $false)]
        [string]$ExportPath = "$env:PUBLIC\Documents\$Organization-M365LicensingReport.html"
    )

    # Calculate license counts for dashboard statistics
    $directLicenses = ($Report | Where-Object { $_.AssignmentType -eq "Direct" }).Count
    $inheritedLicenses = ($Report | Where-Object { $_.AssignmentType -eq "Inherited" }).Count
    $bothLicenses = ($Report | Where-Object { $_.AssignmentType -eq "Both" }).Count
    $DisabledUsersWithLicenses = ($Report | Where-Object { $_.AccountEnabled -eq "No" }).Count
    
    # Create HTML Template with DataTables
    $htmlTemplate = @'
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>$Organization M365 Licensing Report</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
    <link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.4.1/css/buttons.bootstrap5.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <script src="https://code.jquery.com/jquery-3.7.0.js"></script>
    <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.4.1/js/dataTables.buttons.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.bootstrap5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/pdfmake.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/vfs_fonts.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.html5.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.print.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.colVis.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <style>
        :root {
            /* Light mode variables (default) */
            --primary-color: #0078d4;
            --secondary-color: #2b88d8;
            --direct-color: #0078d4;
            --inherited-color: #107c10;
            --both-color: #5c2d91;
            --Disabled-color: #d83b01;
            --bg-color: #f8f9fa;
            --card-bg: #ffffff;
            --text-color: #333333;
            --table-header-bg: #f5f5f5;
            --table-header-color: #333333;
            --table-stripe-bg: rgba(0,0,0,0.02);
            --table-hover-bg: rgba(0,0,0,0.04);
            --table-border-color: #dee2e6;
            --filter-tag-bg: #e9ecef;
            --filter-tag-color: #495057;
            --filter-bg: white;
            --btn-outline-color: #6c757d;
            --border-color: #dee2e6;
            --toggle-bg: #ccc;
            --button-bg: #f8f9fa;
            --button-color: #333;
            --button-border: #ddd;
            --button-hover-bg: #e9ecef;
            --footer-text: white;
            --input-bg: #fff;
            --input-color: #333;
            --input-border: #ced4da;
            --input-focus-border: #86b7fe;
            --input-focus-shadow: rgba(13, 110, 253, 0.25);
            --datatable-even-row-bg: #fff;
            --datatable-odd-row-bg: rgba(0,0,0,0.02);
        }
         
        [data-theme="dark"] {
            /* Dark mode variables */
            --primary-color: #0078d4;
            --secondary-color: #2b88d8;
            --direct-color: #0078d4;
            --inherited-color: #107c10;
            --both-color: #5c2d91;
            --Disabled-color: #d83b01;
            --bg-color: #121212;
            --card-bg: #1e1e1e;
            --text-color: #e0e0e0;
            --table-header-bg: #333333;
            --table-header-color: #e0e0e0;
            --table-stripe-bg: rgba(255,255,255,0.03);
            --table-hover-bg: rgba(255,255,255,0.05);
            --table-border-color: #444444;
            --filter-tag-bg: #2d2d2d;
            --filter-tag-color: #d0d0d0;
            --filter-bg: #252525;
            --btn-outline-color: #adb5bd;
            --border-color: #444444;
            --toggle-bg: #555555;
            --button-bg: #2a2a2a;
            --button-color: #e0e0e0;
            --button-border: #444;
            --button-hover-bg: #3a3a3a;
            --footer-text: white;
            --input-bg: #2a2a2a;
            --input-color: #e0e0e0;
            --input-border: #444444;
            --input-focus-border: #0078d4;
            --input-focus-shadow: rgba(0, 120, 212, 0.25);
            --datatable-even-row-bg: #1e1e1e;
            --datatable-odd-row-bg: #252525;
        }
         
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 0;
            padding: 0;
            background-color: var(--bg-color);
            color: var(--text-color);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            transition: background-color 0.3s ease, color 0.3s ease;
        }
         
        .container-fluid {
            max-width: 1600px;
            padding: 20px;
            flex: 1;
        }
         
        .dashboard-header {
            padding: 20px 0;
            margin-bottom: 30px;
            border-bottom: 1px solid rgba(128,128,128,0.2);
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
         
        .dashboard-title {
            display: flex;
            align-items: center;
            gap: 15px;
        }
         
        .dashboard-title h1 {
            margin: 0;
            font-size: 1.8rem;
            font-weight: 600;
            color: var(--primary-color);
        }
         
        .logo {
            height: 45px;
            width: 45px;
        }
         
        .report-date {
            font-size: 0.9rem;
            color: var(--text-color);
            opacity: 0.8;
        }
         
        .card {
            border: none;
            border-radius: 10px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.05);
            margin-bottom: 25px;
            transition: transform 0.2s, box-shadow 0.2s, background-color 0.3s ease;
            overflow: hidden;
            background-color: var(--card-bg);
        }
         
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 8px 16px rgba(0,0,0,0.1);
        }
         
        .card-header {
            background-color: var(--primary-color);
            color: white;
            font-weight: 600;
            padding: 15px 20px;
            border-bottom: none;
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 10px;
        }
         
        .card-header i {
            font-size: 1.2rem;
        }
         
        .card-body {
            padding: 20px;
        }
         
        .stats-card {
            height: 100%;
            text-align: center;
            padding: 25px 15px;
            border-radius: 10px;
            color: white;
            position: relative;
            overflow: hidden;
            min-height: 160px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            transition: all 0.3s;
        }
         
        .stats-card.Enabled {
            box-shadow: 0 0 0 4px rgba(255,255,255,0.6), 0 8px 16px rgba(0,0,0,0.2);
            transform: scale(1.05);
        }
         
        .stats-card::before {
            content: '';
            position: absolute;
            top: -20px;
            right: -20px;
            width: 100px;
            height: 100px;
            border-radius: 50%;
            background-color: rgba(255,255,255,0.1);
            z-index: 0;
        }
         
        .stats-card i {
            font-size: 2.5rem;
            margin-bottom: 15px;
            position: relative;
            z-index: 1;
        }
         
        .stats-card h3 {
            font-size: 1rem;
            font-weight: 500;
            margin-bottom: 10px;
            position: relative;
            z-index: 1;
        }
         
        .stats-card .number {
            font-size: 2.2rem;
            font-weight: 700;
            position: relative;
            z-index: 1;
        }
         
        .direct-bg {
            background: linear-gradient(135deg, var(--direct-color), #2b88d8);
        }
         
        .inherited-bg {
            background: linear-gradient(135deg, var(--inherited-color), #2a9d2a);
        }
         
        .both-bg {
            background: linear-gradient(135deg, var(--both-color), #7b4db2);
        }
         
        .Disabled-bg {
            background: linear-gradient(135deg, var(--Disabled-color), #f25c05);
        }
         
        /* DataTables Dark Mode Overrides */
        table.dataTable {
            border-collapse: collapse !important;
            width: 100% !important;
            color: var(--text-color) !important;
            border-color: var(--table-border-color) !important;
        }
         
        .table {
            color: var(--text-color) !important;
            border-color: var(--table-border-color) !important;
        }
         
        .table-striped>tbody>tr:nth-of-type(odd) {
            background-color: var(--datatable-odd-row-bg) !important;
        }
         
        .table-striped>tbody>tr:nth-of-type(even) {
            background-color: var(--datatable-even-row-bg) !important;
        }
         
        .table thead th {
            background-color: var(--table-header-bg) !important;
            color: var(--table-header-color) !important;
            font-weight: 600;
            border-top: none;
            padding: 12px;
            border-color: var(--table-border-color) !important;
        }
         
        .table tbody td {
            padding: 12px;
            vertical-align: middle;
            border-color: var(--table-border-color) !important;
            color: var(--text-color) !important;
        }
         
        .table.table-bordered {
            border-color: var(--table-border-color) !important;
        }
         
        .table-bordered td, .table-bordered th {
            border-color: var(--table-border-color) !important;
        }
         
        .table-hover tbody tr:hover {
            background-color: var(--table-hover-bg) !important;
        }
         
        .badge {
            padding: 6px 10px;
            font-weight: 500;
            border-radius: 6px;
        }
         
        .badge-direct {
            background-color: var(--direct-color);
            color: white;
        }
         
        .badge-inherited {
            background-color: var(--inherited-color);
            color: white;
        }
         
        .badge-both {
            background-color: var(--both-color);
            color: white;
        }
         
        .badge-Disabled {
            background-color: var(--Disabled-color);
            color: white;
        }
         
        .dataTables_wrapper .dataTables_length,
        .dataTables_wrapper .dataTables_filter,
        .dataTables_wrapper .dataTables_info,
        .dataTables_wrapper .dataTables_processing,
        .dataTables_wrapper .dataTables_paginate {
            color: var(--text-color) !important;
        }
         
        .dataTables_wrapper .dataTables_paginate .paginate_button {
            padding: 0.3em 0.8em;
            border-radius: 4px;
            margin: 0 3px;
            color: var(--text-color) !important;
            border: 1px solid var(--border-color) !important;
            background-color: var(--button-bg) !important;
        }
         
        .dataTables_wrapper .dataTables_paginate .paginate_button.current {
            background: var(--primary-color) !important;
            border-color: var(--primary-color) !important;
            color: white !important;
        }
         
        .dataTables_wrapper .dataTables_paginate .paginate_button:hover {
            background: var(--button-hover-bg) !important;
            border-color: var(--border-color) !important;
            color: var(--text-color) !important;
        }
         
        .dataTables_wrapper .dataTables_length select,
        .dataTables_wrapper .dataTables_filter input {
            border: 1px solid var(--input-border);
            background-color: var(--input-bg);
            color: var(--input-color);
            border-radius: 4px;
            padding: 5px 10px;
        }
         
        .dataTables_wrapper .dataTables_filter input:focus {
            border-color: var(--input-focus-border);
            box-shadow: 0 0 0 0.25rem var(--input-focus-shadow);
        }
         
        .dataTables_info {
            padding-top: 10px;
            color: var(--text-color);
        }
         
        footer {
            background-color: var(--primary-color);
            color: var(--footer-text);
            text-align: center;
            padding: 15px 0;
            margin-top: auto;
        }
         
        footer p {
            margin: 0;
            font-weight: 500;
        }
         
        .filter-buttons {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-bottom: 20px;
        }
         
        .filter-button {
            padding: 8px 16px;
            border-radius: 20px;
            border: none;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            gap: 8px;
        }
         
        .filter-button:hover {
            opacity: 0.9;
        }
         
        .filter-button.Enabled {
            box-shadow: 0 0 0 2px rgba(128,128,128,0.2);
        }
         
        .filter-button-direct {
            background-color: var(--direct-color);
            color: white;
        }
         
        .filter-button-inherited {
            background-color: var(--inherited-color);
            color: white;
        }
         
        .filter-button-both {
            background-color: var(--both-color);
            color: white;
        }
         
        .filter-button-Disabled {
            background-color: var(--Disabled-color);
            color: white;
        }
         
        .filter-button-all {
            background-color: var(--btn-outline-color);
            color: white;
        }
         
        .toggle-container {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-bottom: 15px;
        }
         
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 60px;
            height: 30px;
        }
         
        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
         
        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: var(--toggle-bg);
            transition: .4s;
            border-radius: 34px;
        }
         
        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 22px;
            width: 22px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
         
        input:checked + .toggle-slider {
            background-color: var(--primary-color);
        }
         
        input:checked + .toggle-slider:before {
            transform: translateX(30px);
        }
         
        .license-filter-container {
            margin: 15px 0;
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }
         
        .license-badge {
            padding: 6px 12px;
            border-radius: 20px;
            background-color: var(--filter-tag-bg);
            color: var(--filter-tag-color);
            cursor: pointer;
            transition: all 0.2s;
            font-size: 0.85rem;
        }
         
        .license-badge.Enabled {
            background-color: var(--primary-color);
            color: white;
        }
         
        @media (max-width: 768px) {
            .dashboard-header {
                flex-direction: column;
                align-items: flex-start;
                gap: 10px;
            }
             
            .stats-card {
                min-height: 140px;
            }
             
            .filter-buttons {
                flex-direction: column;
            }
        }
         
        /* Export buttons styling */
        div.dt-buttons {
            margin-bottom: 1rem;
        }
         
        button.dt-button {
            background-color: var(--button-bg) !important;
            border-color: var(--button-border) !important;
            color: var(--button-color) !important;
            font-weight: 500 !important;
            padding: 8px 16px !important;
            border-radius: 4px !important;
            margin-right: 8px !important;
        }
         
        button.dt-button:hover {
            background-color: var(--button-hover-bg) !important;
            border-color: var(--border-color) !important;
        }
         
        button.dt-button.Enabled {
            background-color: var(--primary-color) !important;
            border-color: var(--primary-color) !important;
            color: white !important;
        }
         
        .filter-section {
            background-color: var(--filter-bg);
            padding: 15px;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.05);
            margin-bottom: 20px;
            transition: background-color 0.3s ease;
        }
         
        .filter-section h5 {
            color: var(--primary-color);
            margin-bottom: 12px;
            font-weight: 600;
        }
         
        .filter-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-top: 8px;
        }
         
        .filter-tag {
            background-color: var(--filter-tag-bg);
            padding: 4px 12px;
            border-radius: 16px;
            font-size: 0.85rem;
            color: var(--filter-tag-color);
            display: flex;
            align-items: center;
            gap: 6px;
            transition: background-color 0.3s ease, color 0.3s ease;
        }
         
        .filter-tag i {
            cursor: pointer;
            color: var(--filter-tag-color);
        }
         
        .filter-tag i:hover {
            color: var(--Disabled-color);
        }
         
        .custom-search {
            display: flex;
            gap: 10px;
            margin-bottom: 15px;
        }
         
        .custom-search input {
            flex: 1;
            padding: 8px 12px;
            border: 1px solid var(--border-color);
            background-color: var(--bg-color);
            color: var(--text-color);
            border-radius: 4px;
        }
         
        .custom-search button {
            background-color: var(--primary-color);
            color: white;
            border: none;
            border-radius: 4px;
            padding: 8px 16px;
            cursor: pointer;
        }
         
        .Enabled-filters-container {
            margin-bottom: 15px;
        }
         
        /* Show all entries toggle */
        .show-all-container {
            display: flex;
            align-items: center;
            gap: 12px;
            background-color: transparent;
            padding: 0;
            border: none;
            margin-left: 15px;
        }
         
        .show-all-text {
            font-weight: 500;
            margin: 0;
            color: white;
            font-size: 0.85rem;
        }
         
        /* Custom DataTable controls wrapper */
        .datatable-header {
            display: flex;
            align-items: center;
            flex-wrap: wrap;
            margin-bottom: 1rem;
        }
         
        .datatable-controls {
            display: flex;
            align-items: center;
            gap: 15px;
            flex-wrap: wrap;
        }
         
        /* Theme toggle styles */
        .theme-toggle {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 1000;
            display: flex;
            align-items: center;
            gap: 10px;
            background-color: var(--card-bg);
            padding: 8px 12px;
            border-radius: 30px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            transition: background-color 0.3s ease;
        }
         
        .theme-toggle-switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 26px;
        }
         
        .theme-toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
         
        .theme-toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: var(--toggle-bg);
            transition: .4s;
            border-radius: 34px;
        }
         
        .theme-toggle-slider:before {
            position: absolute;
            content: "";
            height: 18px;
            width: 18px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
         
        input:checked + .theme-toggle-slider {
            background-color: var(--primary-color);
        }
         
        input:checked + .theme-toggle-slider:before {
            transform: translateX(24px);
        }
         
        .theme-icon {
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 16px;
            color: var(--text-color);
        }
         
        /* Form elements for dark mode */
        .form-select, .form-control {
            background-color: var(--input-bg) !important;
            color: var(--input-color) !important;
            border-color: var(--input-border) !important;
        }
         
        .form-select:focus, .form-control:focus {
            border-color: var(--input-focus-border) !important;
            box-shadow: 0 0 0 0.25rem var(--input-focus-shadow) !important;
        }
         
        .form-label {
            color: var(--text-color);
        }
         
        .btn-outline-secondary {
            color: var(--text-color);
            border-color: var(--border-color);
            background-color: transparent;
        }
         
        .btn-outline-secondary:hover {
            background-color: var(--filter-tag-bg);
            color: var(--text-color);
        }
         
        /* Override for dropdown menus and selects */
        .form-select option {
            background-color: var(--input-bg);
            color: var(--input-color);
        }
         
        /* Fix DataTables odd/even row striping */
        table.dataTable.stripe tbody tr.odd,
        table.dataTable.display tbody tr.odd {
            background-color: var(--datatable-odd-row-bg) !important;
        }
         
        table.dataTable.stripe tbody tr.even,
        table.dataTable.display tbody tr.even {
            background-color: var(--datatable-even-row-bg) !important;
        }
         
        /* Fix DataTables background color for hovered rows */
        table.dataTable.hover tbody tr:hover,
        table.dataTable.display tbody tr:hover {
            background-color: var(--table-hover-bg) !important;
        }
         
        /* Fix DataTables border colors */
        table.dataTable.border-bottom,
        table.dataTable.border-top,
        table.dataTable thead th,
        table.dataTable tfoot th,
        table.dataTable thead td,
        table.dataTable tfoot td {
            border-color: var(--table-border-color) !important;
        }
         
        /* Bootstrap 5 DataTables specific overrides */
        .table-striped>tbody>tr:nth-of-type(odd)>* {
            --bs-table-accent-bg: var(--datatable-odd-row-bg) !important;
            color: var(--text-color) !important;
        }
         
        .table>:not(caption)>*>* {
            background-color: var(--card-bg) !important;
            color: var(--text-color) !important;
        }
         
        .table-striped>tbody>tr {
            background-color: var(--datatable-even-row-bg) !important;
        }
         
        /* Direct cell background colors */
        .table tbody tr td {
            background-color: transparent !important;
        }
         
        /* Force Bootstrap Tables to use the correct colors */
        .table-striped>tbody>tr:nth-of-type(odd) {
            --bs-table-accent-bg: var(--datatable-odd-row-bg) !important;
        }
    </style>
</head>
<body>
    <!-- Dark Mode Toggle -->
    <div class="theme-toggle">
        <div class="theme-icon">
            <i class="fas fa-sun"></i>
        </div>
        <label class="theme-toggle-switch">
            <input type="checkbox" id="themeToggle">
            <span class="theme-toggle-slider"></span>
        </label>
        <div class="theme-icon">
            <i class="fas fa-moon"></i>
        </div>
    </div>
     
    <div class="container-fluid">
        <div class="dashboard-header">
            <div class="dashboard-title">
                <svg class="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
                    <path fill="#ff5722" d="M6 6H22V22H6z" transform="rotate(-180 14 14)"/>
                    <path fill="#4caf50" d="M26 6H42V22H26z" transform="rotate(-180 34 14)"/>
                    <path fill="#ffc107" d="M6 26H22V42H6z" transform="rotate(-180 14 34)"/>
                    <path fill="#03a9f4" d="M26 26H42V42H26z" transform="rotate(-180 34 34)"/>
                </svg>
                <h1>$Organization M365 Licensing Report</h1>
            </div>
            <div class="report-date">
                <i class="fas fa-calendar-alt me-2"></i> Report generated on: $ReportDate
            </div>
        </div>
         
        <div class="row mb-4">
            <div class="col-md-3 mb-3">
                <div class="stats-card direct-bg" id="directFilter">
                    <i class="fas fa-user-tag"></i>
                    <h3>Direct Licenses</h3>
                    <div class="number">$directLicenses</div>
                </div>
            </div>
            <div class="col-md-3 mb-3">
                <div class="stats-card inherited-bg" id="inheritedFilter">
                    <i class="fas fa-users-cog"></i>
                    <h3>Inherited Licenses</h3>
                    <div class="number">$inheritedLicenses</div>
                </div>
            </div>
            <div class="col-md-3 mb-3">
                <div class="stats-card both-bg" id="bothFilter">
                    <i class="fas fa-user-shield"></i>
                    <h3>Both (Direct + Inherited)</h3>
                    <div class="number">$bothLicenses</div>
                </div>
            </div>
            <div class="col-md-3 mb-3">
                <div class="stats-card Disabled-bg" id="DisabledFilter">
                    <i class="fas fa-user-slash"></i>
                    <h3>Disabled Users with Licenses</h3>
                    <div class="number">$DisabledUsersWithLicenses</div>
                </div>
            </div>
        </div>
         
        <div class="filter-section">
            <h5><i class="fas fa-filter me-2"></i>Filter Options</h5>
             
            <div class="row">
                <div class="col-md-6">
                    <div class="mb-3">
                        <label for="accountStatusFilter" class="form-label">Account Status</label>
                        <select id="accountStatusFilter" class="form-select">
                            <option value="">All Accounts</option>
                            <option value="Enabled">Enabled Accounts</option>
                            <option value="Disabled">Disabled Accounts</option>
                        </select>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="mb-3">
                        <label for="assignmentTypeFilter" class="form-label">Assignment Type</label>
                        <select id="assignmentTypeFilter" class="form-select">
                            <option value="">All Types</option>
                            <option value="Direct">Direct</option>
                            <option value="Inherited">Inherited</option>
                            <option value="Both">Both</option>
                        </select>
                    </div>
                </div>
            </div>
             
            <div class="mb-3">
                <label for="licenseNameFilter" class="form-label">License Name</label>
                <input type="text" id="licenseNameFilter" class="form-control" placeholder="Search for license names...">
            </div>
             
            <div class="mb-3">
                <label class="form-label">Quick Filters</label>
                <div class="filter-buttons">
                    <button class="filter-button filter-button-all" data-filter="all"><i class="fas fa-globe"></i> Show All</button>
                    <button class="filter-button filter-button-direct" data-filter="direct"><i class="fas fa-user-tag"></i> Direct Only</button>
                    <button class="filter-button filter-button-inherited" data-filter="inherited"><i class="fas fa-users-cog"></i> Inherited Only</button>
                    <button class="filter-button filter-button-both" data-filter="both"><i class="fas fa-user-shield"></i> Both</button>
                    <button class="filter-button filter-button-Disabled" data-filter="Disabled"><i class="fas fa-user-slash"></i> Disabled Users</button>
                </div>
            </div>
             
            <div class="Enabled-filters-container">
                <div class="d-flex justify-content-between align-items-center">
                    <label class="form-label mb-0">Enabled Filters:</label>
                    <button id="clearAllFilters" class="btn btn-sm btn-outline-secondary">Clear All</button>
                </div>
                <div class="filter-tags" id="EnabledFilters">
                    <!-- Enabled filters will be displayed here -->
                </div>
            </div>
        </div>
         
        <div class="card">
            <div class="card-header">
                <div>
                    <i class="fas fa-id-card"></i> License Assignment
                </div>
                <div class="show-all-container">
                    <label class="toggle-switch">
                        <input type="checkbox" id="licensesShowAllToggle">
                        <span class="toggle-slider"></span>
                    </label>
                    <p class="show-all-text">Show all entries</p>
                </div>
            </div>
            <div class="card-body">
                <div class="table-responsive">
                    <table id="licensesTable" class="table table-striped table-bordered" style="width:100%">
                        <thead>
                            <tr>
                                <th>Display Name</th>
                                <th>User Principal Name</th>
                                <th>Account Status</th>
                                <th>License</th>
                                <th>Assignment Type</th>
                                <th>Inheritance Details</th>
                            </tr>
                        </thead>
                        <tbody>
                            {{TABLE_DATA}}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
         
        <div class="card">
            <div class="card-header">
                <div>
                    <i class="fas fa-project-diagram"></i> Subscription Overview
                </div>
                <div class="show-all-container">
                    <label class="toggle-switch">
                        <input type="checkbox" id="subscriptionShowAllToggle">
                        <span class="toggle-slider"></span>
                    </label>
                    <p class="show-all-text">Show all entries</p>
                </div>
            </div>
            <div class="card-body">
                <div class="table-responsive">
                    <table id="subscriptionTable" class="table table-striped table-bordered" style="width:100%">
                        <thead>
                            <tr>
                                <th>Subscription</th>
                                <th>Created Date</th>
                                <th>End Date</th>
                                <th>License Status</th>
                                <th>Consumed Units</th>
                                <th>Total Licenses</th>
                                <th>Available Licenses</th>
                            </tr>
                        </thead>
                        <tbody>
                            {{SUBSCRIPTION_DATA}}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
     
    <footer>
        <p>Generated by Roy Klooster - RK Solutions</p>
    </footer>
     
    <script>
        // Initialize DataTables
        $(document).ready(function() {
            // Theme toggling functionality
            const themeToggle = document.getElementById('themeToggle');
            const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
             
            // Function to update table colors for dark mode
            function updateTableColors() {
                // Force all table cells to have the correct background
                if (document.documentElement.getAttribute('data-theme') === 'dark') {
                    // Dark mode
                    $('table.dataTable tbody tr').css('background-color', 'var(--datatable-even-row-bg)');
                    $('table.dataTable tbody tr:nth-child(odd)').css('background-color', 'var(--datatable-odd-row-bg)');
                    $('table.dataTable tbody td').css('color', 'var(--text-color)');
                    $('table.dataTable thead th').css({
                        'background-color': 'var(--table-header-bg)',
                        'color': 'var(--table-header-color)'
                    });
                } else {
                    // Light mode
                    $('table.dataTable tbody tr').css('background-color', 'var(--datatable-even-row-bg)');
                    $('table.dataTable tbody tr:nth-child(odd)').css('background-color', 'var(--datatable-odd-row-bg)');
                    $('table.dataTable tbody td').css('color', 'var(--text-color)');
                    $('table.dataTable thead th').css({
                        'background-color': 'var(--table-header-bg)',
                        'color': 'var(--table-header-color)'
                    });
                }
            }
             
            // Check for saved user preference, or use system preference
            const savedTheme = localStorage.getItem('theme');
            if (savedTheme === 'dark' || (!savedTheme && prefersDarkScheme.matches)) {
                document.documentElement.setAttribute('data-theme', 'dark');
                themeToggle.checked = true;
            }
             
            // Add event listener for theme toggle
            themeToggle.addEventListener('change', function() {
                if (this.checked) {
                    document.documentElement.setAttribute('data-theme', 'dark');
                    localStorage.setItem('theme', 'dark');
                } else {
                    document.documentElement.setAttribute('data-theme', 'light');
                    localStorage.setItem('theme', 'light');
                }
                 
                // Apply the table color changes after theme switch
                setTimeout(updateTableColors, 50);
            });
             
            // Initialize DataTable for licenses
            const licensesTable = $('#licensesTable').DataTable({
                dom: 'Bfrtip',
                buttons: [
                    {
                        extend: 'collection',
                        text: '<i class="fas fa-download"></i> Export',
                        buttons: [
                            {
                                extend: 'excel',
                                text: '<i class="fas fa-file-excel"></i> Excel',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            },
                            {
                                extend: 'csv',
                                text: '<i class="fas fa-file-csv"></i> CSV',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            },
                            {
                                extend: 'pdf',
                                text: '<i class="fas fa-file-pdf"></i> PDF',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            },
                            {
                                extend: 'print',
                                text: '<i class="fas fa-print"></i> Print',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            }
                        ]
                    },
                    {
                        extend: 'colvis',
                        text: '<i class="fas fa-columns"></i> Columns'
                    }
                ],
                paging: true,
                searching: true,
                ordering: true,
                info: true,
                responsive: true,
                lengthMenu: [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
                order: [[0, 'asc']],
                language: {
                    search: "<i class='fas fa-search'></i> _INPUT_",
                    searchPlaceholder: "Search records...",
                    lengthMenu: "Show _MENU_ entries",
                    info: "Showing _START_ to _END_ of _TOTAL_ entries",
                    paginate: {
                        first: "<i class='fas fa-angle-double-left'></i>",
                        last: "<i class='fas fa-angle-double-right'></i>",
                        next: "<i class='fas fa-angle-right'></i>",
                        previous: "<i class='fas fa-angle-left'></i>"
                    }
                },
                drawCallback: function() {
                    // Enforce the correct colors after DataTables redraws
                    updateTableColors();
                }
            });
             
            // Initialize DataTable for subscriptions
            const subscriptionTable = $('#subscriptionTable').DataTable({
                dom: 'Bfrtip',
                buttons: [
                    {
                        extend: 'collection',
                        text: '<i class="fas fa-download"></i> Export',
                        buttons: [
                            {
                                extend: 'excel',
                                text: '<i class="fas fa-file-excel"></i> Excel',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            },
                            {
                                extend: 'csv',
                                text: '<i class="fas fa-file-csv"></i> CSV',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            },
                            {
                                extend: 'pdf',
                                text: '<i class="fas fa-file-pdf"></i> PDF',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            },
                            {
                                extend: 'print',
                                text: '<i class="fas fa-print"></i> Print',
                                exportOptions: {
                                    columns: ':visible'
                                }
                            }
                        ]
                    },
                    {
                        extend: 'colvis',
                        text: '<i class="fas fa-columns"></i> Columns'
                    }
                ],
                paging: true,
                searching: true,
                ordering: true,
                info: true,
                responsive: true,
                lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "All"]],
                language: {
                    search: "<i class='fas fa-search'></i> _INPUT_",
                    searchPlaceholder: "Search records...",
                    lengthMenu: "Show _MENU_ entries",
                    info: "Showing _START_ to _END_ of _TOTAL_ entries",
                    paginate: {
                        first: "<i class='fas fa-angle-double-left'></i>",
                        last: "<i class='fas fa-angle-double-right'></i>",
                        next: "<i class='fas fa-angle-right'></i>",
                        previous: "<i class='fas fa-angle-left'></i>"
                    }
                },
                drawCallback: function() {
                    // Enforce the correct colors after DataTables redraws
                    updateTableColors();
                }
            });
             
            // Apply initial table colors
            setTimeout(updateTableColors, 100);
             
            // Show all toggle functionality for licenses table
            $('#licensesShowAllToggle').on('change', function() {
                if ($(this).is(':checked')) {
                    licensesTable.page.len(-1).draw();
                } else {
                    licensesTable.page.len(10).draw();
                }
            });
             
            // Show all toggle functionality for subscription table
            $('#subscriptionShowAllToggle').on('change', function() {
                if ($(this).is(':checked')) {
                    subscriptionTable.page.len(-1).draw();
                } else {
                    subscriptionTable.page.len(10).draw();
                }
            });
             
            // Custom filtering function
            $.fn.dataTable.ext.search.push(
                function(settings, data, dataIndex) {
                    // Only apply to licenses table
                    if (settings.nTable.id !== 'licensesTable') {
                        return true;
                    }
                     
                    // Get filter values
                    const accountStatus = $('#accountStatusFilter').val();
                    const assignmentType = $('#assignmentTypeFilter').val();
                    const licenseNameFilter = $('#licenseNameFilter').val().toLowerCase();
                     
                    // Get row data
                    const rowAccountStatus = data[2]; // Account Status column
                    const rowAssignmentType = data[4]; // Assignment Type column
                    const rowLicenseName = data[3].toLowerCase(); // License Name column
                     
                    // Filter by account status
                    if (accountStatus && accountStatus === 'Enabled' && !rowAccountStatus.includes('Enabled')) {
                        return false;
                    }
                    if (accountStatus && accountStatus === 'Disabled' && !rowAccountStatus.includes('Disabled')) {
                        return false;
                    }
                     
                    // Filter by assignment type
                    if (assignmentType && !rowAssignmentType.includes(assignmentType)) {
                        return false;
                    }
                     
                    // Filter by license name
                    if (licenseNameFilter && !rowLicenseName.includes(licenseNameFilter)) {
                        return false;
                    }
                     
                    return true;
                }
            );
             
            // Stats card filtering
            $('#directFilter').on('click', function() {
                $('#assignmentTypeFilter').val('Direct');
                updateEnabledFilters('Assignment Type', 'Direct');
                applyFilters();
                toggleStatsCardEnabled('directFilter');
            });
             
            $('#inheritedFilter').on('click', function() {
                $('#assignmentTypeFilter').val('Inherited');
                updateEnabledFilters('Assignment Type', 'Inherited');
                applyFilters();
                toggleStatsCardEnabled('inheritedFilter');
            });
             
            $('#bothFilter').on('click', function() {
                $('#assignmentTypeFilter').val('Both');
                updateEnabledFilters('Assignment Type', 'Both');
                applyFilters();
                toggleStatsCardEnabled('bothFilter');
            });
             
            $('#DisabledFilter').on('click', function() {
                $('#accountStatusFilter').val('Disabled');
                updateEnabledFilters('Account Status', 'Disabled');
                applyFilters();
                toggleStatsCardEnabled('DisabledFilter');
            });
             
            // Button filtering
            $('.filter-button').on('click', function() {
                const filterType = $(this).data('filter');
                 
                // Clear all filter button Enabled states
                $('.filter-button').removeClass('Enabled');
                $(this).addClass('Enabled');
                 
                // Reset filters
                $('#accountStatusFilter').val('');
                $('#assignmentTypeFilter').val('');
                $('#licenseNameFilter').val('');
                clearEnabledFilters();
                 
                // Apply selected filter
                switch(filterType) {
                    case 'direct':
                        $('#assignmentTypeFilter').val('Direct');
                        updateEnabledFilters('Assignment Type', 'Direct');
                        toggleStatsCardEnabled('directFilter');
                        break;
                    case 'inherited':
                        $('#assignmentTypeFilter').val('Inherited');
                        updateEnabledFilters('Assignment Type', 'Inherited');
                        toggleStatsCardEnabled('inheritedFilter');
                        break;
                    case 'both':
                        $('#assignmentTypeFilter').val('Both');
                        updateEnabledFilters('Assignment Type', 'Both');
                        toggleStatsCardEnabled('bothFilter');
                        break;
                    case 'Disabled':
                        $('#accountStatusFilter').val('Disabled');
                        updateEnabledFilters('Account Status', 'Disabled');
                        toggleStatsCardEnabled('DisabledFilter');
                        break;
                    case 'all':
                    default:
                        // Reset all filters
                        $('.stats-card').removeClass('Enabled');
                        break;
                }
                 
                applyFilters();
            });
             
            // Apply filters when select boxes change
            $('#accountStatusFilter, #assignmentTypeFilter').on('change', function() {
                const filterType = $(this).attr('id');
                const filterValue = $(this).val();
                 
                if (filterValue) {
                    if (filterType === 'accountStatusFilter') {
                        updateEnabledFilters('Account Status', filterValue);
                    } else if (filterType === 'assignmentTypeFilter') {
                        updateEnabledFilters('Assignment Type', filterValue);
                    }
                } else {
                    if (filterType === 'accountStatusFilter') {
                        removeEnabledFilter('Account Status');
                    } else if (filterType === 'assignmentTypeFilter') {
                        removeEnabledFilter('Assignment Type');
                    }
                }
                 
                applyFilters();
            });
             
            // Apply filter when license name input changes
            $('#licenseNameFilter').on('input', function() {
                const filterValue = $(this).val();
                 
                if (filterValue) {
                    updateEnabledFilters('License Name', filterValue);
                } else {
                    removeEnabledFilter('License Name');
                }
                 
                applyFilters();
            });
             
            // Clear all filters button
            $('#clearAllFilters').on('click', function() {
                $('#accountStatusFilter').val('');
                $('#assignmentTypeFilter').val('');
                $('#licenseNameFilter').val('');
                $('.filter-button').removeClass('Enabled');
                $('.stats-card').removeClass('Enabled');
                clearEnabledFilters();
                applyFilters();
            });
             
            // Function to apply all filters
            function applyFilters() {
                licensesTable.draw();
            }
             
            // Function to toggle stats card Enabled state
            function toggleStatsCardEnabled(cardId) {
                $('.stats-card').removeClass('Enabled');
                $('#' + cardId).addClass('Enabled');
            }
             
            // Function to update Enabled filters
            function updateEnabledFilters(filterType, filterValue) {
                // Remove existing filter of the same type
                removeEnabledFilter(filterType);
                 
                // Add new filter tag
                const filterTag = `
                    <div class="filter-tag" data-filter-type="${filterType}">
                        <span>${filterType}: ${filterValue}</span>
                        <i class="fas fa-times-circle remove-filter" data-filter-type="${filterType}"></i>
                    </div>
                `;
                 
                $('#EnabledFilters').append(filterTag);
                 
                // Add click handler to remove filter
                $('.remove-filter').off('click').on('click', function() {
                    const filterTypeToRemove = $(this).data('filter-type');
                     
                    if (filterTypeToRemove === 'Account Status') {
                        $('#accountStatusFilter').val('');
                    } else if (filterTypeToRemove === 'Assignment Type') {
                        $('#assignmentTypeFilter').val('');
                    } else if (filterTypeToRemove === 'License Name') {
                        $('#licenseNameFilter').val('');
                    }
                     
                    $(this).closest('.filter-tag').remove();
                     
                    // Remove Enabled state from stat cards and filter buttons
                    if (filterTypeToRemove === 'Account Status' && $('#DisabledFilter').hasClass('Enabled')) {
                        $('#DisabledFilter').removeClass('Enabled');
                    } else if (filterTypeToRemove === 'Assignment Type') {
                        $('#directFilter, #inheritedFilter, #bothFilter').removeClass('Enabled');
                    }
                     
                    $('.filter-button').removeClass('Enabled');
                     
                    applyFilters();
                });
            }
             
            // Function to remove Enabled filter by type
            function removeEnabledFilter(filterType) {
                $('.filter-tag[data-filter-type="' + filterType + '"]').remove();
            }
             
            // Function to clear all Enabled filters
            function clearEnabledFilters() {
                $('#EnabledFilters').empty();
            }
             
            // Force dark mode to take effect on page elements
            $(window).on('load', function() {
                setTimeout(updateTableColors, 200);
            });
             
            // Re-apply styles after DataTables operations
            licensesTable.on('draw.dt', function() {
                setTimeout(updateTableColors, 50);
            });
             
            subscriptionTable.on('draw.dt', function() {
                setTimeout(updateTableColors, 50);
            });
        });
    </script>
</body>
</html>
'@


    # Generate table rows for user licenses
    $tableRows = ""
    foreach ($item in $Report) {
        $accountStatusClass = if ($item.AccountEnabled -eq "No") { 'class="table-danger"' } else { '' }
        
        $assignmentTypeBadge = switch ($item.AssignmentType) {
            "Direct" { '<span class="badge badge-direct">Direct</span>' }
            "Inherited" { '<span class="badge badge-inherited">Inherited</span>' }
            "Both" { '<span class="badge badge-both">Both</span>' }
            default { '<span class="badge bg-secondary">Unknown</span>' }
        }
        
        $accountStatus = if ($item.AccountEnabled -eq "Yes") {
            '<span class="badge bg-success">Enabled</span>'
        } else {
            '<span class="badge badge-Disabled">Disabled</span>'
        }
        
        $tableRows += @"
    <tr $accountStatusClass>
        <td>$($item.DisplayName)</td>
        <td>$($item.UserPrincipalName)</td>
        <td>$accountStatus</td>
        <td>$($item.AssignedLicensesFriendlyName)</td>
        <td>$assignmentTypeBadge</td>
        <td>$($item.Inheritence)</td>
    </tr>
"@

    }

    # Generate table rows for subscription overview
    $subscriptionRows = ""
    foreach ($item in $SubscriptionOverview) {
        $availabilityPercentage = if ($item.TotalLicenses -ne 0) { 
            [Math]::Round(($item.AvailableLicenses / $item.TotalLicenses) * 100) 
        } else { 
            0 
        }
        
        $availabilityBadge = if ($availabilityPercentage -lt 10) {
            '<span class="badge bg-danger">' + $item.AvailableLicenses + ' (' + $availabilityPercentage + '%)</span>'
        } elseif ($availabilityPercentage -lt 20) {
            '<span class="badge bg-warning text-dark">' + $item.AvailableLicenses + ' (' + $availabilityPercentage + '%)</span>'
        } else {
            '<span class="badge bg-success">' + $item.AvailableLicenses + ' (' + $availabilityPercentage + '%)</span>'
        }
        
        $licenseStatusBadge = if ($item.LicenseStatus -eq "Enabled") {
            '<span class="badge bg-success">Enabled</span>'
        } else {
            '<span class="badge bg-danger">Disabled</span>'
        }
        
        $subscriptionRows += @"
    <tr>
        <td>$($item.FriendlyName)</td>
        <td>$($item.CreatedDate)</td>
        <td>$($item.EndDate)</td>
        <td>$licenseStatusBadge</td>
        <td>$($item.ConsumedUnits)</td>
        <td>$($item.TotalLicenses)</td>
        <td>$availabilityBadge</td>
    </tr>
"@

    }

    # Get current date for report
    $currentDate = Get-Date -Format "dd-MM-yyyy HH:mm"

    # Replace placeholders in template with actual values
    $htmlContent = $htmlTemplate.Replace('$Organization', $Organization)
    $htmlContent = $htmlContent.Replace('$ReportDate', $currentDate)
    $htmlContent = $htmlContent.Replace('$directLicenses', $directLicenses)
    $htmlContent = $htmlContent.Replace('$inheritedLicenses', $inheritedLicenses)
    $htmlContent = $htmlContent.Replace('$bothLicenses', $bothLicenses)
    $htmlContent = $htmlContent.Replace('$DisabledUsersWithLicenses', $DisabledUsersWithLicenses)
    $htmlContent = $htmlContent.Replace('{{TABLE_DATA}}', $tableRows)
    $htmlContent = $htmlContent.Replace('{{SUBSCRIPTION_DATA}}', $subscriptionRows)

    # Add additional CSS for dark mode pagination
    $darkModePaginationCss = @"
    <style>
        /* Dark mode pagination buttons */
        [data-theme="dark"] .page-link {
            background-color: var(--button-bg) !important;
            color: var(--text-color) !important;
            border-color: var(--border-color) !important;
        }
         
        [data-theme="dark"] .page-item.Enabled .page-link {
            background-color: var(--primary-color) !important;
            color: white !important;
            border-color: var(--primary-color) !important;
        }
         
        [data-theme="dark"] .page-item.disabled .page-link {
            background-color: var(--card-bg) !important;
            color: #6c757d !important;
            border-color: var(--border-color) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button {
            background-color: var(--button-bg) !important;
            color: var(--text-color) !important;
            border-color: var(--border-color) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button.current,
        [data-theme="dark"] .dataTables_paginate .paginate_button.current:hover {
            background: var(--primary-color) !important;
            color: white !important;
            border-color: var(--primary-color) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button:hover {
            background: var(--button-hover-bg) !important;
            color: var(--text-color) !important;
            border-color: var(--button-border) !important;
        }
         
        [data-theme="dark"] .dataTables_paginate .paginate_button.disabled,
        [data-theme="dark"] .dataTables_paginate .paginate_button.disabled:hover {
            background-color: var(--card-bg) !important;
            color: #6c757d !important;
            border-color: var(--border-color) !important;
            opacity: 0.6;
        }
    </style>
"@

    
    # Insert the dark mode pagination CSS before the </head> tag
    $htmlContent = $htmlContent.Replace('</head>', "$darkModePaginationCss`n</head>")

    # Export to HTML file
    $htmlContent | Out-File -FilePath $ExportPath -Encoding utf8

    Write-Host "All actions completed successfully." -ForegroundColor Cyan
    Write-Host "Report saved to: $ExportPath" -ForegroundColor Cyan

    # Open the HTML file
    Invoke-Item $ExportPath
}

Function Install-Requirements {

    $requiredModules = @(
        "Microsoft.Graph.Authentication"
    )

    foreach ($module in $requiredModules) {
        $moduleVersion = "2.25.0"
        $moduleInstalled = Get-Module -ListAvailable -Name $module | Where-Object { $_.Version -eq $moduleVersion }
    
        if (-not $moduleInstalled) {
            Write-Host "Installing module: $module version $moduleVersion" -ForegroundColor Cyan
            Install-Module -Name $module -Scope CurrentUser -Force -AllowClobber -RequiredVersion $moduleVersion -SkipPublisherCheck
        } else {
            Write-Host "Module $module version $moduleVersion is already installed." -ForegroundColor Green
        }
    }

    # Import the required modules
    foreach ($module in $requiredModules) {
        $moduleVersion = "2.25.0"
        $importedModule = Get-Module -Name $module | Where-Object { $_.Version -eq $moduleVersion }
    
        if (-not $importedModule) {
            try {
                Import-Module -Name $module -RequiredVersion $moduleVersion -Force -ErrorAction Stop
                Write-Host "Module $module version $moduleVersion imported successfully." -ForegroundColor Green
            } catch {
                Write-Host "Failed to import module $module version $moduleVersion. Error: $_" -ForegroundColor Red
                throw
            }
        } else {
            Write-Host "Module $module version $moduleVersion was already imported." -ForegroundColor Green
        }
    }
}

function Test-MgGraphConnection {
    Clear-Host

    # Authentication
    $AuthenticationScope = @(
        "User.Read.All",
        "AuditLog.Read.All",
        "Organization.Read.All",
        "RoleManagement.Read.Directory"
    )

    # Check if already connected
    $Contextinfo = Get-MgContext -ErrorAction SilentlyContinue
    if (-not $Contextinfo) {
        # If not connected, connect now
        Connect-MgGraph -Scopes $AuthenticationScope -NoWelcome
    } else {
        Write-Host "Already connected to Microsoft Graph as $($Contextinfo.Account)." -ForegroundColor Green
    }
        
    Start-Sleep -Seconds 2
    Clear-Host
}

function Invoke-GraphRequestWithPaging {
    param (
        [string]$Uri,
        [string]$Method = "GET"
    )
    
    $results = @()
    $currentUri = $Uri
    
    do {
        try {
            $response = Invoke-MgGraphRequest -Uri $currentUri -Method $Method -OutputType PSObject
            
            # Add the current page of results
            if ($response.value) {
                $results += $response.value
            }
            
            # Get the next page URL if it exists
            $currentUri = $response.'@odata.nextLink'
        } catch {
            Write-Error "Error invoking Graph API: $_"
            break
        }
    } while ($currentUri)
    
    return $results
}

function Get-LicenseIdentifiers {
    $header = 'Product_Display_Name', 'String_Id', 'GUID', 'Service_Plan_Name', 'Service_Plan_Id', 'Service_Plans_Included_Friendly_Names'
    $params = @{
        Method = 'Get'
        Uri    = "https://download.microsoft.com/download/e/3/e/e3e9faf2-f28b-490a-9ada-c6089a1fc5b0/Product%20names%20and%20service%20plan%20identifiers%20for%20licensing.csv"
    }
    $Identifiers = Invoke-RestMethod @params | ConvertFrom-Csv -Header $header |
        ForEach-Object {
            [PSCustomObject]@{
                GUID                 = $_.GUID
                String_Id            = $_.String_Id
                Product_Display_Name = $_.Product_Display_Name
            }
        }
    return $Identifiers | Select-Object -Skip 1
}

# Install required modules
Install-Requirements

# Connect to Microsoft Graph
Test-MgGraphConnection

# Get Organization Name
$Organization = Invoke-MgGraphRequest -Uri "beta/organization" -OutputType PSObject | Select-Object -Expand Value | Select-Object -ExpandProperty DisplayName

# Get product identifiers
$Identifiers = Get-LicenseIdentifiers

# Select all SKUs with friendly display name
[array]$SKU_friendly = $Identifiers | Select-Object GUID, String_Id, Product_Display_Name -Unique

# Get products used in tenant
[Array]$Skus = Invoke-MgGraphRequest -Uri "Beta/subscribedSkus" -OutputType PSObject |
    Select-Object -ExpandProperty Value

# Get subscriptions with their end dates
[Array]$Subscriptions = Invoke-MgGraphRequest -Uri "beta/directory/subscriptions" -OutputType PSObject |
    Select-Object -ExpandProperty Value

# Create an overview of subscriptions with their end date
$SubscriptionOverview = @()
foreach ($subscription in $Subscriptions) {
    $sku = $Skus | Where-Object { $_.SkuId -eq $subscription.SkuId }
    $friendlyName = $SKU_friendly | Where-Object { $_.GUID -eq $sku.SkuId } |
        Select-Object -ExpandProperty Product_Display_Name -ErrorAction SilentlyContinue
    # Use a default if no friendly name is found
    if (-not $friendlyName) {
        $friendlyName = "Unknown License ($($sku.SkuId))"
    }
    $endDate = if ($null -eq $subscription.NextLifecycleDateTime) {
        "No end date found"
    } else {
        $subscription.NextLifecycleDateTime
    }
    # Format dates if they're not strings
    $formattedCreatedDate = if ($subscription.CreatedDateTime -is [DateTime]) {
        Get-Date $subscription.CreatedDateTime -Format "dd-MM-yyyy HH:mm"
    } elseif ($subscription.CreatedDateTime) {
        try {
            Get-Date $subscription.CreatedDateTime -Format "dd-MM-yyyy HH:mm"
        } catch {
            $subscription.CreatedDateTime
        }
    } else {
        "Unknown"
    }
    $formattedEndDate = if ($endDate -is [DateTime]) {
        Get-Date $endDate -Format "dd-MM-yyyy HH:mm"
    } elseif ($endDate -and $endDate -ne "No end date found") {
        try {
            Get-Date $endDate -Format "dd-MM-yyyy HH:mm"
        } catch {
            $endDate
        }
    } else {
        $endDate
    }
    # Determine license status based on end date
    $licenseStatus = if ($endDate -eq "No end date found") {
        "Enabled"
    } elseif ($endDate -is [DateTime] -and $endDate -gt (Get-Date)) {
        "Enabled"
    } elseif ($endDate -ne "No end date found") {
        try {
            $dateObj = [DateTime]$endDate
            if ($dateObj -gt (Get-Date)) {
                "Enabled"
            } else {
                "Disabled"
            }
        } catch {
            "Unknown"
        }
    } else {
        "Unknown"
    }
    # Calculate available licenses safely
    $totalLicenses = if ($subscription.TotalLicenses) { $subscription.TotalLicenses } else { 0 }
    $consumedUnits = if ($sku.ConsumedUnits) { $sku.ConsumedUnits } else { 0 }
    $availableLicenses = $totalLicenses - $consumedUnits
    $SubscriptionOverview += [PSCustomObject]@{
        SubscriptionId    = $subscription.Id
        FriendlyName      = $friendlyName
        CreatedDate       = $formattedCreatedDate
        EndDate           = $formattedEndDate
        LicenseStatus     = $licenseStatus
        ConsumedUnits     = $consumedUnits
        TotalLicenses     = $totalLicenses
        AvailableLicenses = $availableLicenses
    }
}

# Output the overview
Write-Host "INFO: Generating subscription overview..." -ForegroundColor Cyan

# Get all users with licenses - using paging to ensure all results are retrieved
Write-Host "INFO: Retrieving user license data..." -ForegroundColor Cyan
$users = Invoke-GraphRequestWithPaging -Uri "beta/users?`$select=UserPrincipalName,LicenseAssignmentStates,DisplayName,AccountEnabled,AssignedLicenses&`$top=999"

# Get all groups with their licenses
Write-Host "INFO: Retrieving group license data..." -ForegroundColor Cyan
$Groups = Invoke-GraphRequestWithPaging -Uri "beta/groups?`$select=id,displayName,assignedLicenses&`$top=999"
$groupsWithLicenses = @()

# Loop through each group and check if it has any licenses assigned
Write-Host "INFO: Checking groups for licenses..." -ForegroundColor Cyan
foreach ($group in $Groups) {
    if ($group.assignedLicenses -and $group.assignedLicenses.Count -gt 0) {
        $groupData = [PSCustomObject]@{
            ObjectId    = $group.id
            DisplayName = $group.displayName
            Licenses    = $group.assignedLicenses
        }
        $groupsWithLicenses += $groupData
    }
}

# Initialize the report array
$Report = @()

# Process user license data
$totalUsers = $users.Count
$currentIndex = 0

foreach ($user in $users) {
    $currentIndex++
    Write-Progress -Activity "Processing users" -Status "Processing $currentIndex of $totalUsers" -PercentComplete (($currentIndex / $totalUsers) * 100)
    
    # Skip users with no license assignment states
    if (-not $user.LicenseAssignmentStates) {
        continue
    }
    
    # Group licenses by SkuId to detect both direct and inherited assignments
    $licensesBySkuId = @{}
    
    foreach ($license in $user.LicenseAssignmentStates) {
        $SkuId = $license.SkuId
        $AssignedByGroup = $license.AssignedByGroup
        
        if (-not $licensesBySkuId.ContainsKey($SkuId)) {
            $licensesBySkuId[$SkuId] = @{
                DirectAssignment = $false
                GroupAssignments = @()
            }
        }
        
        if ($null -eq $AssignedByGroup) {
            $licensesBySkuId[$SkuId].DirectAssignment = $true
        } else {
            $licensesBySkuId[$SkuId].GroupAssignments += $AssignedByGroup
        }
    }
    
    # Process each unique license
    foreach ($SkuId in $licensesBySkuId.Keys) {
        $licenseInfo = $licensesBySkuId[$SkuId]
        $isDirect = $licenseInfo.DirectAssignment
        $isInherited = ($licenseInfo.GroupAssignments.Count -gt 0)
        
        # Determine assignment type
        $assignmentType = if ($isDirect -and $isInherited) {
            "Both"
        } elseif ($isDirect) {
            "Direct"
        } elseif ($isInherited) {
            "Inherited"
        } else {
            "Unknown"
        }
        
        # Get friendly name for the license
        $friendlyName = $SKU_friendly | Where-Object { $_.GUID -eq $SkuId } |
            Select-Object -ExpandProperty Product_Display_Name -ErrorAction SilentlyContinue
                
        if (-not $friendlyName) {
            $friendlyName = "Unknown License ($SkuId)"
        }
        
        # Get group names if inherited
        $groupNames = ""
        if ($isInherited) {
            $groupNamesList = @()
            foreach ($groupId in $licenseInfo.GroupAssignments) {
                $group = $groupsWithLicenses | Where-Object { $_.ObjectId -eq $groupId }
                if ($group) {
                    $groupNamesList += $group.DisplayName
                } else {
                    $groupNamesList += "Unknown Group ($groupId)"
                }
            }
            $groupNames = $groupNamesList -join ", "
        }
        
        # Determine inheritance description
        if ($isDirect -and -not $groupNames) {
            $inheritence = "Direct"
        } elseif (-not $isDirect -and $groupNames) {
            $inheritence = $groupNames
        } elseif ($isDirect -and $groupNames) {
            $inheritence = "Direct, $groupNames"
        } else {
            $inheritence = "Unknown"
        }
        
        # Create the license data object
        $licenseData = [PSCustomObject]@{
            UserPrincipalName            = $user.UserPrincipalName
            DisplayName                  = $user.DisplayName
            AccountEnabled               = if ($user.AccountEnabled) { "Yes" } else { "No" }
            AssignedLicenses             = $SkuId
            AssignedLicensesFriendlyName = $friendlyName
            Inheritence                  = $inheritence
            AssignmentType               = $assignmentType
            IsDirect                     = $isDirect
            IsInherited                  = $isInherited
        }
                
        # Add to the report
        $Report += $licenseData
    }
}


# Calculate metrics for summary boxes
$script:directLicenses = ($Report | Where-Object { $_.IsDirect -eq $true -and $_.IsInherited -eq $false }).Count
$script:inheritedLicenses = ($Report | Where-Object { $_.IsInherited -eq $true -and $_.IsDirect -eq $false }).Count
$script:bothLicenses = ($Report | Where-Object { $_.IsDirect -eq $true -and $_.IsInherited -eq $true }).Count
$script:DisabledUsersWithLicenses = ($Report | Where-Object { $_.AccountEnabled -eq "No" } | Select-Object -Unique UserPrincipalName).Count

# Output summary information
Write-Host "INFO: License Summary:" -ForegroundColor Cyan
Write-Host "Total users processed: $totalUsers" -ForegroundColor White
Write-Host "Users with licenses: $($Report | Select-Object -Unique UserPrincipalName | Measure-Object | Select-Object -ExpandProperty Count)" -ForegroundColor White
Write-Host "Total license assignments: $($Report.Count)" -ForegroundColor White
Write-Host "Direct license assignments: $script:directLicenses" -ForegroundColor White
Write-Host "Inherited license assignments: $script:inheritedLicenses" -ForegroundColor White
Write-Host "Both direct and inherited: $script:bothLicenses" -ForegroundColor White
Write-Host "Disabled users with licenses: $script:DisabledUsersWithLicenses" -ForegroundColor White

# Export to HTML
new-htmlreport -Organization $Organization -Report $report -SubscriptionOverview $SubscriptionOverview

# Disconnect from Microsoft Graph
Disconnect-MgGraph | Out-Null
Write-Host "Disconnected from Microsoft Graph." -ForegroundColor Green