Generate-M365LicenseAssignmentReport.ps1

<#PSScriptInfo
 
.VERSION 2.7
 
.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".
                    - Powershell 7 as requirements have been fixed + faster
    v2.6 - Added Interactive, ClientSecret, Certificate, Identity and AccessToken parameters to support different authentication methods.
                    - Added support for sending the report via email with customizable subject and body text.
    v2.7 - Added last successful sign-in information for each user.
     
                       
.PRIVATEDATA
 
#>


<#
.DESCRIPTION
This PowerShell script generates a comprehensive Microsoft 365 license assignment report by connecting to Microsoft Graph API and analyzing license distribution across users and groups. The script examines all users in the tenant to identify how licenses are assigned - whether directly to users, inherited through group membership, or both - and compiles detailed information about each assignment including friendly license names, assignment sources, and user account status. The script creates an interactive HTML report that provides a complete overview of license utilization, subscription status with end dates, and identifies potential issues like disabled users still consuming licenses. It supports multiple authentication methods (interactive, client secret, certificate, managed identity, and access token) and can automatically email the generated report to specified recipients with the original file being cleaned up afterward.
#>
 

Param(  
    [Parameter(Mandatory = $false, ParameterSetName = "Interactive")]
    [Parameter(Mandatory = $false, ParameterSetName = "ClientSecret")]
    [Parameter(Mandatory = $false, ParameterSetName = "Certificate")]
    [Parameter(Mandatory = $false, ParameterSetName = "Identity")]
    [Parameter(Mandatory = $false, ParameterSetName = "AccessToken")]
    [string[]]$RequiredScopes = @("User.Read.All", "AuditLog.Read.All","GroupMember.Read.All", "Group.Read.All", "Directory.Read.All", "Organization.Read.All", "RoleManagement.Read.Directory", "Mail.Send"),

    
    [Parameter(Mandatory = $true, ParameterSetName = "ClientSecret")]
    [Parameter(Mandatory = $true, ParameterSetName = "Certificate")]
    [Parameter(Mandatory = $false, ParameterSetName = "Identity")]
    [Parameter(Mandatory = $true, ParameterSetName = "AccessToken")]
    [Parameter(Mandatory = $false, ParameterSetName = "Interactive")]
    [string]$TenantId,
    
    [Parameter(Mandatory = $true, ParameterSetName = "ClientSecret")]
    [Parameter(Mandatory = $true, ParameterSetName = "Certificate")]
    [Parameter(Mandatory = $false, ParameterSetName = "Interactive")]
    [string]$ClientId,
    
    [Parameter(Mandatory = $true, ParameterSetName = "ClientSecret")]
    [string]$ClientSecret,
    
    [Parameter(Mandatory = $true, ParameterSetName = "Certificate")]
    [string]$CertificateThumbprint,

    [Parameter(Mandatory = $true, ParameterSetName = "Identity")]
    [switch]$Identity,

    [Parameter(Mandatory = $true, ParameterSetName = "AccessToken")]
    [string]$AccessToken,

    [Parameter(Mandatory = $false, ParameterSetName = "Interactive")]
    [Parameter(Mandatory = $false, ParameterSetName = "ClientSecret")]
    [Parameter(Mandatory = $false, ParameterSetName = "Certificate")]
    [Parameter(Mandatory = $false, ParameterSetName = "Identity")]
    [Parameter(Mandatory = $false, ParameterSetName = "AccessToken")]
    [switch]$SendEmail,
    
    [Parameter(Mandatory = $false, ParameterSetName = "Interactive")]
    [Parameter(Mandatory = $false, ParameterSetName = "ClientSecret")]
    [Parameter(Mandatory = $false, ParameterSetName = "Certificate")]
    [Parameter(Mandatory = $false, ParameterSetName = "Identity")]
    [Parameter(Mandatory = $false, ParameterSetName = "AccessToken")]
    [string[]]$Recipient,

    [Parameter(Mandatory = $false, ParameterSetName = "Interactive")]
    [Parameter(Mandatory = $false, ParameterSetName = "ClientSecret")]
    [Parameter(Mandatory = $false, ParameterSetName = "Certificate")]
    [Parameter(Mandatory = $false, ParameterSetName = "Identity")]
    [Parameter(Mandatory = $false, ParameterSetName = "AccessToken")]
    [string]$From

)

function New-HTMLReport {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Organization,
        
        [Parameter(Mandatory = $true)]
        [array]$Report,
        
        [Parameter(Mandatory = $true)]
        [array]$SubscriptionOverview,
        
        [Parameter(Mandatory = $false)]
        [string]$script: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>Last Successful Sign In</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.LastSuccessfulSignIn)</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 $script:ExportPath -Encoding utf8

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

    # Open the HTML file
    if (-not $SendEmail) {
        Invoke-Item $script: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 Connect-ToMgGraph {
    [CmdletBinding(DefaultParameterSetName = "Interactive")]
    param (
        [Parameter(Mandatory = $false, ParameterSetName = "Interactive")]
        [Parameter(Mandatory = $false, ParameterSetName = "ClientSecret")]
        [Parameter(Mandatory = $false, ParameterSetName = "Certificate")]
        [Parameter(Mandatory = $false, ParameterSetName = "Identity")]
        [Parameter(Mandatory = $false, ParameterSetName = "AccessToken")]
        [string[]]$RequiredScopes = @("User.Read"),
        
        [Parameter(Mandatory = $true, ParameterSetName = "ClientSecret")]
        [Parameter(Mandatory = $true, ParameterSetName = "Certificate")]
        [Parameter(Mandatory = $false, ParameterSetName = "Identity")]
        [Parameter(Mandatory = $true, ParameterSetName = "AccessToken")]
        [string]$TenantId,
        
        [Parameter(Mandatory = $true, ParameterSetName = "ClientSecret")]
        [Parameter(Mandatory = $true, ParameterSetName = "Certificate")]
        [string]$ClientId,
        
        [Parameter(Mandatory = $true, ParameterSetName = "ClientSecret")]
        [string]$ClientSecret,
        
        [Parameter(Mandatory = $true, ParameterSetName = "Certificate")]
        [string]$CertificateThumbprint,

        [Parameter(Mandatory = $true, ParameterSetName = "Identity")]
        [switch]$Identity,

        [Parameter(Mandatory = $true, ParameterSetName = "AccessToken")]
        [string]$AccessToken
    )
    
    # Check if Microsoft Graph module is installed and import it
    Install-Requirements
    
    # Determine authentication method based on parameters
    $AuthMethod = $PSCmdlet.ParameterSetName
    Write-Verbose "Using authentication method: $AuthMethod"
    
    # Check if already connected
    $contextInfo = Get-MgContext -ErrorAction SilentlyContinue
    $reconnect = $false
    
    if ($contextInfo) {
        # Check if we have all the required permissions for interactive auth
        if ($AuthMethod -eq "Interactive") {
            $currentScopes = $contextInfo.Scopes
            $missingScopes = $RequiredScopes | Where-Object { $_ -notin $currentScopes }
            
            if ($missingScopes) {
                Write-Verbose "Missing required scopes: $($missingScopes -join ', ')"
                Write-Verbose "Reconnecting with all required scopes..."
                $reconnect = $true
            } else {
                Write-Verbose "Already connected to Microsoft Graph as $($contextInfo.Account) with all required scopes."
                return $contextInfo
            }
        } else {
            # For other auth methods, disconnect and reconnect with the new credentials
            Write-Verbose "Switching to $AuthMethod authentication method..."
            Disconnect-MgGraph -ErrorAction SilentlyContinue
            $reconnect = $true
        }
    } else {
        $reconnect = $true
    }
    
    if ($reconnect) {
        try {
            # Define connection parameters based on authentication method
            switch ($AuthMethod) {
                "Interactive" {
                    Write-Verbose "Connecting to Microsoft Graph using interactive authentication..."
                    Connect-MgGraph -Scopes $RequiredScopes -NoWelcome
                }
                "ClientSecret" {
                    Write-Verbose "Connecting to Microsoft Graph using client secret authentication..."
                    $secureSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force
                    $clientSecretCredential = New-Object System.Management.Automation.PSCredential($ClientId, $secureSecret)
                    Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $clientSecretCredential -NoWelcome
                }
                "Certificate" {
                    Write-Verbose "Connecting to Microsoft Graph using certificate authentication..."
                    Connect-MgGraph -TenantId $TenantId -ClientId $ClientId -CertificateThumbprint $CertificateThumbprint -NoWelcome
                }
                "Identity" {
                    Write-Verbose "Connecting to Microsoft Graph using managed identity authentication..."
                    if ($TenantId) {
                        Connect-MgGraph -Identity -TenantId $TenantId -NoWelcome
                    } else {
                        Connect-MgGraph -Identity -NoWelcome
                    }
                }
                "AccessToken" {
                    Write-Verbose "Connecting to Microsoft Graph using provided access token..."
                    Connect-MgGraph -AccessToken $AccessToken -TenantId $TenantId -NoWelcome
                }
            }
            
            # Verify connection
            $newContext = Get-MgContext
            if ($newContext) {
                $authDetails = switch ($AuthMethod) {
                    "Interactive" { "as $($newContext.Account)" }
                    "ClientSecret" { "using client secret (App ID: $ClientId)" }
                    "Certificate" { "using certificate authentication (App ID: $ClientId)" }
                    "Identity" { "using managed identity" }
                    "AccessToken" { "using provided access token" }
                }
                
                Write-Verbose "Successfully connected to Microsoft Graph $authDetails"
                return $newContext
            } else {
                throw "Connection attempt completed but unable to confirm connection"
            }
        } catch {
            Write-Error "Error connecting to Microsoft Graph: $_"
            return $null
        }
    }
    
    return $contextInfo
}
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
}

function Send-EmailWithAttachment {
    [CmdletBinding()]
    param(
        # The email addresses of the recipients. e.g. john@contoso.com
        [Parameter(Mandatory = $true, Position = 0)]
        [string[]] $Recipient,
        # HTML file path to attach to the email
        [Parameter(Mandatory = $true)]
        [string] $AttachmentPath,
        # The user id of the sender of the mail. Defaults to the current user.
        [Parameter(Mandatory = $false)]
        [string] $From
    )

    # Check Graph connection
    try {
        $contextInfo = Get-MgContext -ErrorAction Stop
        if (-not $contextInfo) {
            Write-Error "Not connected to Microsoft Graph. Please connect using Connect-MgGraph with appropriate scopes."
            return $false
        }
    } catch {
        Write-Error "Error checking Graph connection: $_"
        return $false
    }

    # Check if file exists and validate size
    if (-not (Test-Path -Path $AttachmentPath)) {
        Write-Error "Attachment file not found: $AttachmentPath"
        return $false
    }
    
    $fileInfo = Get-Item -Path $AttachmentPath
    if ($fileInfo.Length -gt 3MB) {
        Write-Error "Attachment is too large ($(($fileInfo.Length/1MB).ToString('0.00')) MB). Maximum recommended size is 3 MB."
        return $false
    }

    # Get file content as base64
    try {
        $fileName = Split-Path -Path $AttachmentPath -Leaf
        $contentBytes = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($AttachmentPath))
    } catch {
        Write-Error "Failed to read attachment file: $_"
        return $false
    }

    # Build recipients array
    $toRecipients = @()
    foreach ($email in $Recipient) {
        $toRecipients += @{
            emailAddress = @{
                address = $email
            }
        }
    }

    # Create the mail request body with proper Graph API structure
    $mailRequestBody = @{
        message = @{
            subject      = "$organization - Microsoft 365 License Assignment Report"
            toRecipients = $toRecipients
            body         = @{
                contentType = "html"
                content     = @"
    <html>
    <body style="font-family: 'Segoe UI', Arial, sans-serif; color: #333333;">
        <h2 style="color: #0078d4;">Microsoft 365 License Assignment Report</h2>
        <p>Attached is the latest Microsoft 365 license assignment report for $organization.</p>
        <p>This report includes:</p>
        <ul>
        <li>Complete overview of all license assignments across your tenant</li>
        <li>Detailed breakdown of direct and group-based license assignments</li>
        <li>Information about disabled users with licenses</li>
        <li>Subscription status and availability summary</li>
        </ul>
        <p>The HTML report can be opened in any web browser and includes filtering and export options.</p>
        <p style="color: #666666; font-style: italic;">Generated automatically - please do not reply to this email.</p>
    </body>
    </html>
"@

            }
            attachments  = @(
                @{
                    "@odata.type" = "#microsoft.graph.fileAttachment"
                    name          = $fileName
                    contentType   = "text/html"
                    contentBytes  = $contentBytes
                }
            )
        }
    }

    # Determine the endpoint URL
    $sendMailUri = "https://graph.microsoft.com/v1.0/me/sendMail"
    if ($From) {
        # Use the /users/{id}/ endpoint for other users
        $sendMailUri = "https://graph.microsoft.com/v1.0/users/$From/sendMail"
    }

    try {
        # Convert to JSON with proper depth
        $jsonBody = ConvertTo-Json -InputObject $mailRequestBody -Depth 20

        # Enable verbose output for debugging
        Write-Verbose "Request body:"
        Write-Verbose $jsonBody

        # Send the request with error details
        $response = Invoke-MgGraphRequest -Method POST -Uri $sendMailUri -Body $jsonBody -ErrorAction Stop
        Write-Host "Email sent successfully from: $from" -ForegroundColor Green
        Write-Host "Email sent successfully to: $($Recipient -join ', ')" -ForegroundColor Green
        Write-Host "Email sent successfully with attachment: $fileName" -ForegroundColor Green
        return $true
    } catch {
        $errorDetails = $_
        Write-Host "Failed to send email. Error: $($errorDetails.Exception.Message)" -ForegroundColor Red

        # Additional error details for debugging
        Write-Verbose "Error details:"
        Write-Verbose $errorDetails

        # Check if we have more detailed error information
        if ($errorDetails.ErrorDetails) {
            Write-Verbose "Error response content:"
            Write-Verbose $errorDetails.ErrorDetails.Message
        }

        return $false
    }

}

#Connect to the Graph API
Write-Verbose "Connecting to Graph API..."
try {
    # Determine which authentication method to use based on parameters
    $connectionParams = @{
        RequiredScopes = $RequiredScopes
        Verbose        = $VerbosePreference -eq 'Continue'
    }

    # Add parameters based on which ones were provided
    if ($PSCmdlet.ParameterSetName -eq "Interactive") {
        if ($TenantId) { $connectionParams.TenantId = $TenantId }
        if ($ClientId) { $connectionParams.ClientId = $ClientId }
    } elseif ($PSCmdlet.ParameterSetName -eq "ClientSecret") {
        $connectionParams.TenantId = $TenantId
        $connectionParams.ClientId = $ClientId
        $connectionParams.ClientSecret = $ClientSecret
    } elseif ($PSCmdlet.ParameterSetName -eq "Certificate") {
        $connectionParams.TenantId = $TenantId
        $connectionParams.ClientId = $ClientId
        $connectionParams.CertificateThumbprint = $CertificateThumbprint
    } elseif ($PSCmdlet.ParameterSetName -eq "Identity") {
        $connectionParams.Identity = $true
        if ($TenantId) { $connectionParams.TenantId = $TenantId }
    } elseif ($PSCmdlet.ParameterSetName -eq "AccessToken") {
        $connectionParams.AccessToken = $AccessToken
        $connectionParams.TenantId = $TenantId
    }
    
    $connected = Connect-ToMgGraph @connectionParams

    if ($connected) {
        # CODE

        # 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,signInActivity&`$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"
                }

                # Last Login Activity
                $lastSignIn = if ($user.signInActivity -and $user.signInActivity.lastsuccessfulSignInDateTime) {
                    Get-Date $user.signInActivity.lastSignInDateTime -Format "dd-MM-yyyy HH:mm"
                } else {
                    "No sign-in activity"
                }
        
                # Create the license data object
                $licenseData = [PSCustomObject]@{
                    UserPrincipalName            = $user.UserPrincipalName
                    DisplayName                  = $user.DisplayName
                    AccountEnabled               = if ($user.AccountEnabled) { "Yes" } else { "No" }
                    LastSuccessfulSignIn         = $lastSignIn
                    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 "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

        # Send email with the report
        if ($SendEmail) {
            $emailSent = Send-EmailWithAttachment -Recipient $Recipient -AttachmentPath $script:ExportPath -From $From

            if ($emailSent) {
                Write-Host "INFO: Email sent successfully." -ForegroundColor Green
            } else {
                Write-Host "ERROR: Failed to send email." -ForegroundColor Red
            }
        } else {
            Write-Host "INFO: Email sending is disabled. Set -SendEmail to $true to enable." -ForegroundColor Yellow
        }

        # Clean up the report file
        if ($SendEmail) {
            if (Test-Path -Path $script:ExportPath) {
                Remove-Item -Path $script:ExportPath -Force
                Write-Host "INFO: Temporary report file deleted." -ForegroundColor Green
            } else {
                Write-Host "INFO: No temporary report file found to delete." -ForegroundColor Yellow
            }
        }

    } else {
        throw "Failed to connect to Microsoft Graph API."
    }
} catch { 
    Write-Error "Error: $_"
    throw $_
} finally {
    # Disconnect from Microsoft Graph if connected
    $contextInfo = Get-MgContext -ErrorAction SilentlyContinue
    if ($contextInfo) {
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null 
        Write-Host "Disconnected from Microsoft Graph." -ForegroundColor Green
    } else {
        Write-Host "No active connection to disconnect." -ForegroundColor Yellow
    }
}