"""Step definitions for secure template rendering tests."""

from __future__ import annotations

from behave import given, then, when
from behave.runner import Context

from cleveragents.templates.renderer import TemplateRenderer
from cleveragents.templates.secure_renderer import (
    SecureTemplateRenderer,
    TemplateConfig,
    TemplateError,
    TemplateSecurityError,
    TemplateSizeError,
    TemplateValidationError,
    render_template_secure,
    validate_template,
)

__all__: list[str] = []


# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------


@given("a secure template test environment")
def step_given_secure_template_env(context: Context) -> None:
    context.sec_template = ""
    context.sec_context = {}  # dict[str, str]
    context.sec_rendered = ""
    context.sec_error = None  # TemplateError | None
    context.sec_validation_errors = []  # list[str]
    context.sec_config = None  # TemplateConfig | None
    context.sec_allowed_keys = None  # frozenset[str] | None
    context.sec_renderer = None  # SecureTemplateRenderer | None


# ---------------------------------------------------------------------------
# Given steps — template text
# ---------------------------------------------------------------------------


@given('the secure template text "{template}"')
def step_given_template(context: Context, template: str) -> None:
    context.sec_template = template


@given('a secure template context key "{key}" with value "{value}"')
def step_given_context_key(context: Context, key: str, value: str) -> None:
    context.sec_context[key] = value


@given('a secure template with allowed keys "{keys}"')
def step_given_allowed_keys(context: Context, keys: str) -> None:
    context.sec_allowed_keys = frozenset(k.strip() for k in keys.split(","))


@given("the secure template has no key restrictions")
def step_given_template_open(context: Context) -> None:
    context.sec_allowed_keys = None


@given("a secure template that is {n} characters long")
def step_given_long_template(context: Context, n: str) -> None:
    context.sec_template = "x" * int(n)


@given("a secure template config with max output length of {n}")
def step_given_max_output_config(context: Context, n: str) -> None:
    context.sec_config = TemplateConfig(max_output_length=int(n))


@given("a secure template config with logging disabled")
def step_given_config_logging_disabled(context: Context) -> None:
    context.sec_config = TemplateConfig(
        allowed_keys=context.sec_allowed_keys or frozenset({"name"}),
        log_rejected=False,
    )


@given("a secure template config that permits unknown keys")
def step_given_config_permit_unknown(context: Context) -> None:
    context.sec_config = TemplateConfig(
        allowed_keys=frozenset({"name"}),
        reject_unknown_keys=False,
    )


# ---------------------------------------------------------------------------
# When steps
# ---------------------------------------------------------------------------


@when("I render the secure template")
def step_when_render_securely(context: Context) -> None:
    config = context.sec_config
    if config is None and context.sec_allowed_keys is not None:
        config = TemplateConfig(allowed_keys=context.sec_allowed_keys)
    renderer = SecureTemplateRenderer(config=config)
    context.sec_rendered = renderer.render(context.sec_template, context.sec_context)


@when("I render the secure template expecting a security error")
def step_when_render_security_error(context: Context) -> None:
    config = context.sec_config or TemplateConfig()
    renderer = SecureTemplateRenderer(config=config)
    try:
        renderer.render(context.sec_template, context.sec_context)
        context.sec_error = None
    except TemplateError as exc:
        assert isinstance(exc, TemplateSecurityError), (
            f"Expected TemplateSecurityError, got {type(exc).__name__}: {exc}"
        )
        context.sec_error = exc


@when("I render the secure template expecting a size error")
def step_when_render_size_error(context: Context) -> None:
    config = context.sec_config or TemplateConfig()
    renderer = SecureTemplateRenderer(config=config)
    try:
        renderer.render(context.sec_template, context.sec_context)
        context.sec_error = None
    except TemplateError as exc:
        assert isinstance(exc, TemplateSizeError), (
            f"Expected TemplateSizeError, got {type(exc).__name__}: {exc}"
        )
        context.sec_error = exc


@when("I render the secure template expecting a validation error")
def step_when_render_validation_error(context: Context) -> None:
    config = context.sec_config
    if config is None and context.sec_allowed_keys is not None:
        config = TemplateConfig(allowed_keys=context.sec_allowed_keys)
    elif config is None:
        config = TemplateConfig()
    renderer = SecureTemplateRenderer(config=config)
    try:
        renderer.render(context.sec_template, context.sec_context)
        context.sec_error = None
    except TemplateError as exc:
        assert isinstance(exc, TemplateValidationError), (
            f"Expected TemplateValidationError, got {type(exc).__name__}: {exc}"
        )
        context.sec_error = exc


@when("I validate the secure template")
def step_when_validate_template(context: Context) -> None:
    allowed = context.sec_allowed_keys or frozenset()
    context.sec_validation_errors = validate_template(
        context.sec_template, allowed_keys=allowed
    )


@when("I render via the secure convenience function")
def step_when_render_convenience(context: Context) -> None:
    context.sec_rendered = render_template_secure(
        context.sec_template, context.sec_context
    )


@when("I render via the secure convenience function expecting an error")
def step_when_render_convenience_error(context: Context) -> None:
    try:
        render_template_secure(context.sec_template, context.sec_context)
        context.sec_error = None
    except TemplateError as exc:
        context.sec_error = exc


@when("I create a renderer with those settings")
def step_when_create_renderer(context: Context) -> None:
    config = context.sec_config
    if config is None and context.sec_allowed_keys is not None:
        config = TemplateConfig(allowed_keys=context.sec_allowed_keys)
    context.sec_renderer = SecureTemplateRenderer(config=config)


@when("I render via the legacy TemplateRenderer")
def step_when_render_legacy(context: Context) -> None:
    renderer = TemplateRenderer()
    context.sec_rendered = renderer.render(context.sec_template, context.sec_context)


@when("I render via the legacy TemplateRenderer with config")
def step_when_render_legacy_with_config(context: Context) -> None:
    config = context.sec_config
    if config is None and context.sec_allowed_keys is not None:
        config = TemplateConfig(allowed_keys=context.sec_allowed_keys)
    renderer = TemplateRenderer(config=config)
    context.sec_rendered = renderer.render(context.sec_template, context.sec_context)


# ---------------------------------------------------------------------------
# Then steps
# ---------------------------------------------------------------------------


@then('the secure template output should be "{expected}"')
def step_then_rendered_output(context: Context, expected: str) -> None:
    assert context.sec_rendered == expected, (
        f"Expected {expected!r}, got {context.sec_rendered!r}"
    )


@then('the secure template error should mention "{text}"')
def step_then_error_mentions(context: Context, text: str) -> None:
    assert context.sec_error is not None, "Expected a template error"
    assert text in str(context.sec_error), (
        f"Expected '{text}' in error: {context.sec_error}"
    )


@then("the secure template validation should be empty")
def step_then_validation_empty(context: Context) -> None:
    assert context.sec_validation_errors == [], (
        f"Expected no errors, got: {context.sec_validation_errors}"
    )


@then("the secure template validation should not be empty")
def step_then_validation_not_empty(context: Context) -> None:
    assert len(context.sec_validation_errors) > 0, (
        "Expected validation errors but got none"
    )


@then('the secure template validation should mention "{text}"')
def step_then_validation_mentions(context: Context, text: str) -> None:
    combined = " ".join(context.sec_validation_errors)
    assert text in combined, f"Expected '{text}' in: {context.sec_validation_errors}"


@then('the renderer config allowed keys should contain "{key}"')
def step_then_config_has_key(context: Context, key: str) -> None:
    cfg = context.sec_renderer.config  # exercises the .config property (line 155)
    assert key in cfg.allowed_keys, (
        f"Expected '{key}' in allowed_keys, got: {cfg.allowed_keys}"
    )
