"""Step definitions targeting uncovered lines in stream_router.py:
- SEC1: SimpleToolAgent.process() unconditionally rejects code blocks
- Lines 213-214: SimpleLLMAgent._render_prompt() when _template_env is None
- Lines 219-220: SimpleLLMAgent._render_prompt() when rendering raises
"""

from __future__ import annotations

from unittest.mock import MagicMock

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

from cleveragents.core.exceptions import StreamRoutingError
from cleveragents.reactive.stream_router import SimpleLLMAgent, SimpleToolAgent

# ---------------------------------------------------------------------------
# SimpleToolAgent code block rejection (SEC1 — exec fully removed)
# ---------------------------------------------------------------------------


@given("a stream router unsafe tool agent with a valid code block")
def step_given_unsafe_agent_valid_code(context: Context) -> None:
    context.unsafe_agent = SimpleToolAgent(
        tools=[{"code": "result = str(input_data) + '_processed'"}],
        unsafe=True,
    )


@given("a stream router unsafe tool agent with a failing code block")
def step_given_unsafe_agent_failing_code(context: Context) -> None:
    context.unsafe_agent = SimpleToolAgent(
        tools=[{"code": "raise ValueError('boom')"}],
        unsafe=True,
    )


@given("a stream router safe tool agent with a code block")
def step_given_safe_agent_code_block(context: Context) -> None:
    context.unsafe_agent = SimpleToolAgent(
        tools=[{"code": "result = 'should not run'"}],
        unsafe=False,
    )


@when("the stream router unsafe agent processes input expecting rejection")
def step_when_unsafe_agent_processes_expecting_rejection(
    context: Context,
) -> None:
    context.unsafe_error = None
    try:
        context.unsafe_agent.process("test")
    except Exception as exc:  # pylint: disable=broad-except
        context.unsafe_error = exc


@when("the stream router safe agent processes input expecting rejection")
def step_when_safe_agent_processes_expecting_error(context: Context) -> None:
    context.unsafe_error = None
    try:
        context.unsafe_agent.process("test")
    except Exception as exc:  # pylint: disable=broad-except
        context.unsafe_error = exc


@then("the stream router unsafe agent should raise StreamRoutingError about code")
def step_then_unsafe_agent_raises_routing_error(context: Context) -> None:
    assert isinstance(context.unsafe_error, StreamRoutingError), (
        f"Expected StreamRoutingError, got {type(context.unsafe_error).__name__}: "
        f"{context.unsafe_error}"
    )
    assert "code" in str(context.unsafe_error).lower()


@then("the stream router safe agent should raise StreamRoutingError about code")
def step_then_safe_agent_raises_routing_error(context: Context) -> None:
    assert isinstance(context.unsafe_error, StreamRoutingError), (
        f"Expected StreamRoutingError, got {type(context.unsafe_error).__name__}: "
        f"{context.unsafe_error}"
    )
    assert "code" in str(context.unsafe_error).lower()


# ---------------------------------------------------------------------------
# SimpleLLMAgent._render_prompt - lines 213-214, 219-220
# ---------------------------------------------------------------------------


@given("a stream router llm agent with no template env")
def step_given_llm_agent_no_template_env(context: Context) -> None:
    context.llm_render_agent = SimpleLLMAgent("test_no_env", {})
    context.llm_render_agent._template_env = None


@given("a stream router llm agent with a failing template env")
def step_given_llm_agent_failing_template_env(context: Context) -> None:
    context.llm_render_agent = SimpleLLMAgent("test_fail_env", {})
    mock_env = MagicMock()
    mock_env.from_string.side_effect = RuntimeError("template rendering failed")
    context.llm_render_agent._template_env = mock_env


@given("a stream router llm agent for empty template test")
def step_given_llm_agent_empty_template(context: Context) -> None:
    context.llm_render_agent = SimpleLLMAgent("test_empty", {})


@when('the stream router llm agent renders "{template}" with context name "{name}"')
def step_when_llm_agent_renders_template(
    context: Context, template: str, name: str
) -> None:
    context.llm_render_result = context.llm_render_agent._render_prompt(
        template, {"name": name}
    )


@when("the stream router llm agent renders empty template")
def step_when_llm_agent_renders_empty(context: Context) -> None:
    context.llm_render_result = context.llm_render_agent._render_prompt(
        "", {"name": "World"}
    )


@then('the stream router llm render result should be "{expected}"')
def step_then_llm_render_result(context: Context, expected: str) -> None:
    assert context.llm_render_result == expected, (
        f"Expected '{expected}', got '{context.llm_render_result}'"
    )


@then("the stream router llm render result should be empty string")
def step_then_llm_render_result_empty(context: Context) -> None:
    assert context.llm_render_result == "", (
        f"Expected empty string, got '{context.llm_render_result}'"
    )


# ---------------------------------------------------------------------------
# SimpleToolAgent named operations - supplementary coverage
# ---------------------------------------------------------------------------


@given("a stream router tool agent with uppercase operation")
def step_given_tool_agent_uppercase(context: Context) -> None:
    context.op_agent = SimpleToolAgent(tools=[{"operation": "uppercase"}])


@given('a stream router tool agent with unknown operation "{op_name}"')
def step_given_tool_agent_unknown_op(context: Context, op_name: str) -> None:
    context.op_agent = SimpleToolAgent(tools=[{"operation": op_name}])


@when('the stream router tool agent processes "{content}" through operation')
def step_when_tool_agent_processes_operation(context: Context, content: str) -> None:
    context.op_result = context.op_agent.process(content)


@then('the stream router tool agent operation result should be "{expected}"')
def step_then_tool_agent_operation_result(context: Context, expected: str) -> None:
    assert context.op_result == expected, (
        f"Expected '{expected}', got '{context.op_result}'"
    )
