---
phase: 04-script-generation
plan: 01
type: tdd
wave: 1
depends_on: []
files_modified:
  - imptune/generators/script_generator.py
  - imptune/templates/scripts/install.ps1.j2
  - tests/test_script_generator.py
autonomous: true
requirements:
  - SCRPT-01
  - SCRPT-04
  - SCRPT-05

must_haves:
  truths:
    - "render_install() produces a complete PowerShell script containing pnputil /add-driver, Add-PrinterPort, Add-PrinterDriver, Add-Printer, Set-PrintConfiguration"
    - "Generated install script contains WOW64 relaunch guard as the first executable block"
    - "Generated install script contains SYSTEM vs user detection with UAC self-elevation"
    - "Set-PrintConfiguration receives translated duplex values (TwoSidedLongEdge, TwoSidedShortEdge)"
    - "All add operations are wrapped in idempotency checks (Get-PrinterPort, Get-Printer)"
  artifacts:
    - path: "imptune/generators/script_generator.py"
      provides: "Jinja2 Environment + render_install function with duplex_map"
      exports: ["render_install"]
    - path: "imptune/templates/scripts/install.ps1.j2"
      provides: "PowerShell install template with WOW64, UAC, pnputil, idempotency"
      min_lines: 30
    - path: "tests/test_script_generator.py"
      provides: "Unit tests for SCRPT-01, SCRPT-04, SCRPT-05"
      min_lines: 40
  key_links:
    - from: "imptune/generators/script_generator.py"
      to: "imptune/templates/scripts/install.ps1.j2"
      via: "Jinja2 FileSystemLoader"
      pattern: "_env\\.get_template.*install"
    - from: "imptune/generators/script_generator.py"
      to: "imptune/db/models.py"
      via: "Printer model fields used as template vars"
      pattern: "printer\\.name|printer\\.ip_address|printer\\.port_name"
---

<objective>
Create the script generator module and install.ps1 Jinja2 template with full correctness guards.

Purpose: The install script is the most complex of the three scripts (WOW64, UAC, pnputil two-step, idempotency, duplex mapping). Building it first with TDD ensures all edge cases are covered before the simpler templates.

Output: `script_generator.py` with `render_install()`, `install.ps1.j2` template, and passing unit tests.
</objective>

<execution_context>
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/04-script-generation/04-RESEARCH.md

<interfaces>
<!-- Key types and contracts from existing codebase -->

From imptune/db/models.py:
```python
class Printer(BaseModel):
    name = CharField()
    ip_address = CharField()
    port_name = CharField()
    client = ForeignKeyField(Client, null=True, backref="printers")
    driver = ForeignKeyField(Driver, null=True, backref="printers")
    duplex_mode = CharField(default="OneSided")     # "OneSided" | "LongEdge" | "ShortEdge"
    color_mode = BooleanField(default=True)
    paper_size = CharField(default="A4")             # "A4" | "Letter" | "Legal"
    collate = BooleanField(default=True)

class Driver(BaseModel):
    sha256 = CharField(unique=True, index=True)
    original_filename = CharField()
    driver_desc = CharField(null=True)   # JSON list: '["HP Universal Printing PCL 6"]'
    inf_filename = CharField(null=True)  # e.g. "hpcu270u.inf"
```

From imptune/generators/intunewin_builder.py (pattern reference):
```python
# Existing generator module pattern — script_generator.py follows same structure
# Module-level setup, public render functions
```
</interfaces>
</context>

<tasks>

<feature>
  <name>Install script generator with TDD</name>
  <files>imptune/generators/script_generator.py, imptune/templates/scripts/install.ps1.j2, tests/test_script_generator.py</files>
  <behavior>
    - render_install(printer_name, ip_address, port_name, driver_name, inf_filename, duplex_mode, color_mode, paper_size, collate) returns a string containing valid PowerShell
    - Output contains WOW64 guard: `$env:PROCESSOR_ARCHITECTURE -eq "x86"` and `SysNative` relaunch as the FIRST executable block
    - Output contains SYSTEM/admin detection: `WindowsIdentity::GetCurrent()`, `IsSystem`, `IsInRole(Administrator)`, `Start-Process -Verb Runas`
    - Output contains pnputil two-step: `pnputil.exe /add-driver "$PSScriptRoot\drivers\{inf_filename}" /install` then `Add-PrinterDriver -Name "{driver_name}"`
    - Output contains idempotent port creation: `Get-PrinterPort` check before `Add-PrinterPort`
    - Output contains idempotent printer creation: `Get-Printer` check before `Add-Printer`
    - Output contains `Set-PrintConfiguration` with translated duplex: "LongEdge" -> "TwoSidedLongEdge", "ShortEdge" -> "TwoSidedShortEdge", "OneSided" -> "OneSided"
    - Output contains correct boolean rendering: color_mode=True -> `$true`, color_mode=False -> `$false`
    - Output contains paper size and collate values
    - Script uses proper quoting for printer name, port name, driver name (double-quoted in PS)
  </behavior>
  <implementation>
    **RED phase — write tests first in tests/test_script_generator.py:**

    1. Create `imptune/templates/scripts/` directory (empty, needed for Jinja2 loader)
    2. Write tests that import `render_install` from `imptune.generators.script_generator` and assert on rendered output:
       - `test_render_install_contains_pnputil`: assert `pnputil.exe /add-driver` and `Add-PrinterDriver` in output
       - `test_render_install_print_config`: assert `Set-PrintConfiguration` with `-DuplexingMode TwoSidedLongEdge` when duplex_mode="LongEdge"
       - `test_render_install_wow64_guard`: assert `PROCESSOR_ARCHITECTURE` and `SysNative` in output
       - `test_render_install_uac_guard`: assert `IsSystem` and `Start-Process` and `-Verb Runas` in output
       - `test_render_install_idempotency`: assert `Get-PrinterPort` and `Get-Printer` checks before add operations
       - `test_render_install_booleans`: assert `$true` / `$false` for color and collate
    3. Run tests — all MUST fail (RED)

    **GREEN phase — implement:**

    4. Create `imptune/generators/script_generator.py`:
       - Module-level Jinja2 Environment with `FileSystemLoader` pointing to `imptune/templates/scripts/`
       - `trim_blocks=True`, `lstrip_blocks=True`, `keep_trailing_newline=True`
       - `_duplex_map` dict: `{"OneSided": "OneSided", "LongEdge": "TwoSidedLongEdge", "ShortEdge": "TwoSidedShortEdge"}`
       - `render_install(printer_name, ip_address, port_name, driver_name, inf_filename, duplex_mode, color_mode, paper_size, collate) -> str`
         - Translates duplex_mode via `_duplex_map`
         - Converts color_mode/collate bools to `"true"` / `"false"` (lowercase, template adds `$` prefix)
         - Calls `_env.get_template("install.ps1.j2").render(...)` with all variables
       - Use plain string parameters (not ORM objects) so the function is testable without DB

    5. Create `imptune/templates/scripts/install.ps1.j2`:
       Template structure (in this exact order):
       ```
       # Header comment: Generated by ImpTune, printer name, install command hint
       # WOW64 Guard (FIRST executable block)
       if ($env:PROCESSOR_ARCHITECTURE -eq "x86" -and $env:PROCESSOR_ARCHITEW6432) { ... relaunch 64-bit ... exit }
       # SYSTEM/Admin check + UAC elevation
       $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
       $isSystem = $id.IsSystem
       $isAdmin = ... IsInRole(Administrator)
       if (-not $isSystem -and -not $isAdmin) { Start-Process -Verb Runas ... exit }
       # pnputil driver staging
       pnputil.exe /add-driver "$PSScriptRoot\drivers\{{ inf_filename }}" /install
       # Add-PrinterDriver
       Add-PrinterDriver -Name "{{ driver_name }}"
       # Idempotent port creation
       if (-not (Get-PrinterPort -Name "{{ port_name }}" -ErrorAction SilentlyContinue)) { Add-PrinterPort ... }
       # Idempotent printer creation
       if (-not (Get-Printer -Name "{{ printer_name }}" -ErrorAction SilentlyContinue)) { Add-Printer ... }
       # Set-PrintConfiguration
       Set-PrintConfiguration -PrinterName "{{ printer_name }}" -DuplexingMode {{ duplex_mode }} -Color ${{ color }} -PaperSize {{ paper_size }} -Collate ${{ collate }}
       ```

    6. Run tests — all MUST pass (GREEN)

    **Important notes:**
    - Use plain string args for render_install, NOT Printer ORM object — keeps tests DB-free
    - The `_duplex_map` must translate BEFORE passing to template (template receives already-mapped value)
    - Boolean values: pass as lowercase string `"true"` / `"false"` so template renders `$true` / `$false` with `${{ color }}`
    - Template must use `{{ }}` for all variable interpolation — no `{% set %}` for simple values
    - All PowerShell string parameters (printer_name, port_name, driver_name) must be double-quoted in the template
  </implementation>
</feature>

</tasks>

<verification>
```bash
python -m pytest tests/test_script_generator.py -x -q
```
All 6+ tests pass. Rendered install script contains all required blocks in correct order.
</verification>

<success_criteria>
- render_install() produces complete PowerShell install script
- WOW64 guard appears before any other logic
- UAC self-elevation skips when SYSTEM
- Duplex mode values correctly translated (LongEdge -> TwoSidedLongEdge)
- All add operations wrapped in idempotency checks
- All unit tests pass
</success_criteria>

<output>
After completion, create `.planning/phases/04-script-generation/04-01-SUMMARY.md`
</output>
