"""Unit tests for script_generator.py — SCRPT-01, SCRPT-02, SCRPT-03, SCRPT-04, SCRPT-05."""
import pytest
from imptune.generators.script_generator import render_detect, render_install, render_uninstall


def _create_test_driver_and_printer():
    """Helper: create a Driver + Printer for integration tests."""
    from imptune.db.models import Driver, Printer

    driver = Driver.create(
        sha256="abc123",
        original_filename="test.zip",
        size_bytes=100,
        driver_desc='["Test Driver"]',
        inf_filename="test.inf",
    )
    printer = Printer.create(
        name="Test Printer",
        ip_address="10.0.0.1",
        port_name="IP_10.0.0.1",
        driver=driver,
        duplex_mode="LongEdge",
        color_mode=True,
        paper_size="A4",
        collate=True,
    )
    return driver, printer


SAMPLE_ARGS = dict(
    printer_name="HP LaserJet 4000",
    ip_address="192.168.1.10",
    port_name="IP_192.168.1.10",
    driver_name="HP Universal Printing PCL 6",
    inf_filename="hpcu270u.inf",
    duplex_mode="LongEdge",
    color_mode=True,
    paper_size="A4",
    collate=True,
)


def test_render_install_returns_string():
    """render_install returns a non-empty string."""
    result = render_install(**SAMPLE_ARGS)
    assert isinstance(result, str)
    assert len(result) > 0


def test_render_install_contains_pnputil():
    """SCRPT-01: Output contains pnputil /add-driver and Add-PrinterDriver."""
    result = render_install(**SAMPLE_ARGS)
    assert "pnputil.exe /add-driver" in result
    assert "Add-PrinterDriver" in result
    assert "hpcu270u.inf" in result
    assert "HP Universal Printing PCL 6" in result


def test_render_install_print_config():
    """SCRPT-01: Set-PrintConfiguration with translated duplex mode."""
    # LongEdge -> TwoSidedLongEdge
    result = render_install(**SAMPLE_ARGS)
    assert "Set-PrintConfiguration" in result
    assert "TwoSidedLongEdge" in result
    assert "A4" in result

    # ShortEdge -> TwoSidedShortEdge
    result_short = render_install(**{**SAMPLE_ARGS, "duplex_mode": "ShortEdge"})
    assert "TwoSidedShortEdge" in result_short

    # OneSided -> OneSided (no change)
    result_one = render_install(**{**SAMPLE_ARGS, "duplex_mode": "OneSided"})
    assert "OneSided" in result_one
    # Must NOT contain the two-sided variants when OneSided
    assert "TwoSided" not in result_one


def test_render_install_wow64_guard():
    """SCRPT-05: WOW64 relaunch guard is present in the output."""
    result = render_install(**SAMPLE_ARGS)
    assert "PROCESSOR_ARCHITECTURE" in result
    assert "SysNative" in result
    # Guard must appear near the top (before pnputil)
    wow64_pos = result.find("PROCESSOR_ARCHITECTURE")
    pnputil_pos = result.find("pnputil.exe")
    assert wow64_pos < pnputil_pos, "WOW64 guard must appear before pnputil"


def test_render_install_uac_guard():
    """SCRPT-04: SYSTEM vs user detection with UAC self-elevation."""
    result = render_install(**SAMPLE_ARGS)
    assert "IsSystem" in result
    assert "Start-Process" in result
    assert "-Verb Runas" in result or "Verb Runas" in result


def test_render_install_idempotency():
    """SCRPT-01: All add operations wrapped in idempotency checks."""
    result = render_install(**SAMPLE_ARGS)
    # Port idempotency
    assert "Get-PrinterPort" in result
    assert "Add-PrinterPort" in result
    # Printer idempotency
    assert "Get-Printer" in result
    assert "Add-Printer" in result
    # Port check must come before Add-PrinterPort
    get_port_pos = result.find("Get-PrinterPort")
    add_port_pos = result.find("Add-PrinterPort")
    assert get_port_pos < add_port_pos, "Get-PrinterPort check must precede Add-PrinterPort"


def test_render_install_booleans():
    """Color and collate are rendered as $true / $false in the script."""
    result_true = render_install(**SAMPLE_ARGS)  # color_mode=True, collate=True
    assert "$true" in result_true

    result_false = render_install(**{**SAMPLE_ARGS, "color_mode": False, "collate": False})
    assert "$false" in result_false


def test_render_uninstall():
    """SCRPT-02: render_uninstall produces correct removal order with SilentlyContinue."""
    result = render_uninstall("Test Printer", "HP Driver", "IP_10.0.0.1")
    # All three Remove-* commands must be present
    assert 'Remove-Printer -Name "Test Printer"' in result
    assert 'Remove-PrinterDriver -Name "HP Driver"' in result
    assert 'Remove-PrinterPort -Name "IP_10.0.0.1"' in result
    # All must have -ErrorAction SilentlyContinue
    assert result.count("-ErrorAction SilentlyContinue") >= 3
    # Order: Remove-Printer BEFORE Remove-PrinterDriver (driver removal fails if printer still active)
    printer_pos = result.find("Remove-Printer -Name")
    driver_pos = result.find("Remove-PrinterDriver -Name")
    port_pos = result.find("Remove-PrinterPort -Name")
    assert printer_pos < driver_pos, "Remove-Printer must appear before Remove-PrinterDriver"
    assert driver_pos < port_pos, "Remove-PrinterDriver must appear before Remove-PrinterPort"


def test_render_detect():
    """SCRPT-03: render_detect follows Intune detection contract (Write-Output + exit 0/1)."""
    result = render_detect("Test Printer")
    assert 'Get-Printer -Name "Test Printer"' in result
    assert "Write-Output" in result
    assert "exit 0" in result
    assert "exit 1" in result


# ---------------------------------------------------------------------------
# Integration tests — script API endpoints
# ---------------------------------------------------------------------------


def test_install_endpoint(client):
    """GET /printers/{id}/scripts/install returns 200 with pnputil in content."""
    _driver, printer = _create_test_driver_and_printer()
    response = client.get(f"/printers/{printer.id}/scripts/install")
    assert response.status_code == 200
    assert "pnputil" in response.text


def test_uninstall_endpoint(client):
    """GET /printers/{id}/scripts/uninstall returns 200 with Remove-Printer in content."""
    _driver, printer = _create_test_driver_and_printer()
    response = client.get(f"/printers/{printer.id}/scripts/uninstall")
    assert response.status_code == 200
    assert "Remove-Printer" in response.text


def test_detect_endpoint(client):
    """GET /printers/{id}/scripts/detect returns 200 with Write-Output in content."""
    _driver, printer = _create_test_driver_and_printer()
    response = client.get(f"/printers/{printer.id}/scripts/detect")
    assert response.status_code == 200
    assert "Write-Output" in response.text


def test_script_endpoint_missing_printer(client):
    """GET /printers/{id}/scripts/install returns 404 for nonexistent printer."""
    response = client.get("/printers/9999/scripts/install")
    assert response.status_code == 404


def test_script_endpoint_no_driver(client):
    """GET /printers/{id}/scripts/install returns 422 when no driver assigned."""
    from imptune.db.models import Printer

    printer = Printer.create(
        name="No Driver Printer",
        ip_address="10.0.0.2",
        port_name="IP_10.0.0.2",
        driver=None,
        duplex_mode="OneSided",
        color_mode=True,
        paper_size="A4",
        collate=True,
    )
    response = client.get(f"/printers/{printer.id}/scripts/install")
    assert response.status_code == 422
