#Requires -Version 5.1
<#
.SYNOPSIS
    LetUsMsg Windows Permission Diagnostic & Auto-Fix Tool

.DESCRIPTION
    Diagnoses write-permission issues that prevent LetUsMsg from storing data
    on Windows. Covers the five most common failure modes on raw-ZIP-distributed
    apps: OS context, Documents path resolution, actual write access, Zone.Identifier
    "Mark of the Web" blocks on app binaries, and Windows security policies
    (Controlled Folder Access, AppLocker).

    Auto-fixes where safe:
      - Creates the data directory if missing
      - Removes Zone.Identifier ADS from app binaries (Unblock-File)
      - Grants the current user Full Control on the data directory if ACL is wrong

.PARAMETER DataDir
    Override the default data directory path.
    Default: $env:USERPROFILE\Documents\lum0

.PARAMETER AppDir
    Path to the extracted LetUsMsg application folder (checked for blocked binaries).
    Default: directory containing this script (one level up).

.PARAMETER SaveLog
    If specified, saves the full diagnostic output to a text file in the current
    directory named lum_diag_<YYYYMMDD_HHmmss>.txt

.EXAMPLE
    # Run with defaults
    .\check_permissions.ps1

.EXAMPLE
    # Custom data dir and save a log
    .\check_permissions.ps1 -DataDir "D:\LumData\lum0" -SaveLog

.EXAMPLE
    # Point at a specific app folder
    .\check_permissions.ps1 -AppDir "C:\Tools\LetUsMsg" -SaveLog
#>

[CmdletBinding()]
param(
    [string]$DataDir  = "",
    [string]$AppDir   = "",
    [switch]$SaveLog
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Continue"

# ---------------------------------------------------------------------------
# Colour helpers
# ---------------------------------------------------------------------------
function Write-OK   { param([string]$msg) Write-Host "  [OK]   $msg" -ForegroundColor Green  }
function Write-WARN { param([string]$msg) Write-Host "  [WARN] $msg" -ForegroundColor Yellow }
function Write-FAIL { param([string]$msg) Write-Host "  [FAIL] $msg" -ForegroundColor Red    }
function Write-INFO { param([string]$msg) Write-Host "  [INFO] $msg" -ForegroundColor Cyan   }
function Write-FIX  { param([string]$msg) Write-Host "  [FIX]  $msg" -ForegroundColor Magenta }
function Write-Head { param([string]$msg) Write-Host "`n=== $msg ===" -ForegroundColor White  }

# ---------------------------------------------------------------------------
# Output capture (for -SaveLog)
# ---------------------------------------------------------------------------
$script:logLines = [System.Collections.Generic.List[string]]::new()
$origOut = [Console]::Out

if ($SaveLog) {
    # Tee output to an in-memory list; we'll flush to disk at the end.
    $script:captureLog = $true
} else {
    $script:captureLog = $false
}

# Simple wrapper so log appending doesn't complicate every Write-* call.
# We re-exec with transcript instead.
$logPath = ""
if ($SaveLog) {
    $stamp   = Get-Date -Format "yyyyMMdd_HHmmss"
    $logPath = Join-Path (Get-Location) "lum_diag_$stamp.txt"
    Start-Transcript -Path $logPath -Force | Out-Null
}

# ---------------------------------------------------------------------------
# Issue / fix tracking
# ---------------------------------------------------------------------------
$issues = [System.Collections.Generic.List[string]]::new()
$fixes  = [System.Collections.Generic.List[string]]::new()

function Add-Issue { param([string]$msg) $script:issues.Add($msg) }
function Add-Fix   { param([string]$msg) $script:fixes.Add($msg)  }

# ---------------------------------------------------------------------------
# Resolve paths
# ---------------------------------------------------------------------------
if (-not $AppDir) {
    # Default: parent of the script's own folder (script lives in sh\, app lives one level up)
    $scriptFolder = Split-Path $MyInvocation.MyCommand.Path -Parent
    $AppDir = Split-Path $scriptFolder -Parent
}

# Resolve Documents via Shell API (handles folder redirection correctly)
$documentsPath = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::MyDocuments)

if (-not $DataDir) {
    $DataDir = Join-Path $documentsPath "lum0"
}

# ---------------------------------------------------------------------------
# Banner
# ---------------------------------------------------------------------------
Write-Host ""
Write-Host "############################################################" -ForegroundColor Cyan
Write-Host "  LetUsMsg   -   Windows Permission Diagnostic Tool"          -ForegroundColor Cyan
Write-Host "  $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"                -ForegroundColor Cyan
Write-Host "############################################################" -ForegroundColor Cyan
Write-Host ""
Write-INFO "Data directory : $DataDir"
Write-INFO "App directory  : $AppDir"
if ($SaveLog) { Write-INFO "Log file       : $logPath" }

# ===========================================================================
# PHASE 1  -  System & User Context
# ===========================================================================
Write-Head "PHASE 1: System & User Context"

# OS version
$os = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue
if ($os) {
    Write-INFO "OS       : $($os.Caption) (Build $($os.BuildNumber))"
    if ([int]$os.BuildNumber -lt 22000) {
        Write-WARN "Build $($os.BuildNumber) is not Windows 11  -  some policy checks may differ"
    } else {
        Write-OK "Windows 11 detected"
    }
} else {
    Write-WARN "Could not query OS version via WMI"
}

# Current user
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal   = [System.Security.Principal.WindowsPrincipal]::new($currentUser)
$isAdmin     = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)

Write-INFO "User     : $($currentUser.Name)"
Write-INFO "SID      : $($currentUser.User.Value)"

if ($isAdmin) {
    Write-OK "Running as Administrator"
} else {
    Write-WARN "Running as standard user (some checks may be limited; app does NOT need admin)"
}

# UAC status
try {
    $uacVal = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" `
                                -Name "EnableLUA" -ErrorAction Stop).EnableLUA
    if ($uacVal -eq 1) {
        Write-OK "UAC enabled (EnableLUA = 1)"
    } else {
        Write-WARN "UAC is disabled (EnableLUA = 0)  -  unusual; may affect permission model"
        Add-Issue "UAC disabled  -  unexpected configuration"
    }
} catch {
    Write-WARN "Could not read UAC registry key: $_"
}

# ===========================================================================
# PHASE 2  -  Documents Path Resolution
# ===========================================================================
Write-Head "PHASE 2: Documents Path Resolution"

# Shell API resolved path
Write-INFO "Shell API resolved Documents : $documentsPath"

# Registry shell folders (may differ if IT has applied folder redirection)
$regPath  = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
$regPath2 = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
try {
    $regDocs = (Get-ItemProperty -Path $regPath  -Name "Personal" -ErrorAction Stop).Personal
    Write-INFO "Registry Shell Folders\Personal    : $regDocs"
    if ($regDocs -ne $documentsPath) {
        Write-WARN "Registry path differs from Shell API path  -  folder redirection may be active"
        Add-Issue "Documents folder appears redirected: Shell API='$documentsPath'  Registry='$regDocs'"
    }
} catch {
    Write-WARN "Could not read Shell Folders registry key"
}
try {
    $regDocs2 = (Get-ItemProperty -Path $regPath2 -Name "Personal" -ErrorAction Stop).Personal
    Write-INFO "Registry User Shell Folders\Personal: $regDocs2"
} catch { }

# OneDrive Known Folder Move (KFM) detection
$oneDriveKFM = $false
try {
    $odPolicies = Get-ItemProperty -Path "HKCU:\Software\Microsoft\OneDrive\Accounts" `
                                   -ErrorAction Stop
    # KFM moves Documents into OneDrive sync root  -  check if documentsPath is inside OneDrive
    $odRoot = [System.Environment]::GetEnvironmentVariable("OneDrive", "User")
    if ($odRoot -and $documentsPath.StartsWith($odRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
        $oneDriveKFM = $true
        Write-WARN "OneDrive Known Folder Move (KFM) active  -  Documents is inside OneDrive sync root"
        Write-WARN "  OneDrive root : $odRoot"
        Write-WARN "  Documents     : $documentsPath"
        Write-WARN "  If OneDrive sync is paused/throttled, writes may silently fail or be delayed"
        Add-Issue "OneDrive KFM: Documents is inside '$odRoot'  -  may cause write delays or failures"
    } else {
        Write-OK "Documents is NOT inside OneDrive sync root"
    }
} catch {
    Write-OK "No OneDrive KFM configuration detected"
}

# Verify Documents directory exists
if (Test-Path $documentsPath) {
    Write-OK "Documents directory exists: $documentsPath"
} else {
    Write-FAIL "Documents directory does NOT exist: $documentsPath"
    Add-Issue "Documents directory missing: $documentsPath"
}

# ===========================================================================
# PHASE 3  -  Data Directory Checks
# ===========================================================================
Write-Head "PHASE 3: Data Directory Write Test  [$DataDir]"

# Create directory if missing
if (-not (Test-Path $DataDir)) {
    Write-WARN "Data directory does not exist  -  attempting to create..."
    try {
        New-Item -ItemType Directory -Path $DataDir -Force | Out-Null
        Write-FIX "Created directory: $DataDir"
        Add-Fix "Created data directory: $DataDir"
    } catch {
        Write-FAIL "Could not create data directory: $_"
        Add-Issue "Cannot create data directory '$DataDir': $_"
    }
} else {
    Write-OK "Data directory exists"
}

# Dump ACL
Write-INFO "--- ACL for $DataDir ---"
try {
    $acl = Get-Acl -Path $DataDir -ErrorAction Stop
    Write-INFO "Owner: $($acl.Owner)"
    foreach ($ace in $acl.Access) {
        $colour = if ($ace.AccessControlType -eq "Allow") { "Green" } else { "Red" }
        Write-Host ("  [ACL] {0,-40} {1,-10} {2}" -f $ace.IdentityReference, $ace.AccessControlType, $ace.FileSystemRights) -ForegroundColor $colour
    }
} catch {
    Write-WARN "Could not read ACL: $_"
}

# Check current user has Write in the ACL
$hasWriteInAcl = $false
try {
    $acl = Get-Acl -Path $DataDir -ErrorAction Stop
    $sid = $currentUser.User
    foreach ($ace in $acl.Access) {
        if ($ace.AccessControlType -ne "Allow") { continue }
        $aceSid = $null
        try { $aceSid = (New-Object System.Security.Principal.NTAccount($ace.IdentityReference.Value)).Translate([System.Security.Principal.SecurityIdentifier]) } catch {}
        $rights = [int]$ace.FileSystemRights
        # 0x4 = WriteData/CreateFiles, 0x40000000 = GenericWrite, 0x10000000 = GenericAll, 0x1F01FF = FullControl
        if ($aceSid -and ($aceSid -eq $sid -or $currentUser.Groups -contains $aceSid)) {
            if (($rights -band 0x4) -or ($rights -band 0x40000000) -or ($rights -band 0x10000000) -or ($rights -band 0x1F01FF)) {
                $hasWriteInAcl = $true
            }
        }
    }
} catch { }

# Actual write test (ground truth  -  overrides ACL analysis)
$writeTestPassed = $false
$testFile = Join-Path $DataDir "_lum_perm_test_$(Get-Random).tmp"
try {
    [System.IO.File]::WriteAllText($testFile, "lum-permission-test")
    $readBack = [System.IO.File]::ReadAllText($testFile)
    if ($readBack -eq "lum-permission-test") {
        $writeTestPassed = $true
        Write-OK "Write test PASSED  -  file created, written, read back, and deleted successfully"
    } else {
        Write-FAIL "Write test: file written but content mismatch (unexpected)"
        Add-Issue "Write test: content mismatch in $DataDir"
    }
    Remove-Item $testFile -Force -ErrorAction SilentlyContinue
} catch {
    $errMsg = "$_"
    Write-FAIL "Write test FAILED: $errMsg"

    # CFA (Controlled Folder Access) surfaces as 'Could not find file' rather than
    # 'Access Denied' because the kernel hook intercepts CreateFile and returns
    # ERROR_FILE_NOT_FOUND. Flag this explicitly so the summary is unambiguous.
    if ($errMsg -match 'Could not find file|cannot find the file') {
        Write-FAIL "  ^ This 'file not found' error is the known fingerprint of Controlled Folder"
        Write-FAIL "    Access blocking the write. Check Phase 5 output for CFA status."
        Add-Issue "Write test: 'Could not find file' error is caused by Controlled Folder Access blocking writes to '$DataDir'"
    } else {
        Add-Issue "Cannot write to data directory '$DataDir': $errMsg"
    }

    # Attempt ACL fix: grant current user Full Control on their own Documents subfolder
    $parentDocs = Split-Path $DataDir -Parent
    if ($parentDocs -eq $documentsPath) {
        Write-INFO "Attempting to grant '$($currentUser.Name)' Full Control on $DataDir ..."
        try {
            $acl = Get-Acl -Path $DataDir
            $ar  = [System.Security.AccessControl.FileSystemAccessRule]::new(
                       $currentUser.Name,
                       "FullControl",
                       "ContainerInherit,ObjectInherit",
                       "None",
                       "Allow")
            $acl.SetAccessRule($ar)
            Set-Acl -Path $DataDir -AclObject $acl -ErrorAction Stop
            Write-FIX "Granted Full Control to '$($currentUser.Name)' on $DataDir"
            Add-Fix "Granted Full Control to '$($currentUser.Name)' on $DataDir"

            # Re-test
            [System.IO.File]::WriteAllText($testFile, "lum-permission-test-2")
            Remove-Item $testFile -Force -ErrorAction SilentlyContinue
            Write-OK "Write test PASSED after ACL fix"
            $writeTestPassed = $true
        } catch {
            Write-FAIL "ACL fix attempt failed: $_"
            Add-Issue "ACL fix failed: $_"
        }
    } else {
        Write-WARN "Data dir is not directly under Documents  -  skipping automatic ACL fix"
        Write-WARN "Manually run (as admin):  icacls `"$DataDir`" /grant `"$($currentUser.Name)`":(OI)(CI)F"
    }
}

# Check for read-only attribute on existing files
if (Test-Path $DataDir) {
    $readOnlyFiles = Get-ChildItem -Path $DataDir -Recurse -File -ErrorAction SilentlyContinue |
                     Where-Object { $_.IsReadOnly } |
                     Select-Object -First 20
    if ($readOnlyFiles) {
        Write-WARN "Read-only files found in data directory:"
        $readOnlyFiles | ForEach-Object { Write-WARN "  $($_.FullName)" }
        Add-Issue "$($readOnlyFiles.Count) read-only file(s) found in $DataDir"
        Write-INFO "To remove read-only flag: attrib -R `"$DataDir\*`" /S"
    } else {
        Write-OK "No read-only files found in data directory"
    }

    # EFS encryption check
    $efsFiles = Get-ChildItem -Path $DataDir -Recurse -File -ErrorAction SilentlyContinue |
                Where-Object { $_.Attributes -band [System.IO.FileAttributes]::Encrypted } |
                Select-Object -First 10
    if ($efsFiles) {
        Write-WARN "EFS-encrypted files found (these are tied to the encrypting user's certificate):"
        $efsFiles | ForEach-Object { Write-WARN "  $($_.FullName)" }
        Add-Issue "$($efsFiles.Count) EFS-encrypted file(s) in $DataDir  -  may be inaccessible after profile migration"
    } else {
        Write-OK "No EFS-encrypted files found in data directory"
    }
}

# ===========================================================================
# PHASE 4  -  App Binary Zone.Identifier (Mark of the Web) Check
# ===========================================================================
Write-Head "PHASE 4: App Binary Block Check (Zone.Identifier ADS)  [$AppDir]"

$blockedFiles = [System.Collections.Generic.List[string]]::new()

if (Test-Path $AppDir) {
    $binaries = Get-ChildItem -Path $AppDir -Recurse -Include "*.exe","*.dll" -ErrorAction SilentlyContinue

    if (-not $binaries) {
        Write-INFO "No .exe or .dll files found in app directory (may not be pointing at the right folder)"
    } else {
        Write-INFO "Scanning $($binaries.Count) binary file(s)..."
        foreach ($f in $binaries) {
            $zoneStream = $f.FullName + ":Zone.Identifier"
            if (Test-Path $zoneStream) {
                # Read zone ID value (3 = Internet, 4 = Restricted)
                $zoneContent = Get-Content $zoneStream -ErrorAction SilentlyContinue
                $zoneId = ($zoneContent | Where-Object { $_ -match "^ZoneId=" }) -replace "ZoneId=", ""
                $blockedFiles.Add($f.FullName)
                Write-FAIL "BLOCKED (Zone $zoneId): $($f.Name)"
            }
        }

        if ($blockedFiles.Count -eq 0) {
            Write-OK "No Zone.Identifier marks found  -  all binaries are unblocked"
        } else {
            Write-WARN "$($blockedFiles.Count) blocked file(s) found  -  attempting Unblock-File..."
            Add-Issue "$($blockedFiles.Count) app binary file(s) have Zone.Identifier (Mark of the Web) set"
            $unblockFailed = 0
            foreach ($f in $blockedFiles) {
                try {
                    Unblock-File -Path $f -ErrorAction Stop
                    Write-FIX "Unblocked: $(Split-Path $f -Leaf)"
                } catch {
                    Write-FAIL "Could not unblock '$f': $_"
                    $unblockFailed++
                }
            }
            if ($unblockFailed -eq 0) {
                Add-Fix "Unblocked $($blockedFiles.Count) app binary file(s) via Unblock-File"
                Write-OK "All blocked files successfully unblocked  -  RESTART the app to take effect"
            } else {
                Add-Issue "$unblockFailed file(s) could not be unblocked  -  run as Administrator or right-click -> Properties -> Unblock"
            }
        }
    }
} else {
    Write-WARN "App directory not found: $AppDir"
    Write-WARN "Re-run with: -AppDir `"C:\Path\To\LetUsMsg`""
}

# Also check the ZIP itself if found nearby
$parentOfApp = Split-Path $AppDir -Parent
$zips = Get-ChildItem -Path $parentOfApp -Filter "*.zip" -ErrorAction SilentlyContinue |
        Where-Object { $_.FullName -match "lum|letusmsg" }
if ($zips) {
    foreach ($z in $zips) {
        $zoneStream = $z.FullName + ":Zone.Identifier"
        if (Test-Path $zoneStream) {
            Write-WARN "Source ZIP is marked (Zone.Identifier): $($z.Name)"
            Write-WARN "  Files extracted from a blocked ZIP may inherit the block  -  re-extract after unblocking the ZIP"
            Add-Issue "Source ZIP '$($z.Name)' has Zone.Identifier  -  consider unblocking it and re-extracting"
            try {
                Unblock-File -Path $z.FullName
                Write-FIX "Unblocked source ZIP: $($z.Name)"
                Add-Fix "Unblocked source ZIP: $($z.Name)"
            } catch { }
        }
    }
}

# ===========================================================================
# PHASE 5  -  Windows Security Policies
# ===========================================================================
Write-Head "PHASE 5: Windows Security Policies"

# --- 5a. Controlled Folder Access (ransomware protection) ---
Write-INFO "--- Controlled Folder Access (Ransomware Protection) ---"
$cfaEnabled = $false
try {
    $mpPref = Get-MpPreference -ErrorAction Stop
    $cfaEnabled = $mpPref.EnableControlledFolderAccess -ne 0   # 0=Disabled, 1=Enabled, 2=Audit
    $cfaMode    = switch ($mpPref.EnableControlledFolderAccess) {
        0 { "Disabled" }
        1 { "Enabled (blocking)" }
        2 { "Audit mode (logging only)" }
        default { "Unknown ($($mpPref.EnableControlledFolderAccess))" }
    }
    Write-INFO "Controlled Folder Access: $cfaMode"

    if ($mpPref.EnableControlledFolderAccess -eq 1) {
        Write-FAIL "Controlled Folder Access is ENABLED  -  this silently blocks untrusted apps from writing to Documents"
        Add-Issue "Controlled Folder Access is ENABLED  -  most likely cause of write failure"

        $lumExe = Join-Path $AppDir "letusmsg.exe"
        $exeExists = Test-Path $lumExe

        if (-not $exeExists) {
            Write-WARN "  letusmsg.exe not found at: $lumExe"
            Write-WARN "  Re-run with: -AppDir `"C:\Path\To\LetUsMsg`""
        } elseif ($isAdmin) {
            Write-INFO "  Running as admin  -  attempting to add letusmsg.exe to CFA allowlist..."
            try {
                Add-MpPreference -ControlledFolderAccessAllowedApplications $lumExe -ErrorAction Stop
                Write-FIX "Added '$lumExe' to Controlled Folder Access allowlist"
                Add-Fix "Added letusmsg.exe to Controlled Folder Access allowlist"
                Write-OK "CFA fix applied  -  RESTART the app for it to take effect"
            } catch {
                Write-FAIL "Could not add to CFA allowlist: $_"
                Add-Issue "CFA auto-fix failed: $_"
                Write-INFO "  Manual fix: Windows Security -> Virus & threat protection ->"
                Write-INFO "  Ransomware protection -> Controlled folder access -> Allow an app through"
                Write-INFO "  Add: $lumExe"
            }
        } else {
            Write-WARN "  Not running as admin  -  cannot auto-fix CFA. Re-run as Administrator, or fix manually:"
            Write-INFO ""
            Write-INFO "  Option 1 (GUI): Windows Security -> Virus & threat protection ->"
            Write-INFO "  Ransomware protection -> Controlled folder access -> Allow an app through"
            Write-INFO "  Add: $lumExe"
            Write-INFO ""
            Write-INFO "  Option 2 (PowerShell as admin):"
            Write-INFO "  Add-MpPreference -ControlledFolderAccessAllowedApplications `"$lumExe`""
        }
    } elseif ($mpPref.EnableControlledFolderAccess -eq 2) {
        Write-WARN "Controlled Folder Access is in Audit mode  -  writes are logged but not blocked"
    } else {
        Write-OK "Controlled Folder Access is Disabled"
    }

    # Show allowed apps list
    if ($mpPref.ControlledFolderAccessAllowedApplications) {
        Write-INFO "Currently allowed apps in CFA:"
        $mpPref.ControlledFolderAccessAllowedApplications | ForEach-Object { Write-INFO "  - $_" }
        $lumAllowed = $mpPref.ControlledFolderAccessAllowedApplications |
                      Where-Object { $_ -match "letusmsg" }
        if ($lumAllowed) {
            Write-OK "LetUsMsg already in CFA allowlist"
        } elseif ($cfaEnabled) {
            Write-FAIL "LetUsMsg is NOT in the CFA allowlist  -  it will be blocked"
        }
    }
} catch {
    Write-WARN "Could not query Windows Defender preferences (Get-MpPreference failed): $_"
    Write-WARN "Windows Defender may be managed by Group Policy or a third-party AV is in use"
}

# --- 5b. Windows Defender exclusions ---
Write-INFO "--- Windows Defender Exclusions ---"
try {
    $mpPref = Get-MpPreference -ErrorAction Stop
    $pathExclusions = $mpPref.ExclusionPath
    if ($pathExclusions) {
        $dataExcluded = $pathExclusions | Where-Object { $DataDir.StartsWith($_, [System.StringComparison]::OrdinalIgnoreCase) }
        if ($dataExcluded) {
            Write-OK "Data directory is under a Defender exclusion path: $dataExcluded"
        } else {
            Write-INFO "Data directory is NOT in Defender exclusion paths (normal; not required)"
        }
    } else {
        Write-INFO "No Defender path exclusions configured"
    }
} catch { }

# --- 5c. AppLocker / WDAC ---
Write-INFO "--- AppLocker / Windows Defender Application Control ---"
$appLockerSvc = Get-Service -Name "AppIDSvc" -ErrorAction SilentlyContinue
if ($appLockerSvc -and $appLockerSvc.Status -eq "Running") {
    Write-WARN "AppLocker service (AppIDSvc) is RUNNING  -  AppLocker policies may restrict app execution"
    Add-Issue "AppLocker service is running  -  policies may block LetUsMsg"
    try {
        $policy = Get-AppLockerPolicy -Effective -ErrorAction Stop
        Write-INFO "AppLocker policy retrieved  -  check RuleCollections for exe/dll restrictions"
    } catch {
        Write-INFO "Could not read effective AppLocker policy (may need admin)"
    }
} else {
    Write-OK "AppLocker service is not running"
}

# WDAC (check for CI policy files)
$wdacPolicies = Get-ChildItem -Path "$env:WINDIR\System32\CodeIntegrity\CIPolicies\Active" `
                               -Filter "*.cip" -ErrorAction SilentlyContinue
if ($wdacPolicies) {
    Write-WARN "WDAC (Windows Defender Application Control) policies found: $($wdacPolicies.Count) active policy file(s)"
    $wdacPolicies | ForEach-Object { Write-WARN "  $($_.Name)" }
    Add-Issue "WDAC policies active  -  may affect app execution on managed devices"
} else {
    Write-OK "No active WDAC policy files detected"
}

# --- 5d. Group Policy / MDM ---
Write-INFO "--- Group Policy / MDM Enrollment ---"
try {
    $mdm = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Enrollments" -ErrorAction SilentlyContinue
    if ($mdm -and $mdm.Count -gt 0) {
        Write-WARN "MDM enrollment detected  -  device may have corporate policies restricting file access"
        Add-Issue "Device is MDM-enrolled  -  corporate policies may be restricting access"
    } else {
        Write-OK "No MDM enrollment detected"
    }
} catch {
    Write-OK "Could not check MDM enrollment (non-critical)"
}

# ===========================================================================
# SUMMARY
# ===========================================================================
Write-Head "SUMMARY"

if ($issues.Count -eq 0 -and $fixes.Count -eq 0) {
    Write-Host "`n  All checks passed  -  no issues detected." -ForegroundColor Green
    Write-Host "  If the app still cannot write, re-run with -AppDir pointing at the LetUsMsg folder" -ForegroundColor Cyan
    Write-Host "  and with -SaveLog to capture the full output." -ForegroundColor Cyan
} else {
    if ($fixes.Count -gt 0) {
        Write-Host "`n  Fixes applied ($($fixes.Count)):" -ForegroundColor Magenta
        $fixes | ForEach-Object { Write-Host "    + $_" -ForegroundColor Magenta }
    }
    if ($issues.Count -gt 0) {
        Write-Host "`n  Issues needing attention ($($issues.Count)):" -ForegroundColor Red
        $issues | ForEach-Object { Write-Host "    ! $_" -ForegroundColor Red }
    }
}

Write-Host ""
if ($writeTestPassed) {
    Write-Host "  WRITE TEST: PASSED" -ForegroundColor Green
} else {
    Write-Host "  WRITE TEST: FAILED   -  app cannot store data at $DataDir" -ForegroundColor Red
}

Write-Host ""
if ($SaveLog) {
    Stop-Transcript | Out-Null
    Write-Host "  Log saved to: $logPath" -ForegroundColor Cyan
}
Write-Host ""
