Private/ReportHelper.ps1

<#
.SYNOPSIS
    Report formatting and generation helper functions.

.DESCRIPTION
    This file contains functions for formatting DLP policy data into various
    output formats (HTML, Markdown) including SIT configurations, advanced rules,
    location scopes, and policy statistics.

.NOTES
    Internal module file - not exported to users.
    Functions are available to all module cmdlets.
#>


function Get-PolicyStatistics {
    <#
    .SYNOPSIS
        Calculates statistics from an array of DLP policies.
    
    .DESCRIPTION
        Analyzes DLP policy data and returns comprehensive statistics including
        policy counts, rule counts, mode distribution, and workload distribution.
    
    .PARAMETER Policies
        Array of policy objects (from backup JSON or Get-DlpCompliancePolicy).
    
    .OUTPUTS
        System.Collections.Hashtable
        Returns hashtable with statistics: TotalPolicies, EnabledPolicies,
        DisabledPolicies, TotalRules, TestMode, EnforceMode, Workloads.
    
    .EXAMPLE
        $stats = Get-PolicyStatistics -Policies $policies
        Write-Host "Total: $($stats.TotalPolicies), Enabled: $($stats.EnabledPolicies)"
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param(
        [Parameter(Mandatory = $true)]
        [array]$Policies
    )
    
    $stats = @{
        TotalPolicies = $Policies.Count
        EnabledPolicies = ($Policies | Where-Object { $_.Enabled -eq $true }).Count
        DisabledPolicies = ($Policies | Where-Object { $_.Enabled -eq $false }).Count
        TotalRules = ($Policies | ForEach-Object { $_.Rules.Count } | Measure-Object -Sum).Sum
        TestMode = ($Policies | Where-Object { $_.Mode -like "*Test*" }).Count
        EnforceMode = ($Policies | Where-Object { $_.Mode -eq "Enforce" }).Count
        Workloads = @{}
    }
    
    # Count workload distribution
    foreach ($policy in $Policies) {
        if ($policy.Workload) {
            $workloads = $policy.Workload -split ", " | ForEach-Object { $_.Trim() }
            foreach ($workload in $workloads) {
                if ($stats.Workloads.ContainsKey($workload)) {
                    $stats.Workloads[$workload]++
                }
                else {
                    $stats.Workloads[$workload] = 1
                }
            }
        }
    }
    
    Write-Verbose "Calculated statistics: $($stats.TotalPolicies) policies, $($stats.TotalRules) rules"
    return $stats
}

function Format-SITConfiguration {
    <#
    .SYNOPSIS
        Formats Sensitive Information Type (SIT) configuration for display.
    
    .DESCRIPTION
        Parses and formats SIT JSON configuration into human-readable format.
        Supports both grouped and simple array formats. Handles confidence levels,
        count ranges, and classifier types.
    
    .PARAMETER SITJson
        The JSON string containing SIT configuration.
    
    .PARAMETER Format
        Output format: "Markdown" or "HTML".
    
    .OUTPUTS
        System.String
        Returns formatted SIT configuration string.
    
    .EXAMPLE
        $formatted = Format-SITConfiguration -SITJson $rule.ContentContainsSensitiveInformation -Format "HTML"
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $false)]
        [string]$SITJson,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('Markdown', 'HTML')]
        [string]$Format
    )
    
    if (-not $SITJson) { return "" }
    
    try {
        $sitData = $SITJson | ConvertFrom-Json
        
        if ($sitData.groups) {
            # Complex grouped SIT configuration
            if ($Format -eq "Markdown") {
                $topOp = if ($sitData.operator) { $sitData.operator } else { "(not specified)" }
                $output = "`n**Operator:** $topOp`n`n"
                foreach ($group in $sitData.groups) {
                    $groupName = if ($group.name) { $group.name } else { "(unnamed)" }
                    $groupOp = if ($group.operator) { $group.operator } else { "(not specified)" }
                    $output += "**Group: $groupName** (Operator: $groupOp)`n`n"
                    foreach ($sit in $group.sensitivetypes) {
                        $output += "- **$($sit.name)**`n"
                        if ($sit.confidencelevel) { $output += " - Confidence: $($sit.confidencelevel)`n" }
                        if ($sit.mincount) { $output += " - Count: $($sit.mincount) to $($sit.maxcount)`n" }
                        if ($sit.classifiertype -and $sit.classifiertype -ne 'Content') { $output += " - Type: $($sit.classifiertype)`n" }
                    }
                    $output += "`n"
                }
                return $output
            }
            else {
                # HTML format
                $topOp = if ($sitData.operator) { $sitData.operator } else { "<em>(not specified)</em>" }
                $output = "<div class='sit-config'><strong>Operator:</strong> $topOp<br>"
                foreach ($group in $sitData.groups) {
                    $groupName = if ($group.name) { $group.name } else { "<em>(unnamed)</em>" }
                    $groupOp = if ($group.operator) { $group.operator } else { "<em>(not specified)</em>" }
                    $output += "<div class='sit-group'><strong>Group: $groupName</strong> (Operator: $groupOp)<ul>"
                    foreach ($sit in $group.sensitivetypes) {
                        $output += "<li><strong>$($sit.name)</strong>"
                        if ($sit.confidencelevel -or $sit.mincount -or ($sit.classifiertype -and $sit.classifiertype -ne 'Content')) {
                            $output += "<ul>"
                            if ($sit.confidencelevel) { $output += "<li>Confidence: $($sit.confidencelevel)</li>" }
                            if ($sit.mincount) { $output += "<li>Count: $($sit.mincount) to $($sit.maxcount)</li>" }
                            if ($sit.classifiertype -and $sit.classifiertype -ne 'Content') { $output += "<li>Type: $($sit.classifiertype)</li>" }
                            $output += "</ul>"
                        }
                        $output += "</li>"
                    }
                    $output += "</ul></div>"
                }
                $output += "</div>"
                return $output
            }
        }
        elseif ($sitData -is [array]) {
            # Simple array format
            if ($Format -eq "Markdown") {
                $output = "`n"
                foreach ($sit in $sitData) {
                    $output += "- **$($sit.name)**`n"
                    if ($sit.confidencelevel) { $output += " - Confidence: $($sit.confidencelevel)`n" }
                    if ($sit.mincount) { $output += " - Count: $($sit.mincount) to $($sit.maxcount)`n" }
                    if ($sit.classifiertype) { $output += " - Type: $($sit.classifiertype)`n" }
                }
                return $output
            }
            else {
                $output = "<ul>"
                foreach ($sit in $sitData) {
                    $output += "<li><strong>$($sit.name)</strong>"
                    if ($sit.confidencelevel -or $sit.mincount -or $sit.classifiertype) {
                        $output += "<ul>"
                        if ($sit.confidencelevel) { $output += "<li>Confidence: $($sit.confidencelevel)</li>" }
                        if ($sit.mincount) { $output += "<li>Count: $($sit.mincount) to $($sit.maxcount)</li>" }
                        if ($sit.classifiertype) { $output += "<li>Type: $($sit.classifiertype)</li>" }
                        $output += "</ul>"
                    }
                    $output += "</li>"
                }
                $output += "</ul>"
                return $output
            }
        }
    }
    catch {
        Write-Warning "Error parsing SIT configuration: $($_.Exception.Message)"
        return "Error parsing SIT configuration"
    }
    
    return ""
}

function Format-AdvancedRule {
    <#
    .SYNOPSIS
        Formats Advanced Rule configuration for display.
    
    .DESCRIPTION
        Parses and formats Advanced Rule JSON configuration into human-readable format.
        Recursively handles nested conditions and operators.
    
    .PARAMETER AdvancedRuleJson
        The JSON string containing Advanced Rule configuration.
    
    .PARAMETER Format
        Output format: "Markdown", "HTML", or "Plain" (for CSV).
    
    .OUTPUTS
        System.String
        Returns formatted Advanced Rule configuration string.
    
    .EXAMPLE
        $formatted = Format-AdvancedRule -AdvancedRuleJson $rule.AdvancedRule -Format "HTML"
    
    .EXAMPLE
        $formatted = Format-AdvancedRule -AdvancedRuleJson $rule.AdvancedRule -Format "Plain"
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $false)]
        [string]$AdvancedRuleJson,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('Markdown', 'HTML', 'Plain')]
        [string]$Format
    )
    
    if (-not $AdvancedRuleJson) { return "" }
    
    try {
        $advancedRule = $AdvancedRuleJson | ConvertFrom-Json
        
        if ($Format -eq "Markdown") {
            $output = "`n**Advanced Rule Configuration**`n`n"
            $output += "**Version:** $($advancedRule.Version)`n`n"
            
            if ($advancedRule.Condition) {
                $output += "**Conditions:**`n`n"
                $output += Format-AdvancedRuleCondition -Condition $advancedRule.Condition -Format "Markdown" -Indent 0
            }
            
            return $output
        }
        elseif ($Format -eq "Plain") {
            # Plain text format for CSV
            $output = "Advanced Rule (v$($advancedRule.Version)): "
            
            if ($advancedRule.Condition) {
                $output += Format-AdvancedRuleCondition -Condition $advancedRule.Condition -Format "Plain" -Indent 0
            }
            
            return $output
        }
        else {
            # HTML format
            $output = "<div class='advanced-rule'>"
            $output += "<strong>Advanced Rule Configuration</strong> (Version: $($advancedRule.Version))<br>"
            
            if ($advancedRule.Condition) {
                $output += "<div class='advanced-rule-conditions'>"
                $output += Format-AdvancedRuleCondition -Condition $advancedRule.Condition -Format "HTML" -Indent 0
                $output += "</div>"
            }
            
            $output += "</div>"
            return $output
        }
    }
    catch {
        Write-Warning "Error parsing Advanced Rule: $($_.Exception.Message)"
        return "Error parsing Advanced Rule: $($_.Exception.Message)"
    }
}

function Format-AdvancedRuleCondition {
    <#
    .SYNOPSIS
        Recursively formats Advanced Rule conditions.
    
    .DESCRIPTION
        Internal helper function for Format-AdvancedRule that recursively processes
        nested conditions and operators in Advanced Rule configurations.
    
    .PARAMETER Condition
        The condition object to format.
    
    .PARAMETER Format
        Output format: "Markdown", "HTML", or "Plain".
    
    .PARAMETER Indent
        Indentation level for nested conditions.
    
    .OUTPUTS
        System.String
        Returns formatted condition string.
    
    .EXAMPLE
        $formatted = Format-AdvancedRuleCondition -Condition $cond -Format "HTML" -Indent 1
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true)]
        [object]$Condition,
        
        [Parameter(Mandatory = $true)]
        [ValidateSet('Markdown', 'HTML', 'Plain')]
        [string]$Format,
        
        [Parameter(Mandatory = $false)]
        [int]$Indent = 0
    )
    
    $indentStr = " " * $Indent
    $output = ""
    
    if ($Format -eq "Markdown") {
        if ($Condition.Operator) {
            $output += "$indentStr**Operator:** $($Condition.Operator)`n"
        }
        
        if ($Condition.SubConditions) {
            foreach ($subCond in $Condition.SubConditions) {
                if ($subCond.ConditionName) {
                    $output += "$indentStr- **$($subCond.ConditionName)**`n"
                    
                    if ($subCond.Value) {
                        if ($subCond.ConditionName -eq "ContentContainsSensitiveInformation") {
                            foreach ($sitGroup in $subCond.Value) {
                                if ($sitGroup.groups) {
                                    foreach ($group in $sitGroup.groups) {
                                        $output += "$indentStr - Group: $($group.name) (Operator: $($group.Operator))`n"
                                        if ($group.sensitivetypes) {
                                            foreach ($sit in $group.sensitivetypes) {
                                                $output += "$indentStr - **$($sit.name)**"
                                                if ($sit.confidencelevel) { $output += " (Confidence: $($sit.confidencelevel))" }
                                                $output += "`n"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        else {
                            $output += "$indentStr - Value: $($subCond.Value -join ', ')`n"
                        }
                    }
                }
                elseif ($subCond.Operator) {
                    $output += Format-AdvancedRuleCondition -Condition $subCond -Format $Format -Indent ($Indent + 1)
                }
            }
        }
    }
    elseif ($Format -eq "Plain") {
        # Plain text format for CSV - compact and readable
        if ($Condition.Operator) {
            $output += "$($Condition.Operator) ["
        }
        
        if ($Condition.SubConditions) {
            $conditions = @()
            foreach ($subCond in $Condition.SubConditions) {
                if ($subCond.ConditionName) {
                    $condText = $subCond.ConditionName
                    
                    if ($subCond.Value) {
                        if ($subCond.ConditionName -eq "ContentContainsSensitiveInformation") {
                            # Handle SITs - extract just the names
                            $sitNames = @()
                            foreach ($sitGroup in $subCond.Value) {
                                if ($sitGroup.groups) {
                                    foreach ($group in $sitGroup.groups) {
                                        if ($group.sensitivetypes) {
                                            $sitNames += $group.sensitivetypes | ForEach-Object { $_.name }
                                        }
                                    }
                                }
                            }
                            if ($sitNames.Count -gt 0) {
                                $condText += ": " + ($sitNames -join ", ")
                            }
                        }
                        else {
                            $condText += ": " + ($subCond.Value -join ", ")
                        }
                    }
                    $conditions += $condText
                }
                elseif ($subCond.Operator) {
                    $conditions += Format-AdvancedRuleCondition -Condition $subCond -Format $Format -Indent ($Indent + 1)
                }
            }
            $output += $conditions -join "; "
        }
        
        if ($Condition.Operator) {
            $output += "]"
        }
    }
    else {
        # HTML format
        if ($Condition.Operator) {
            $output += "<div style='margin-left: ${Indent}em;'><strong>Operator:</strong> $($Condition.Operator)</div>"
        }
        
        if ($Condition.SubConditions) {
            $output += "<ul style='margin-left: ${Indent}em;'>"
            foreach ($subCond in $Condition.SubConditions) {
                if ($subCond.ConditionName) {
                    $output += "<li><strong>$($subCond.ConditionName)</strong>"
                    
                    if ($subCond.Value) {
                        if ($subCond.ConditionName -eq "ContentContainsSensitiveInformation") {
                            # Handle SITs within advanced rules - can be grouped or flat array
                            $output += "<ul>"
                            
                            # Check if first item has 'groups' property (grouped format)
                            if ($subCond.Value[0].groups) {
                                foreach ($sitGroup in $subCond.Value) {
                                    foreach ($group in $sitGroup.groups) {
                                        $groupName = if ($group.name) { $group.name } else { "(unnamed)" }
                                        $groupOp = if ($group.Operator) { $group.Operator } else { "(not specified)" }
                                        $output += "<li>Group: <strong>$groupName</strong> (Operator: $groupOp)<ul>"
                                        if ($group.sensitivetypes) {
                                            foreach ($sit in $group.sensitivetypes) {
                                                $output += "<li><strong>$($sit.name)</strong>"
                                                if ($sit.confidencelevel -or $sit.mincount) {
                                                    $output += "<ul>"
                                                    if ($sit.confidencelevel) { $output += "<li>Confidence: $($sit.confidencelevel)</li>" }
                                                    if ($sit.mincount) { $output += "<li>Count: $($sit.mincount) to $($sit.maxcount)</li>" }
                                                    $output += "</ul>"
                                                }
                                                $output += "</li>"
                                            }
                                        }
                                        $output += "</ul></li>"
                                    }
                                }
                            }
                            else {
                                # Flat array of SITs
                                foreach ($sit in $subCond.Value) {
                                    $output += "<li><strong>$($sit.name)</strong>"
                                    if ($sit.confidencelevel -or $sit.mincount) {
                                        $output += "<ul>"
                                        if ($sit.confidencelevel) { $output += "<li>Confidence: $($sit.confidencelevel)</li>" }
                                        if ($sit.mincount) { $output += "<li>Count: $($sit.mincount) to $($sit.maxcount)</li>" }
                                        $output += "</ul>"
                                    }
                                    $output += "</li>"
                                }
                            }
                            $output += "</ul>"
                        }
                        elseif ($subCond.ConditionName -eq "ContentFileTypeMatches") {
                            # Resolve file type GUIDs
                            $fileTypes = @{
                                "29b89383-a6f8-47ad-b594-3b364698b921" = "Word Document (.docx)"
                                "abae71fd-17b1-4716-963f-e0cdbe8ddf9b" = "Excel Workbook (.xlsx)"
                                "31ed09c0-1da5-4b7f-a23f-4f8b5bae839f" = "PowerPoint Presentation (.pptx)"
                                "d6ea7928-706f-4c8a-9a1f-53926659dd5c" = "PDF Document (.pdf)"
                                "6256dc0f-9add-4dd2-8798-5139664ed8dc" = "Text File (.txt)"
                            }
                            $resolved = $subCond.Value | ForEach-Object {
                                $guid = $_.ToString()
                                if ($fileTypes.ContainsKey($guid)) { $fileTypes[$guid] } else { $guid }
                            }
                            $output += ": $($resolved -join ', ')"
                        }
                        elseif ($subCond.ConditionName -eq "SharedByIRMUserRisk") {
                            # Resolve user risk GUIDs
                            $userRisks = @{
                                "FCB9FA93-6269-4ACF-A756-832E79B36A2A" = "Elevated Risk"
                                "797C4446-5C73-484F-8E58-0CCA08D6DF6C" = "Moderate Risk"
                                "75A4318B-94A2-4323-BA42-2CA6DB29AAFE" = "Minor Risk"
                            }
                            $resolved = $subCond.Value | ForEach-Object {
                                $guid = $_.ToString()
                                if ($userRisks.ContainsKey($guid)) { $userRisks[$guid] } else { $guid }
                            }
                            $output += ": $($resolved -join ', ')"
                        }
                        else {
                            $output += ": $($subCond.Value -join ', ')"
                        }
                    }
                    $output += "</li>"
                }
                elseif ($subCond.Operator) {
                    $output += "<li>"
                    $output += Format-AdvancedRuleCondition -Condition $subCond -Format $Format -Indent ($Indent + 1)
                    $output += "</li>"
                }
            }
            $output += "</ul>"
        }
    }
    
    return $output
}

function Get-LocationScopeHTML {
    <#
    .SYNOPSIS
        Generates HTML representation of location scope information.
    
    .DESCRIPTION
        Creates formatted HTML showing workload location configurations including
        whether each workload is set to "All", specific targeting, or not configured.
    
    .PARAMETER Policy
        The policy object containing location information.
    
    .OUTPUTS
        System.String
        Returns HTML string with location scope information.
    
    .EXAMPLE
        $html = Get-LocationScopeHTML -Policy $policy
    
    .NOTES
        This function uses Get-LocationScopeInfo from LocationScope.ps1 to
        accurately determine location scopes.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true)]
        [object]$Policy
    )
    
    $html = "<div class='location-scope'><table class='location-table'>"
    $html += "<tr><th>Workload</th><th>Scope</th><th>Details</th></tr>"
    
    # Exchange
    $exchangeScope = Get-LocationScopeInfo -LocationArray $Policy.ExchangeLocation `
        -LocationName "Exchange" `
        -ScopingProperties @($Policy.ExchangeSender, $Policy.ExchangeSenderMemberOf) `
        -AdaptiveScopeProperties @($Policy.ExchangeAdaptiveScopes)
    
    if ($exchangeScope.IsConfigured) {
        $scopeClass = if ($exchangeScope.IsAll) { "scope-all" } else { "scope-specific" }
        $scopeText = if ($exchangeScope.IsAll) { "All" } else { "Specific ($($exchangeScope.Count))" }
        $html += "<tr><td>Exchange</td><td class='$scopeClass'>$scopeText</td><td>$($exchangeScope.Locations)</td></tr>"
    }
    
    # SharePoint
    $sharepointScope = Get-LocationScopeInfo -LocationArray $Policy.SharePointLocation `
        -LocationName "SharePoint" `
        -AdaptiveScopeProperties @($Policy.SharePointAdaptiveScopes)
    
    if ($sharepointScope.IsConfigured) {
        $scopeClass = if ($sharepointScope.IsAll) { "scope-all" } else { "scope-specific" }
        $scopeText = if ($sharepointScope.IsAll) { "All" } else { "Specific ($($sharepointScope.Count))" }
        $html += "<tr><td>SharePoint</td><td class='$scopeClass'>$scopeText</td><td>$($sharepointScope.Locations)</td></tr>"
    }
    
    # OneDrive
    $onedriveScope = Get-LocationScopeInfo -LocationArray $Policy.OneDriveLocation `
        -LocationName "OneDrive" `
        -ScopingProperties @($Policy.OneDriveSharedBy, $Policy.OneDriveSharedByMemberOf) `
        -AdaptiveScopeProperties @($Policy.OneDriveAdaptiveScopes)
    
    if ($onedriveScope.IsConfigured) {
        $scopeClass = if ($onedriveScope.IsAll) { "scope-all" } else { "scope-specific" }
        $scopeText = if ($onedriveScope.IsAll) { "All" } else { "Specific ($($onedriveScope.Count))" }
        $html += "<tr><td>OneDrive</td><td class='$scopeClass'>$scopeText</td><td>$($onedriveScope.Locations)</td></tr>"
    }
    
    # Teams
    $teamsScope = Get-LocationScopeInfo -LocationArray $Policy.TeamsLocation `
        -LocationName "Teams" `
        -ScopingProperties @($Policy.TeamsSender, $Policy.TeamsSenderMemberOf) `
        -AdaptiveScopeProperties @($Policy.TeamsAdaptiveScopes)
    
    if ($teamsScope.IsConfigured) {
        $scopeClass = if ($teamsScope.IsAll) { "scope-all" } else { "scope-specific" }
        $scopeText = if ($teamsScope.IsAll) { "All" } else { "Specific ($($teamsScope.Count))" }
        $html += "<tr><td>Teams</td><td class='$scopeClass'>$scopeText</td><td>$($teamsScope.Locations)</td></tr>"
    }
    
    $html += "</table></div>"
    return $html
}

function Build-ReportHTML {
    <#
    .SYNOPSIS
        Builds a complete HTML report from DLP policy data.
    
    .DESCRIPTION
        Generates a comprehensive, interactive HTML report showing all DLP policies,
        rules, and configurations with collapsible sections and color-coded indicators.
    
    .PARAMETER Policies
        Array of policy objects to include in the report.
    
    .PARAMETER Statistics
        Hashtable containing policy statistics (from Get-PolicyStatistics).
    
    .PARAMETER ReportTitle
        Title to display at the top of the report.
        Default: "Microsoft Purview DLP Policy Report"
    
    .OUTPUTS
        System.String
        Returns complete HTML document as a string.
    
    .EXAMPLE
        $html = Build-ReportHTML -Policies $policies -Statistics $stats
        $html | Out-File -FilePath "report.html" -Encoding UTF8
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory = $true)]
        [array]$Policies,
        
        [Parameter(Mandatory = $true)]
        [hashtable]$Statistics,
        
        [Parameter(Mandatory = $false)]
        [string]$ReportTitle = "Microsoft Purview DLP Policy Report"
    )
    
    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    
    # Build HTML header with styles
    $html = @"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>$ReportTitle</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            padding: 20px;
            color: #333;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 12px;
            box-shadow: 0 10px 40px rgba(0,0,0,0.2);
            overflow: hidden;
        }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }
        .header h1 {
            font-size: 32px;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
        }
        .header .timestamp {
            font-size: 14px;
            opacity: 0.9;
        }
        .summary {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 20px;
            padding: 30px;
            background: #f8f9fa;
            border-bottom: 1px solid #dee2e6;
        }
        .stat-card {
            background: white;
            padding: 20px;
            border-radius: 8px;
            border-left: 4px solid #667eea;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .stat-card .label {
            font-size: 12px;
            color: #6c757d;
            text-transform: uppercase;
            font-weight: 600;
            margin-bottom: 8px;
        }
        .stat-card .value {
            font-size: 28px;
            font-weight: bold;
            color: #667eea;
        }
        .content {
            padding: 30px;
        }
        .policy {
            background: white;
            border: 1px solid #dee2e6;
            border-radius: 8px;
            margin-bottom: 20px;
            overflow: hidden;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
        }
        .policy-header {
            background: #f8f9fa;
            padding: 20px;
            cursor: pointer;
            border-bottom: 1px solid #dee2e6;
            transition: background 0.2s;
        }
        .policy-header:hover {
            background: #e9ecef;
        }
        .policy-name {
            font-size: 20px;
            font-weight: bold;
            color: #212529;
            margin-bottom: 8px;
        }
        .policy-meta {
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
            font-size: 13px;
            color: #6c757d;
        }
        .badge {
            display: inline-block;
            padding: 4px 10px;
            border-radius: 4px;
            font-size: 11px;
            font-weight: 600;
            text-transform: uppercase;
        }
        .badge-enforce { background: #d4edda; color: #155724; }
        .badge-test { background: #fff3cd; color: #856404; }
        .badge-disabled { background: #f8d7da; color: #721c24; }
        .badge-enabled { background: #d4edda; color: #155724; }
        .badge-portable { background: #d1ecf1; color: #0c5460; }
        .badge-not-portable { background: #f8d7da; color: #721c24; }
        .policy.disabled .policy-header {
            background: #f1f1f1;
            border-left: 5px solid #6c757d;
        }
        .policy.disabled .policy-name {
            color: #6c757d;
        }
        .policy-body {
            padding: 20px;
            display: none;
            background: #fff;
        }
        .policy-body.expanded {
            display: block;
        }
        .info-section {
            margin-bottom: 25px;
        }
        .info-section h3 {
            font-size: 16px;
            color: #495057;
            margin-bottom: 12px;
            padding-bottom: 8px;
            border-bottom: 2px solid #667eea;
        }
        .info-grid {
            display: grid;
            grid-template-columns: 200px 1fr;
            gap: 10px;
            font-size: 14px;
        }
        .info-label {
            font-weight: 600;
            color: #6c757d;
        }
        .info-value {
            color: #212529;
        }
        .rule {
            background: #f8f9fa;
            border: 1px solid #dee2e6;
            border-radius: 6px;
            padding: 15px;
            margin-bottom: 15px;
        }
        .rule.disabled {
            background: #fff3cd;
            border-left: 4px solid #856404;
        }
        .rule-name {
            font-size: 16px;
            font-weight: bold;
            color: #495057;
            margin-bottom: 10px;
        }
        .rule-details {
            font-size: 13px;
            line-height: 1.8;
        }
        .sit-section {
            background: #fff9e6;
            border: 1px solid #ffe066;
            border-radius: 5px;
            padding: 15px;
            margin-top: 15px;
        }
        .sit-section h4 {
            color: #cc8800;
            margin-bottom: 10px;
            font-size: 14px;
        }
        .sit-config {
            font-size: 13px;
        }
        .sit-group {
            margin: 10px 0;
            padding-left: 15px;
        }
        .advanced-rule {
            background: #e7f3ff;
            border-left: 4px solid #0078D4;
            padding: 15px;
            margin-top: 15px;
            font-size: 13px;
            border-radius: 5px;
        }
        .advanced-rule strong {
            color: #0078D4;
        }
        .advanced-rule-conditions ul {
            margin-top: 10px;
            margin-bottom: 10px;
        }
        .advanced-rule-conditions li {
            margin: 5px 0;
        }
        .code {
            font-family: 'Courier New', monospace;
            background: #e9ecef;
            padding: 2px 6px;
            border-radius: 3px;
            font-size: 12px;
        }
        .toggle-icon {
            float: right;
            font-size: 18px;
            transition: transform 0.2s;
        }
        .toggle-icon.expanded {
            transform: rotate(180deg);
        }
        .scope-all { color: #28a745; font-weight: bold; }
        .scope-specific { color: #ffc107; font-weight: bold; }
        table {
            width: 100%;
            border-collapse: collapse;
            font-size: 13px;
            margin-top: 10px;
        }
        table th {
            background: #e9ecef;
            padding: 10px;
            text-align: left;
            font-weight: 600;
            color: #495057;
        }
        table td {
            padding: 10px;
            border-bottom: 1px solid #dee2e6;
        }
        .footer {
            background: #f8f9fa;
            padding: 20px;
            text-align: center;
            font-size: 12px;
            color: #6c757d;
            border-top: 1px solid #dee2e6;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>$ReportTitle</h1>
            <div class="timestamp">Generated: $timestamp</div>
        </div>
        
        <div class="summary">
            <div class="stat-card">
                <div class="label">Total Policies</div>
                <div class="value">$($Statistics.TotalPolicies)</div>
            </div>
            <div class="stat-card">
                <div class="label">Enabled</div>
                <div class="value">$($Statistics.EnabledPolicies)</div>
            </div>
            <div class="stat-card">
                <div class="label">Disabled</div>
                <div class="value">$($Statistics.DisabledPolicies)</div>
            </div>
            <div class="stat-card">
                <div class="label">Total Rules</div>
                <div class="value">$($Statistics.TotalRules)</div>
            </div>
            <div class="stat-card">
                <div class="label">Enforce Mode</div>
                <div class="value">$($Statistics.EnforceMode)</div>
            </div>
            <div class="stat-card">
                <div class="label">Test Mode</div>
                <div class="value">$($Statistics.TestMode)</div>
            </div>
        </div>
        
        <div class="content">
            <h2 style="margin-bottom: 20px; color: #495057;">Policy Details</h2>
"@

    
    # Add each policy
    $policyIndex = 0
    foreach ($policy in $Policies) {
        $policyIndex++
        
        # Determine badges
        $modeBadge = switch ($policy.Mode) {
            "Enforce" { "badge-enforce" }
            { $_ -like "*Test*" } { "badge-test" }
            default { "badge-test" }
        }
        
        $enabledBadge = if ($policy.Enabled) { "badge-enabled" } else { "badge-disabled" }
        $enabledText = if ($policy.Enabled) { "Enabled" } else { "Disabled" }
        
        $portableBadge = if ($policy.IsCrossTenantPortable) { "badge-portable" } else { "badge-not-portable" }
        $portableText = if ($policy.IsCrossTenantPortable) { "Portable" } else { "Not Portable" }
        
        $disabledClass = if (-not $policy.Enabled) { " disabled" } else { "" }
        
        # Determine display name for policy
        $policyDisplayText = if ($policy.PolicyDisplayName -and $policy.PolicyDisplayName -ne $policy.PolicyName) {
            "$($policy.PolicyDisplayName) <span style='color:#888;font-size:0.85em;'>(Internal: $($policy.PolicyName))</span>"
        } else {
            $policy.PolicyName
        }
        
        $html += @"
            <div class="policy$disabledClass">
                <div class="policy-header" onclick="togglePolicy($policyIndex)">
                    <div class="policy-name">
                        $policyDisplayText
                        <span class="toggle-icon" id="icon-$policyIndex">▼</span>
                    </div>
                    <div class="policy-meta">
                        <span class="badge $modeBadge">$($policy.Mode)</span>
                        <span class="badge $enabledBadge">$enabledText</span>
                        <span class="badge $portableBadge">$portableText</span>
                        <span>Rules: $($policy.RuleCount)</span>
                        <span>Priority: $($policy.Priority)</span>
                    </div>
                </div>
                <div class="policy-body" id="policy-$policyIndex">
                    <div class="info-section">
                        <h3>Policy Information</h3>
                        <div class="info-grid">
                            <div class="info-label">Policy ID:</div>
                            <div class="info-value code">$($policy.PolicyId)</div>
                            <div class="info-label">Internal Name:</div>
                            <div class="info-value code">$($policy.PolicyName)</div>
                            <div class="info-label">Display Name:</div>
                            <div class="info-value">$(if ($policy.PolicyDisplayName) { $policy.PolicyDisplayName } else { '<i>Same as internal name</i>' })</div>
                            <div class="info-label">Created By:</div>
                            <div class="info-value">$($policy.CreatedBy)</div>
                            <div class="info-label">Created:</div>
                            <div class="info-value">$($policy.CreatedDateTime)</div>
                            <div class="info-label">Modified By:</div>
                            <div class="info-value">$($policy.ModifiedBy)</div>
                            <div class="info-label">Modified:</div>
                            <div class="info-value">$($policy.ModifiedDateTime)</div>
                            <div class="info-label">Distribution:</div>
                            <div class="info-value">$($policy.DistributionStatus)</div>
                        </div>
"@

        
        if ($policy.Comment) {
            $html += @"
                        <div style="margin-top: 10px;">
                            <strong>Comment:</strong> $($policy.Comment)
                        </div>
"@

        }
        
        $html += "</div>"
        
        # Location Scopes
        $html += @"
                    <div class="info-section">
                        <h3>Location Scopes</h3>
                        <table>
                            <thead>
                                <tr>
                                    <th>Workload</th>
                                    <th>Scope</th>
                                    <th>Details</th>
                                </tr>
                            </thead>
                            <tbody>
"@

        
        # Add location rows
        $locations = @(
            @{ Name = "Exchange"; Location = $policy.ExchangeLocation; IsAll = $policy.ExchangeLocationIsAll; Exception = $policy.ExchangeLocationException }
            @{ Name = "SharePoint"; Location = $policy.SharePointLocation; IsAll = $policy.SharePointLocationIsAll; Exception = $policy.SharePointLocationException }
            @{ Name = "OneDrive"; Location = $policy.OneDriveLocation; IsAll = $policy.OneDriveLocationIsAll; Exception = $policy.OneDriveLocationException }
            @{ Name = "Teams"; Location = $policy.TeamsLocation; IsAll = $policy.TeamsLocationIsAll; Exception = $policy.TeamsLocationException }
            @{ Name = "Endpoint"; Location = $policy.EndpointDlpLocation; IsAll = $policy.EndpointDlpLocationIsAll; Exception = $null }
        )
        
        foreach ($loc in $locations) {
            if ($loc.Location) {
                $scopeClass = if ($loc.IsAll) { "scope-all" } else { "scope-specific" }
                $scopeText = if ($loc.IsAll) { "All" } else { "Specific" }
                $details = if ($loc.IsAll) { "All $($loc.Name)" } else { $loc.Location }
                
                $html += "<tr><td>$($loc.Name)</td><td class='$scopeClass'>$scopeText</td><td>$details</td></tr>"
                
                if ($loc.Exception) {
                    $html += "<tr><td></td><td colspan='2' style='color: #dc3545;'>Exceptions: $($loc.Exception)</td></tr>"
                }
            }
        }
        
        $html += @"
                            </tbody>
                        </table>
                    </div>
"@

        
        # Rules
        if ($policy.Rules -and $policy.Rules.Count -gt 0) {
            $html += @"
                    <div class="info-section">
                        <h3>Rules ($($policy.Rules.Count))</h3>
"@

            
            foreach ($rule in $policy.Rules) {
                $ruleDisabled = if ($rule.Disabled) { " (Disabled)" } else { "" }
                $ruleDisabledClass = if ($rule.Disabled) { " disabled" } else { "" }
                
                # Determine display name for rule
                $ruleDisplayText = if ($rule.RuleDisplayName -and $rule.RuleDisplayName -ne $rule.RuleName) {
                    "$($rule.RuleDisplayName) <span style='color:#888;font-size:0.85em;'>(Internal: $($rule.RuleName))</span>"
                } else {
                    $rule.RuleName
                }
                
                $html += @"
                        <div class="rule$ruleDisabledClass">
                            <div class="rule-name">$ruleDisplayText$ruleDisabled</div>
                            <div class="rule-details">
"@

                
                if ($rule.Priority) {
                    $html += "<div><strong>Priority:</strong> $($rule.Priority)</div>"
                }
                
                # Show AdvancedRule if it exists (has complete data including confidence/counts)
                # Otherwise fall back to ContentContainsSensitiveInformation
                if ($rule.AdvancedRule) {
                    $advHtml = Format-AdvancedRule -AdvancedRuleJson $rule.AdvancedRule -Format "HTML"
                    $html += @"
<div class="advanced-rule">
    $advHtml
</div>
"@

                }
                elseif ($rule.ContentContainsSensitiveInformation) {
                    # Only show this if no AdvancedRule (to avoid duplication)
                    $sitHtml = Format-SITConfiguration -SITJson $rule.ContentContainsSensitiveInformation -Format "HTML"
                    $html += @"
<div class="sit-section">
    <h4>Sensitive Information Types</h4>
    $sitHtml
</div>
"@

                }
                
                if ($rule.NotifyUser) {
                    $html += "<div><strong>Notify User:</strong> $($rule.NotifyUser)</div>"
                }
                
                if ($rule.BlockAccess) {
                    $html += "<div><strong>Block Access:</strong> Yes ($($rule.BlockAccessScope))</div>"
                }
                
                if ($rule.GenerateIncidentReport) {
                    $html += "<div><strong>Incident Report:</strong> $($rule.GenerateIncidentReport) (Severity: $($rule.ReportSeverityLevel))</div>"
                }
                
                $html += @"
                            </div>
                        </div>
"@

            }
            
            $html += "</div>"
        }
        
        $html += @"
                </div>
            </div>
"@

    }
    
    # Close HTML
    $html += @"
        </div>
        
        <div class="footer">
            Generated by PurviewDLP PowerShell Module | $timestamp
        </div>
    </div>
    
    <script>
        function togglePolicy(index) {
            const body = document.getElementById('policy-' + index);
            const icon = document.getElementById('icon-' + index);
            
            if (body.classList.contains('expanded')) {
                body.classList.remove('expanded');
                icon.classList.remove('expanded');
            } else {
                body.classList.add('expanded');
                icon.classList.add('expanded');
            }
        }
    </script>
</body>
</html>
"@

    
    return $html
}