#!/usr/bin/env python3
r"""
Generate CHCFC Windows 11 PXE Deployment Guide PDF.
Follows CHCFC branding guidelines and documentation standards.
A complete, technician-friendly printable sheet detailing the PXE deployment workflow:
- Step 1: Machine Boot & BIOS Selection (F12 One-Time Boot, UEFI vs Legacy)
- Step 2: iPXE Boot Menu & Asset Streaming
- Step 3: WinPE RAM Disk Architecture (X:\ vs Y:\) & startnet.cmd
- Step 4: Dynamic Hardware Tailoring (configure.vbs)
- Step 5: Unattended Setup Engine & autounattend.xml Settings
- Step 6: First-Boot Post-Install Pipeline (SetupComplete.cmd) & Quick Reference
"""

import os
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, KeepTogether, PageBreak
)
from reportlab.pdfgen import canvas

PDF_OUTPUT_PATH = "/mnt/docker/httpd-ficit-files/CHCFC Windows 11 PXE Deployment Guide.pdf"
LOGO_PATH = "/mnt/docker/httpd-ficit-files/Design-Elements/Logo/Primary Logo-no background.png"

# Color Palette (matching CHCFC branding guidelines)
PRIMARY_TEAL = colors.HexColor("#0f766e")      # Deep Emerald / Teal
ACCENT_GREEN = colors.HexColor("#059669")      # Green
DARK_SLATE   = colors.HexColor("#1e293b")      # Text Dark
MUTED_TEXT   = colors.HexColor("#475569")      # Slate 600
LIGHT_BG     = colors.HexColor("#f8fafc")      # Slate 50
BORDER_COLOR = colors.HexColor("#cbd5e1")      # Slate 300
HIGHLIGHT_BG = colors.HexColor("#f0fdf4")      # Mint / Emerald 50
WARN_BG      = colors.HexColor("#fef2f2")      # Red 50
WARN_BORDER  = colors.HexColor("#f87171")      # Red 400
WARN_TEXT    = colors.HexColor("#991b1b")      # Red 800
INFO_BG      = colors.HexColor("#f0f9ff")      # Sky 50
INFO_BORDER  = colors.HexColor("#38bdf8")      # Sky 400
CODE_BG      = colors.HexColor("#f1f5f9")      # Slate 100

class NumberedCanvas(canvas.Canvas):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._saved_page_states = []

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.draw_page_decorations(num_pages)
            super().showPage()
        super().save()

    def draw_page_decorations(self, total_pages):
        self.saveState()
        
        # Header accent bar (top border)
        self.setFillColor(PRIMARY_TEAL)
        self.rect(36, 792 - 12, 612 - 72, 4, fill=1, stroke=0)
        
        # Footer
        self.setFont("Helvetica", 8)
        self.setFillColor(MUTED_TEXT)
        footer_text = "Community Health Center of Franklin County (CHCFC)  |  IT Infrastructure & Deployment Engineering"
        self.drawString(36, 22, footer_text)
        
        page_num_str = f"Page {self._pageNumber} of {total_pages}"
        self.drawRightString(612 - 36, 22, page_num_str)
        
        # Footer thin line
        self.setStrokeColor(BORDER_COLOR)
        self.setLineWidth(0.5)
        self.line(36, 32, 612 - 36, 32)
        
        self.restoreState()


def build_pdf():
    # Page setup: letter is 612 x 792 points. Margins: 36 pt (0.5 in) left/right, 24 pt top, 40 pt bottom
    doc = SimpleDocTemplate(
        PDF_OUTPUT_PATH,
        pagesize=letter,
        leftMargin=36,
        rightMargin=36,
        topMargin=22,
        bottomMargin=42
    )

    styles = getSampleStyleSheet()
    
    # Custom Typography
    title_style = ParagraphStyle(
        'DocTitle', parent=styles['Normal'],
        fontName='Helvetica-Bold', fontSize=15, leading=19,
        textColor=PRIMARY_TEAL
    )
    
    subtitle_style = ParagraphStyle(
        'DocSubTitle', parent=styles['Normal'],
        fontName='Helvetica', fontSize=8.5, leading=11.5,
        textColor=MUTED_TEXT
    )
    
    banner_head = ParagraphStyle(
        'BannerHead', parent=styles['Normal'],
        fontName='Helvetica-Bold', fontSize=9, leading=12,
        textColor=PRIMARY_TEAL
    )
    
    banner_body = ParagraphStyle(
        'BannerBody', parent=styles['Normal'],
        fontName='Helvetica', fontSize=7.5, leading=10.5,
        textColor=DARK_SLATE
    )
    
    sec_title = ParagraphStyle(
        'SecTitle', parent=styles['Normal'],
        fontName='Helvetica-Bold', fontSize=10, leading=13,
        textColor=PRIMARY_TEAL, spaceAfter=2
    )

    body_text = ParagraphStyle(
        'Body', parent=styles['Normal'],
        fontName='Helvetica', fontSize=7.5, leading=10.5,
        textColor=DARK_SLATE
    )
    
    body_bold = ParagraphStyle(
        'BodyBold', parent=styles['Normal'],
        fontName='Helvetica-Bold', fontSize=7.5, leading=10.5,
        textColor=DARK_SLATE
    )

    code_text = ParagraphStyle(
        'CodeStyle', parent=styles['Normal'],
        fontName='Courier', fontSize=7, leading=9.5,
        textColor=colors.HexColor("#0f172a")
    )

    story = []

    # ================= PAGE 1 =================
    # 1. Header with Title & Logo
    logo_img = Image(LOGO_PATH, width=150, height=52.2)
    logo_img.hAlign = 'LEFT'
    
    header_title_cell = [
        Paragraph("Windows 11 Enterprise PXE Deployment Guide", title_style),
        Spacer(1, 2),
        Paragraph("Standard Operating Procedure: Network Boot, Automated WinPE & Zero-Touch Domain Staging", subtitle_style),
    ]
    
    header_table = Table([[header_title_cell, logo_img]], colWidths=[380, 160])
    header_table.setStyle(TableStyle([
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('LEFTPADDING', (0, 0), (-1, -1), 0),
        ('RIGHTPADDING', (0, 0), (-1, -1), 0),
        ('TOPPADDING', (0, 0), (-1, -1), 0),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
    ]))
    story.append(header_table)
    story.append(Spacer(1, 6))

    # Overview Banner
    overview_content = [
        Paragraph("<b>Automated Zero-Touch Deployment Architecture</b>", banner_head),
        Spacer(1, 1),
        Paragraph(
            "This deployment pipeline enables end-to-end bare-metal installation of Windows 11 Pro across CHCFC workstations and laptops. "
            "From power-on (F12) to ready-to-use domain join, the system dynamically configures chassis naming, auto-partitions drives, "
            "bypasses TPM/CPU limits on legacy hardware, joins Active Directory, and deploys Bitdefender & NinjaOne agents automatically.",
            banner_body
        )
    ]
    overview_box = Table([[overview_content]], colWidths=[540])
    overview_box.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), HIGHLIGHT_BG),
        ('BOX', (0, 0), (-1, -1), 0.75, ACCENT_GREEN),
        ('TOPPADDING', (0, 0), (-1, -1), 5),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
        ('LEFTPADDING', (0, 0), (-1, -1), 8),
        ('RIGHTPADDING', (0, 0), (-1, -1), 8),
    ]))
    story.append(overview_box)
    story.append(Spacer(1, 7))

    # SECTION 1: BIOS & Network Boot Selection
    s1_title = Paragraph("1. Physical Machine Boot & BIOS Selection (Press F12)", sec_title)
    s1_content = [
        Paragraph("<b>One-Time Boot Menu Execution:</b> Connect the target workstation to the staging network (LAN <code>10.77.1.0/24</code>) via Ethernet. Power on the machine and immediately tap <b>F12</b> continuously until the One-Time Boot Menu appears.", body_text),
        Spacer(1, 3),
        Paragraph("<b>Boot Mode Matrix & Firmware Constraints:</b>", body_bold),
        Paragraph("  • <b>Modern Hardware (OptiPlex 7040+, Micro PCs, All Laptops):</b> Select <b>UEFI Boot &rarr; ONBOARD NIC (IPv4)</b>. Ensure Secure Boot is enabled or disabled as needed; TPM 2.0 bypass is applied automatically if missing.<br/>"
                  "  • <b>Legacy Hardware (Dell OptiPlex 790):</b> In BIOS (F2) &rarr; <i>Boot Sequence</i>, ensure <i>Boot List Option</i> is set to <b>Legacy</b>. Under Legacy Boot, select <b>Onboard NIC</b>. (OptiPlex 790 has incomplete UEFI 2.0 and hangs on UEFI PXE).<br/>"
                  "  • <b>SATA Controller Mode:</b> Ensure SATA operation in BIOS is set to <b>AHCI</b>. Windows 11 native inbox <code>storahci.sys</code> recognizes internal drives out of the box with zero third-party storage drivers required.", body_text),
        Spacer(1, 3),
        Paragraph("<b>DHCP & TFTP Handshake:</b> The client DHCP request receives Option 66 (Next Server: <code>10.77.1.220</code>) and Option 67 (Boot File). Legacy BIOS loads <code>undionly.kpxe</code>; UEFI loads <code>ipxe.efi</code>, which chains directly to <code>http://10.77.1.220:8089/boot.ipxe</code>.", body_text)
    ]
    s1_box = Table([[s1_content]], colWidths=[540])
    s1_box.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), LIGHT_BG),
        ('BOX', (0, 0), (-1, -1), 0.5, BORDER_COLOR),
        ('TOPPADDING', (0, 0), (-1, -1), 5),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
        ('LEFTPADDING', (0, 0), (-1, -1), 8),
        ('RIGHTPADDING', (0, 0), (-1, -1), 8),
    ]))
    story.append(s1_title)
    story.append(s1_box)
    story.append(Spacer(1, 7))

    # SECTION 2: iPXE Menu & Asset Streaming
    s2_title = Paragraph("2. iPXE Menu Selection & Pre-Boot Streaming", sec_title)
    s2_content = [
        Paragraph("<b>Interactive Boot Menu:</b> iPXE presents the <i>Enterprise Network Boot & Installation</i> menu with a 5-minute fallback to local drive:", body_text),
        Spacer(1, 2),
        Paragraph("  • <b>Option 1 (Automated):</b> <code>Install Windows 11 Pro - Automated Unattended (Domain Join)</code> &larr; <i>Recommended default for staging.</i><br/>"
                  "  • <b>Option 2 (Manual):</b> <code>Install Windows 11 Pro - Interactive Setup (via Media Share)</code> &larr; <i>For custom disk or edition selection.</i>", body_text),
        Spacer(1, 3),
        Paragraph("<b>High-Speed HTTP Asset Streaming via wimboot:</b>", body_bold),
        Paragraph("When selected, iPXE streams the Windows boot environment over high-speed HTTP (TCP port 8089) instead of slow TFTP. <code>wimboot</code> packages the following core files into system memory in ~15-20 seconds:", body_text),
        Spacer(1, 2),
        Paragraph("<code>kernel http://10.77.1.220:8089/wimboot</code><br/>"
                  "<code>initrd http://10.77.1.220:8089/win11/bootmgr          bootmgr</code> (or <code>bootmgr.efi</code>)<br/>"
                  "<code>initrd http://10.77.1.220:8089/win11/BCD              BCD</code> (Pre-configured BCD store)<br/>"
                  "<code>initrd http://10.77.1.220:8089/win11/boot.sdi          boot.sdi</code> (System deployment image RAM container)<br/>"
                  "<code>initrd http://10.77.1.220:8089/win11/boot.wim          boot.wim</code> (WinPE OS image with injected network drivers)<br/>"
                  "<code>initrd http://10.77.1.220:8089/win11/autounattend.xml  autounattend.xml</code> (Base unattended answer file)", code_text),
        Spacer(1, 2),
        Paragraph("<b>Deployment Monitor Live Tracking:</b> NGINX logs every downloaded block. Technicians can view active downloads live at <code>http://10.77.1.220:8087/monitor</code> or by typing <code>pxe-monitor -w</code> in terminal.", body_text)
    ]
    s2_box = Table([[s2_content]], colWidths=[540])
    s2_box.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), INFO_BG),
        ('BOX', (0, 0), (-1, -1), 0.5, INFO_BORDER),
        ('TOPPADDING', (0, 0), (-1, -1), 5),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
        ('LEFTPADDING', (0, 0), (-1, -1), 8),
        ('RIGHTPADDING', (0, 0), (-1, -1), 8),
    ]))
    story.append(s2_title)
    story.append(s2_box)
    story.append(Spacer(1, 7))

    # SECTION 3: WinPE RAM Disk Architecture (X:\ vs Y:\) & startnet.cmd
    s3_title = Paragraph("3. WinPE RAM Disk Architecture (X:\\ vs Y:\\) & Automated Startup", sec_title)
    
    arch_table_data = [
        [
            Paragraph("<b>Filesystem</b>", body_bold),
            Paragraph("<b>Physical Location</b>", body_bold),
            Paragraph("<b>Contents & Operational Role</b>", body_bold)
        ],
        [
            Paragraph("<b>X:\\ Drive</b><br/><i>(WinPE RAM Disk)</i>", body_text),
            Paragraph("Volatile System RAM<br/>(Expanded <code>boot.wim</code>)", body_text),
            Paragraph("Minimal Windows Preinstallation OS kernel, core diagnostic utilities (<code>diskpart</code>, <code>drvload</code>, <code>curl</code>, <code>findstr</code>), injected drivers staged in <code>X:\\Drivers</code>, and staging scripts. Size: ~900 MB.", body_text)
        ],
        [
            Paragraph("<b>Y:\\ Drive</b><br/><i>(Media Network Share)</i>", body_text),
            Paragraph("Local High-Speed Samba<br/><code>\\\\10.77.1.220\\win11</code>", body_text),
            Paragraph("Full Windows 11 installation media (<code>Y:\\sources\\install.wim</code>, <code>setup.exe</code>, <code>$OEM$</code> scripts). Mounted anonymously (guest). Windows Setup streams the multi-gigabyte OS image directly over Gigabit LAN without consuming local RAM.", body_text)
        ]
    ]
    arch_table = Table(arch_table_data, colWidths=[90, 130, 320])
    arch_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), LIGHT_BG),
        ('LINEBELOW', (0, 0), (-1, 0), 1, PRIMARY_TEAL),
        ('GRID', (0, 0), (-1, -1), 0.5, BORDER_COLOR),
        ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ('TOPPADDING', (0, 0), (-1, -1), 3),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 3),
        ('LEFTPADDING', (0, 0), (-1, -1), 5),
        ('RIGHTPADDING', (0, 0), (-1, -1), 5),
    ]))

    s3_startup_content = [
        Paragraph("<b>Automated Startup Pipeline (<code>winpeshl.ini &rarr; startnet.cmd</code>):</b>", body_bold),
        Paragraph("1. <b>Dynamic Driver Loading:</b> Ingests all <code>.inf</code> drivers staged in <code>X:\\Drivers</code> via <code>drvload</code> (specifically official SHA-256 WHQL driver <code>e1c65x64.inf</code> for Intel 82579LM NICs).<br/>"
                  "2. <b>Network Initialization:</b> Executes <code>wpeinit</code> and <code>wpeutil initializenetwork</code>; pings <code>10.77.1.220</code> until the network link negotiates.<br/>"
                  "3. <b>Hardware Compatibility Bypasses:</b> Injects registry DWORDs into <code>HKLM\\SYSTEM\\Setup\\LabConfig</code> (<code>BypassTPMCheck</code>, <code>BypassSecureBootCheck</code>, <code>BypassCPUCheck</code>, <code>BypassRAMCheck</code>, <code>BypassStorageCheck</code>).<br/>"
                  "4. <b>Local Share Mount:</b> Mounts <code>\\\\10.77.1.220\\win11</code> to drive letter <code>Y:</code> with automatic retries.<br/>"
                  "5. <b>Monitor Telemetry:</b> Fires beacons to <code>http://10.77.1.220:8087/api/beacon</code> (<code>WinPE_Network_Ready</code> and <code>Media_Share_Mounted</code>).", body_text)
    ]
    s3_startup_box = Table([[s3_startup_content]], colWidths=[540])
    s3_startup_box.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), LIGHT_BG),
        ('BOX', (0, 0), (-1, -1), 0.5, BORDER_COLOR),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
        ('LEFTPADDING', (0, 0), (-1, -1), 8),
        ('RIGHTPADDING', (0, 0), (-1, -1), 8),
    ]))

    story.append(s3_title)
    story.append(arch_table)
    story.append(Spacer(1, 4))
    story.append(s3_startup_box)

    # ================= PAGE 2 =================
    story.append(PageBreak())

    # SECTION 4: Dynamic Hardware Tailoring (configure.vbs)
    s4_title = Paragraph("4. Dynamic Machine Tailoring & Auto-Partitioning (configure.vbs)", sec_title)
    s4_content = [
        Paragraph("Before launching Windows Setup, <code>startnet.cmd</code> executes <code>cscript //nologo X:\\Windows\\System32\\configure.vbs</code>. This script inspects the bare-metal hardware and adapts the installation dynamically:", body_text),
        Spacer(1, 3),
        Paragraph("<b>1. Chassis Detection & Standardized Computer Naming:</b><br/>"
                  "Queries <code>Win32_SystemEnclosure.ChassisTypes</code> and <code>Win32_BIOS.SerialNumber</code> to generate the official NetBIOS hostname:<br/>"
                  "  • <b>Laptops / Portables (Chassis Types 8, 9, 10, 14, 30, 31, 32):</b> Assigned <code>CHC-LP-&lt;SERVICE TAG&gt;</code> (e.g. <code>CHC-LP-7K2X9Q1</code>).<br/>"
                  "  • <b>Desktops / Workstations (Chassis Types 3, 4, 6, 7, etc.):</b> Assigned <code>CHC-PC-&lt;SERVICE TAG&gt;</code> (e.g. <code>CHC-PC-4M8P2W1</code>).<br/>"
                  "  • Enforces the strict 15-character Windows NetBIOS naming limitation.", body_text),
        Spacer(1, 3),
        Paragraph("<b>2. Firmware Detection & Automated Zero-Touch Diskpart:</b><br/>"
                  "Inspects <code>HKLM\\System\\CurrentControlSet\\Control\\PEFirmwareType</code> to dynamically create <code>X:\\diskpart.txt</code> and execute <code>diskpart /s X:\\diskpart.txt</code> to clean Disk 0:", body_text),
        Spacer(1, 2),
        Paragraph("  • <b>UEFI Mode (PEFirmwareType = 2):</b> <code>clean &rarr; convert gpt &rarr; create efi 100MB FAT32 (System) &rarr; create msr 16MB &rarr; create primary NTFS (Windows, letter=W)</code><br/>"
                  "  • <b>Legacy BIOS Mode (PEFirmwareType = 1):</b> <code>clean &rarr; convert mbr &rarr; create primary 100MB NTFS Active (System) &rarr; create primary NTFS (Windows, letter=W)</code>", code_text),
        Spacer(1, 3),
        Paragraph("<b>3. Dynamic Answer File Generation:</b> Injects the calculated ComputerName into <code>X:\\autounattend.xml</code> and exports <code>COMP_NAME</code>, <code>MODEL</code>, <code>MAC</code>, and <code>FIRMWARE</code> to <code>X:\\env.cmd</code>.", body_text)
    ]
    s4_box = Table([[s4_content]], colWidths=[540])
    s4_box.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), LIGHT_BG),
        ('BOX', (0, 0), (-1, -1), 0.5, BORDER_COLOR),
        ('TOPPADDING', (0, 0), (-1, -1), 5),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 5),
        ('LEFTPADDING', (0, 0), (-1, -1), 8),
        ('RIGHTPADDING', (0, 0), (-1, -1), 8),
    ]))
    story.append(s4_title)
    story.append(s4_box)
    story.append(Spacer(1, 7))

    # SECTION 5: autounattend.xml Settings Breakdown
    s5_title = Paragraph("5. Unattended Configuration Engine: autounattend.xml Settings", sec_title)
    
    unattend_table_data = [
        [
            Paragraph("<b>Configuration Pass</b>", body_bold),
            Paragraph("<b>XML Component</b>", body_bold),
            Paragraph("<b>Settings Applied & Automation Details</b>", body_bold)
        ],
        [
            Paragraph("<b>1. windowsPE</b><br/><i>(Pre-Install)</i>", body_text),
            Paragraph("<code>Microsoft-Windows-Setup</code><br/><code>International-Core-WinPE</code>", code_text),
            Paragraph("  • Sets InputLocale, SystemLocale, and UILanguage to <b>en-US (0409)</b>.<br/>"
                      "  • Bypasses Setup UI prompts: <code>WillShowUI = Never</code>.<br/>"
                      "  • Automatically accepts Microsoft EULA: <code>AcceptEula = true</code>.<br/>"
                      "  • Installs <b>Windows 11 Pro</b> from <code>Y:\\sources\\install.wim</code>.<br/>"
                      "  • Targets formatted storage: <code>InstallToAvailablePartition = true</code>.<br/>"
                      "  • Applies default Pro product key: <code>VK7JG-NPHTM-C97JM-9MPGT-3V66T</code>.", body_text)
        ],
        [
            Paragraph("<b>2. specialize</b><br/><i>(Hardware Config)</i>", body_text),
            Paragraph("<code>Microsoft-Windows-Shell-Setup</code><br/><code>UnattendedJoin</code>", code_text),
            Paragraph("  • Sets ComputerName dynamically from <code>configure.vbs</code> (<code>CHC-PC-*</code> / <code>CHC-LP-*</code>).<br/>"
                      "  • Sets TimeZone to <b>Eastern Standard Time</b>.<br/>"
                      "  • Sets Organization to <b>CHCFC</b> and RegisteredOwner to <b>CHCFC IT</b>.<br/>"
                      "  • <b>Automated Active Directory Domain Join:</b><br/>"
                      "    - Domain: <code>inside.chcfc.org</code><br/>"
                      "    - Target OU: <code>OU=Staging,DC=inside,DC=chcfc,DC=org</code><br/>"
                      "    - Service Account: <code>svc-domainjoin</code> (Delegated machine join).", body_text)
        ],
        [
            Paragraph("<b>3. oobeSystem</b><br/><i>(First User Boot)</i>", body_text),
            Paragraph("<code>Microsoft-Windows-Shell-Setup</code><br/><code>(OOBE & Accounts)</code>", code_text),
            Paragraph("  • <b>Completely Hides All Out-of-Box Experience (OOBE) Screens:</b><br/>"
                      "    <code>HideEULAPage</code>, <code>HideLocalAccountScreen</code>, <code>HideOEMRegistrationScreen</code>,<br/>"
                      "    <code>HideOnlineAccountScreens</code>, <code>HideWirelessSetupInOOBE</code>, <code>ProtectYourPC=1</code>.<br/>"
                      "  • Sets NetworkLocation to <b>Work</b>.<br/>"
                      "  • <b>Local Administrator Account Provisioning:</b><br/>"
                      "    - Sets Built-in Administrator password to standard enterprise IT secret.<br/>"
                      "    - Provisions local IT admin account: <b><code>chcfcadmin</code></b> in Administrators group.", body_text)
        ]
    ]
    unattend_table = Table(unattend_table_data, colWidths=[95, 145, 300])
    unattend_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), LIGHT_BG),
        ('LINEBELOW', (0, 0), (-1, 0), 1, PRIMARY_TEAL),
        ('GRID', (0, 0), (-1, -1), 0.5, BORDER_COLOR),
        ('VALIGN', (0, 0), (-1, -1), 'TOP'),
        ('TOPPADDING', (0, 0), (-1, -1), 3),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 3),
        ('LEFTPADDING', (0, 0), (-1, -1), 5),
        ('RIGHTPADDING', (0, 0), (-1, -1), 5),
    ]))
    story.append(s5_title)
    story.append(unattend_table)
    story.append(Spacer(1, 7))

    # SECTION 6: Post-Install Pipeline & Technician Reference
    s6_title = Paragraph("6. First-Boot Software Pipeline (SetupComplete.cmd) & Quick Reference", sec_title)
    s6_content = [
        Paragraph("<b>Post-Install Automation (<code>C:\\Windows\\Setup\\Scripts\\SetupComplete.cmd</code>):</b><br/>"
                  "Upon initial Windows 11 boot, Windows executes <code>SetupComplete.cmd</code> under <code>NT AUTHORITY\\SYSTEM</code> before user logon:<br/>"
                  "  1. Mounts software distribution repository <code>\\\\g22.inside.chcfc.org\\SoftwareDeploy</code>.<br/>"
                  "  2. Deploys <b>Bitdefender Endpoint Security</b> silently (<code>/bdparams /silent</code>).<br/>"
                  "  3. Deploys <b>NinjaOne Greenfield 1 RMM Agent</b> silently (<code>msiexec /i ninja-gf1-auto.msi /qn /norestart</code>).<br/>"
                  "  4. Posts completion beacon to Deployment Monitor (marking node <b>100% Completed</b>) and notifies Zulip <b>#SoftwareInstall</b>.<br/>"
                  "  5. Disconnects software deploy share and unregisters temporary deployment staging files.", body_text)
    ]
    s6_box = Table([[s6_content]], colWidths=[540])
    s6_box.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), LIGHT_BG),
        ('BOX', (0, 0), (-1, -1), 0.5, BORDER_COLOR),
        ('TOPPADDING', (0, 0), (-1, -1), 4),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 4),
        ('LEFTPADDING', (0, 0), (-1, -1), 8),
        ('RIGHTPADDING', (0, 0), (-1, -1), 8),
    ]))

    ref_table_data = [
        [
            Paragraph("<b>PXE Service / URL</b>", body_bold),
            Paragraph("<b>Address / Location</b>", body_bold),
            Paragraph("<b>Credentials / Access Role</b>", body_bold)
        ],
        [
            Paragraph("<b>Deployment Monitor Web</b>", body_text),
            Paragraph("<code>http://10.77.1.220:8087/monitor</code>", code_text),
            Paragraph("Auto-refreshing dashboard; CLI: <code>pxe-monitor -w</code>", body_text)
        ],
        [
            Paragraph("<b>Win11 Samba Image Share</b>", body_text),
            Paragraph("<code>\\\\10.77.1.220\\win11</code>", code_text),
            Paragraph("Anonymous / Guest access, No password", body_text)
        ],
        [
            Paragraph("<b>Software Deployment Share</b>", body_text),
            Paragraph("<code>\\\\g22.inside.chcfc.org\\SoftwareDeploy</code>", code_text),
            Paragraph("Active Directory Domain Authenticated", body_text)
        ],
        [
            Paragraph("<b>Local Admin Fallback</b>", body_text),
            Paragraph("User: <b><code>chcfcadmin</code></b>", code_text),
            Paragraph("Standard Enterprise IT Secret (IT password vault)", body_text)
        ]
    ]
    ref_table = Table(ref_table_data, colWidths=[140, 200, 200])
    ref_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), LIGHT_BG),
        ('LINEBELOW', (0, 0), (-1, 0), 1, PRIMARY_TEAL),
        ('GRID', (0, 0), (-1, -1), 0.5, BORDER_COLOR),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('TOPPADDING', (0, 0), (-1, -1), 2.5),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 2.5),
        ('LEFTPADDING', (0, 0), (-1, -1), 5),
        ('RIGHTPADDING', (0, 0), (-1, -1), 5),
    ]))

    story.append(s6_title)
    story.append(s6_box)
    story.append(Spacer(1, 4))
    story.append(ref_table)

    # Build the document using NumberedCanvas
    doc.build(story, canvasmaker=NumberedCanvas)
    print(f"Successfully generated PDF: {PDF_OUTPUT_PATH}")

if __name__ == '__main__':
    build_pdf()
