"""Tests for ScriptTemplateEngine — parameter substitution, sanitization, and filters."""
import pytest
from app.services.script_template_engine import ScriptTemplateEngine, ScriptRenderError


@pytest.fixture
def engine():
    return ScriptTemplateEngine()


# ── Basic substitution ────────────────────────────────────────────────────

def test_simple_substitution(engine):
    body = "New-ADUser -Name '{{ first_name }} {{ last_name }}'"
    result = engine.render(body, {"first_name": "John", "last_name": "Smith"})
    assert result == "New-ADUser -Name 'John Smith'"


def test_missing_required_param_raises(engine):
    body = "New-ADUser -Name '{{ first_name }}'"
    with pytest.raises(ScriptRenderError, match="first_name"):
        engine.render(body, {})


def test_extra_params_ignored(engine):
    body = "New-ADUser -Name '{{ first_name }}'"
    result = engine.render(body, {"first_name": "John", "extra": "ignored"})
    assert result == "New-ADUser -Name 'John'"


# ── Security: single-quote injection ─────────────────────────────────────

def test_single_quote_in_value_is_escaped(engine):
    body = "Set-ADUser -Name '{{ name }}'"
    result = engine.render(body, {"name": "O'Brien"})
    # Single quotes doubled for PowerShell safety
    assert "O''Brien" in result


def test_backtick_in_value_is_escaped(engine):
    body = "Write-Host '{{ msg }}'"
    result = engine.render(body, {"msg": "hello`world"})
    assert "`" not in result or "``" in result  # backtick is escaped


def test_dollar_sign_in_value_is_escaped(engine):
    body = "Write-Host '{{ msg }}'"
    result = engine.render(body, {"msg": "price is $100"})
    # Dollar sign escaped so it doesn't interpolate as a PowerShell variable
    assert "`$100" in result or "'price is $100'" in result


# ── Filters ──────────────────────────────────────────────────────────────

def test_as_secure_string_filter(engine):
    body = "$secPwd = {{ password | as_secure_string }}"
    result = engine.render(body, {"password": "MyP@ss123"})
    assert "ConvertTo-SecureString" in result
    assert "MyP@ss123" in result
    assert "-AsPlainText -Force" in result


def test_as_array_filter(engine):
    body = "$groups = @({{ groups | as_array }})"
    result = engine.render(body, {"groups": ["GroupA", "GroupB"]})
    assert "'GroupA','GroupB'" in result


def test_as_array_filter_single_item(engine):
    body = "$groups = @({{ groups | as_array }})"
    result = engine.render(body, {"groups": ["OnlyGroup"]})
    assert "'OnlyGroup'" in result


def test_as_bool_filter_true(engine):
    body = "$force = {{ force_change | as_bool }}"
    result = engine.render(body, {"force_change": True})
    assert "$true" in result


def test_as_bool_filter_false(engine):
    body = "$force = {{ force_change | as_bool }}"
    result = engine.render(body, {"force_change": False})
    assert "$false" in result


# ── Conditional blocks ───────────────────────────────────────────────────

def test_if_block_included_when_truthy(engine):
    body = "{% if groups %}\nAdd-Groups\n{% endif %}"
    result = engine.render(body, {"groups": ["GroupA"]})
    assert "Add-Groups" in result


def test_if_block_excluded_when_falsy(engine):
    body = "{% if groups %}\nAdd-Groups\n{% endif %}"
    result = engine.render(body, {"groups": []})
    assert "Add-Groups" not in result


def test_if_block_excluded_when_missing(engine):
    body = "{% if groups %}\nAdd-Groups\n{% endif %}"
    result = engine.render(body, {})
    assert "Add-Groups" not in result


# ── Parameter redaction ──────────────────────────────────────────────────

def test_sensitive_params_redacted_in_record(engine):
    params = {"first_name": "John", "password": "Secret123"}
    sensitive_keys = {"password"}
    redacted = engine.redact_sensitive(params, sensitive_keys)
    assert redacted["first_name"] == "John"
    assert redacted["password"] == "[REDACTED]"
