# Show-IntuneDeviceQR.ps1 - Standalone version of the QR-code panel from
# Monitor-IntuneProgress.ps1, extracted for use at sites that only need the
# device-ID display (no DSC monitoring, no Intune sync triggers, no reboot
# orchestration).
#
# Polls dsregcmd /status every 15s until the device reports an Azure AD
# DeviceId, then renders that ID as a half-block QR code in the console
# along with the literal text. Stays open until the user presses a key.
#
# REQUIREMENTS
#   - Windows PowerShell 5.1 (or PS 7) with .NET Framework available
#   - QRCoder.dll alongside this script (or at one of the fallback paths
#     below). Single ~75 KB native dependency, no internet needed.
#
# USAGE
#   powershell.exe -NoProfile -ExecutionPolicy Bypass -File Show-IntuneDeviceQR.ps1
#
# Or double-click via a one-line .bat:
#   powershell.exe -NoProfile -ExecutionPolicy Bypass -NoExit -File "%~dp0Show-IntuneDeviceQR.ps1"

[CmdletBinding()]
param(
    [int]$PollSeconds = 15,
    [string]$QRCoderDllPath = ''
)

$ErrorActionPreference = 'Continue'

# ----------------------------------------------------------------------------
# Locate QRCoder.dll. Search order:
#   1. -QRCoderDllPath if explicitly passed
#   2. Next to this script ($PSScriptRoot\QRCoder.dll)
#   3. Current working directory
#   4. WJ canonical staging path (kept for cross-site portability)
# ----------------------------------------------------------------------------
function Resolve-QRCoderDll {
    param([string]$Override)

    $candidates = @()
    if ($Override) { $candidates += $Override }
    if ($PSScriptRoot) { $candidates += (Join-Path $PSScriptRoot 'QRCoder.dll') }
    $candidates += (Join-Path (Get-Location) 'QRCoder.dll')
    $candidates += 'C:\Enrollment\shopfloor-setup\Shopfloor\QRCoder.dll'

    foreach ($c in $candidates) {
        if ($c -and (Test-Path -LiteralPath $c)) { return (Resolve-Path -LiteralPath $c).Path }
    }
    return $null
}

# ----------------------------------------------------------------------------
# Get the AAD DeviceId from dsregcmd. Returns $null until the device is
# Azure AD joined AND dsregcmd has populated the field.
# ----------------------------------------------------------------------------
function Get-AadDeviceId {
    try {
        $dsreg = dsregcmd /status 2>&1
    } catch {
        return $null
    }
    $line = $dsreg | Select-String 'DeviceId' | Select-Object -First 1
    if (-not $line) { return $null }
    $parts = $line.ToString().Split(':')
    if ($parts.Count -lt 2) { return $null }
    $id = $parts[1].Trim()
    if ([string]::IsNullOrWhiteSpace($id)) { return $null }
    return $id
}

# ----------------------------------------------------------------------------
# Half-block QR renderer. Each output character is 1 module wide, 2 modules
# tall, using:
#   U+2588 = both top and bottom modules set
#   U+2580 = top only
#   U+2584 = bottom only
#   space  = neither
# Cuts QR height in half vs full-block rendering. Adds a 4-module quiet
# zone manually since QRCoder's ModuleMatrix excludes it by default.
# ----------------------------------------------------------------------------
function Format-QrHalfBlocks {
    param([string]$Payload, [string]$DllPath, [int]$IndentSpaces = 8)

    Add-Type -Path $DllPath
    $gen  = New-Object QRCoder.QRCodeGenerator
    $data = $gen.CreateQrCode($Payload, [QRCoder.QRCodeGenerator+ECCLevel]::L)

    $matrix = $data.ModuleMatrix
    $size   = $matrix.Count
    $pad    = 4
    $total  = $size + 2 * $pad

    $upper = [char]0x2580
    $lower = [char]0x2584
    $full  = [char]0x2588
    $left  = ' ' * $IndentSpaces

    $lines = New-Object System.Collections.Generic.List[string]
    for ($y = 0; $y -lt $total; $y += 2) {
        $sb = New-Object System.Text.StringBuilder
        [void]$sb.Append($left)
        for ($x = 0; $x -lt $total; $x++) {
            $mx  = $x - $pad
            $my1 = $y - $pad
            $my2 = $y + 1 - $pad

            $top = ($my1 -ge 0 -and $my1 -lt $size -and $mx -ge 0 -and $mx -lt $size -and $matrix[$my1].Get($mx))
            $bot = ($my2 -ge 0 -and $my2 -lt $size -and $mx -ge 0 -and $mx -lt $size -and $matrix[$my2].Get($mx))

            if     ($top -and $bot) { [void]$sb.Append($full) }
            elseif ($top)           { [void]$sb.Append($upper) }
            elseif ($bot)           { [void]$sb.Append($lower) }
            else                    { [void]$sb.Append(' ') }
        }
        $lines.Add($sb.ToString())
    }
    return $lines
}

# ----------------------------------------------------------------------------
# Bigger console so the QR + text frame fits in one viewport.
# ----------------------------------------------------------------------------
try {
    $rui = $Host.UI.RawUI
    $maxH = $rui.MaxPhysicalWindowSize.Height
    $targetWindow = [Math]::Min(58, [int]$maxH)
    $targetBuffer = [Math]::Max($targetWindow, 200)

    $bs = $rui.BufferSize
    if ($bs.Height -lt $targetBuffer) { $bs.Height = $targetBuffer; $rui.BufferSize = $bs }

    $ws = $rui.WindowSize
    if ($ws.Height -lt $targetWindow) { $ws.Height = $targetWindow; $rui.WindowSize = $ws }
} catch {}

# ----------------------------------------------------------------------------
# Wait for QRCoder.dll
# ----------------------------------------------------------------------------
$dllPath = Resolve-QRCoderDll -Override $QRCoderDllPath

# Mark-of-the-Web: when files arrive via SMB / zip download / email,
# Windows attaches a Zone.Identifier alternate data stream that flags the
# file as "from another computer". Add-Type then refuses to load the DLL
# with "Operation is not supported" / "Could not load file or assembly".
# Unblock-File silently strips the ADS. Best-effort: errors are non-fatal
# (file may already be unblocked, or the user may not have write access).
if ($dllPath) {
    try { Unblock-File -LiteralPath $dllPath -ErrorAction SilentlyContinue } catch {}
}
if ($PSCommandPath) {
    try { Unblock-File -LiteralPath $PSCommandPath -ErrorAction SilentlyContinue } catch {}
}

if (-not $dllPath) {
    Write-Host ""
    Write-Host "ERROR: QRCoder.dll not found." -ForegroundColor Red
    Write-Host "Searched (in order):"
    if ($QRCoderDllPath) { Write-Host "  - $QRCoderDllPath  (-QRCoderDllPath)" }
    if ($PSScriptRoot)   { Write-Host "  - $(Join-Path $PSScriptRoot 'QRCoder.dll')" }
    Write-Host "  - $(Join-Path (Get-Location) 'QRCoder.dll')"
    Write-Host "  - C:\Enrollment\shopfloor-setup\Shopfloor\QRCoder.dll"
    Write-Host ""
    Write-Host "Drop QRCoder.dll alongside this script and re-run." -ForegroundColor Yellow
    exit 2
}

# ----------------------------------------------------------------------------
# Poll loop - retry every $PollSeconds until DeviceId appears
# ----------------------------------------------------------------------------
Write-Host ""
Write-Host "  Waiting for Azure AD device registration..." -ForegroundColor Cyan
Write-Host "  Polling dsregcmd every $PollSeconds s. Press Ctrl+C to abort." -ForegroundColor DarkGray
Write-Host ""

$attempt = 0
while ($true) {
    $attempt++
    $deviceId = Get-AadDeviceId
    if ($deviceId) { break }

    Write-Host ("  [{0}]  attempt {1,3}  no DeviceId yet, retrying in {2}s..." -f (Get-Date -Format 'HH:mm:ss'), $attempt, $PollSeconds) -ForegroundColor DarkGray
    Start-Sleep -Seconds $PollSeconds
}

# ----------------------------------------------------------------------------
# Render
# ----------------------------------------------------------------------------
Clear-Host
Write-Host ""
Write-Host "  GE Aerospace  --  Intune Device ID" -ForegroundColor Cyan
Write-Host "  $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor DarkGray
Write-Host ""
Write-Host "  Device ID: $deviceId" -ForegroundColor Green
Write-Host ""

try {
    $qrLines = Format-QrHalfBlocks -Payload $deviceId -DllPath $dllPath
    foreach ($l in $qrLines) { Write-Host $l }
} catch {
    Write-Host "  (QR generation failed: $($_.Exception.Message))" -ForegroundColor Red
}

Write-Host ""
Write-Host "  Scan with phone camera or Intune admin app." -ForegroundColor DarkGray
Write-Host ""
Write-Host "  Press any key to close..." -ForegroundColor Yellow
try { [void][Console]::ReadKey($true) } catch { [void](Read-Host) }
exit 0
