# pyrightconfig: reportRedeclaration=false
"""Step definitions for _compute_actor_impact error handling feature."""

from __future__ import annotations

import logging
from typing import Any
from unittest.mock import MagicMock, patch

from behave import given, then, when


@given("the session service raises an exception when listing sessions")
def step_session_service_raises(context: Any) -> None:
    """Configure session service to raise an exception."""
    context.session_raises = RuntimeError("DB unavailable")
    context.plan_raises = None
    context.action_raises = None
    context.session_count_override = None
    context.plan_count_override = None
    context.action_count_override = None


@given("the unit of work raises an exception when listing plans")
def step_uow_raises(context: Any) -> None:
    """Configure unit of work to raise an exception."""
    context.session_raises = None
    context.plan_raises = RuntimeError("DB unavailable")
    context.action_raises = None
    context.session_count_override = 0
    context.plan_count_override = None
    context.action_count_override = None


@given("the plan lifecycle service raises an exception when listing actions")
def step_lifecycle_service_raises(context: Any) -> None:
    """Configure plan lifecycle service to raise an exception."""
    context.session_raises = None
    context.plan_raises = None
    context.action_raises = RuntimeError("DB unavailable")
    context.session_count_override = 0
    context.plan_count_override = 0
    context.action_count_override = None


@given('the session service returns {count:d} sessions for actor "{actor_name}"')
def step_session_service_returns(context: Any, count: int, actor_name: str) -> None:
    """Configure session service to return a specific count."""
    context.session_raises = None
    context.plan_raises = None
    context.action_raises = None
    context.session_count_override = count
    context.plan_count_override = None
    context.action_count_override = None


@given('the unit of work returns {count:d} active plan for actor "{actor_name}"')
def step_uow_returns_plan(context: Any, count: int, actor_name: str) -> None:
    """Configure unit of work to return a specific active plan count."""
    context.plan_count_override = count


@given('the plan lifecycle service returns {count:d} actions for actor "{actor_name}"')
def step_lifecycle_returns_actions(context: Any, count: int, actor_name: str) -> None:
    """Configure plan lifecycle service to return a specific action count."""
    context.action_count_override = count


@given('the session service raises a RuntimeError with message "{message}"')
def step_session_raises_with_message(context: Any, message: str) -> None:
    """Configure session service to raise a RuntimeError with a specific message."""
    context.session_raises = RuntimeError(message)
    context.plan_raises = None
    context.action_raises = None
    context.session_count_override = None
    context.plan_count_override = None
    context.action_count_override = None


@when('I call _compute_actor_impact for actor "{actor_name}"')
def step_call_compute_actor_impact(context: Any, actor_name: str) -> None:
    """Call _compute_actor_impact with mocked services and capture log output."""
    from cleveragents.cli.commands.actor import _compute_actor_impact

    session_raises = getattr(context, "session_raises", None)
    plan_raises = getattr(context, "plan_raises", None)
    action_raises = getattr(context, "action_raises", None)
    session_count_override = getattr(context, "session_count_override", 0)
    plan_count_override = getattr(context, "plan_count_override", 0)
    action_count_override = getattr(context, "action_count_override", 0)

    # Build mock container
    mock_container = MagicMock()

    # Configure session service
    if session_raises is not None:
        mock_container.session_service.return_value.list.side_effect = session_raises
    else:
        count = session_count_override or 0
        mock_sessions = [MagicMock(actor_name=actor_name) for _ in range(count)]
        mock_container.session_service.return_value.list.return_value = mock_sessions

    # Configure unit of work / plans
    if plan_raises is not None:
        mock_uow = MagicMock()
        mock_uow.transaction.side_effect = plan_raises
        mock_container.unit_of_work.return_value = mock_uow
    else:
        count = plan_count_override or 0
        mock_plans = []
        for _ in range(count):
            p = MagicMock()
            p.processing_state = "queued"
            p.strategy_actor = actor_name
            p.execution_actor = None
            mock_plans.append(p)
        mock_ctx = MagicMock()
        mock_ctx.lifecycle_plans.list_plans.return_value = mock_plans
        mock_uow = MagicMock()
        mock_uow.transaction.return_value.__enter__ = MagicMock(return_value=mock_ctx)
        mock_uow.transaction.return_value.__exit__ = MagicMock(return_value=False)
        mock_container.unit_of_work.return_value = mock_uow

    # Configure plan lifecycle service / actions
    if action_raises is not None:
        mock_container.plan_lifecycle_service.return_value.list_actions.side_effect = (
            action_raises
        )
    else:
        count = action_count_override or 0
        mock_actions = []
        for _ in range(count):
            a = MagicMock()
            a.strategy_actor = actor_name
            a.execution_actor = None
            mock_actions.append(a)
        mock_container.plan_lifecycle_service.return_value.list_actions.return_value = (
            mock_actions
        )

    # Capture log records
    log_records: list[logging.LogRecord] = []

    class CapturingHandler(logging.Handler):
        def emit(self, record: logging.LogRecord) -> None:
            log_records.append(record)

    handler = CapturingHandler()
    actor_logger = logging.getLogger("cleveragents.cli.commands.actor")
    actor_logger.addHandler(handler)
    original_level = actor_logger.level
    actor_logger.setLevel(logging.WARNING)

    try:
        with patch(
            "cleveragents.cli.commands.actor.get_container",
            return_value=mock_container,
        ):
            context.impact_result = _compute_actor_impact(actor_name)
    finally:
        actor_logger.removeHandler(handler)
        actor_logger.setLevel(original_level)

    context.log_records = log_records


@then("the session count should be {count:d}")
def step_session_count(context: Any, count: int) -> None:
    """Assert the session count in the result."""
    assert context.impact_result is not None, "impact_result was not set"
    actual = context.impact_result[0]
    assert actual == count, f"Expected session count {count}, got {actual}"


@then("the active plan count should be {count:d}")
def step_active_plan_count(context: Any, count: int) -> None:
    """Assert the active plan count in the result."""
    assert context.impact_result is not None, "impact_result was not set"
    actual = context.impact_result[1]
    assert actual == count, f"Expected active plan count {count}, got {actual}"


@then("the action count should be {count:d}")
def step_action_count(context: Any, count: int) -> None:
    """Assert the action count in the result."""
    assert context.impact_result is not None, "impact_result was not set"
    actual = context.impact_result[2]
    assert actual == count, f"Expected action count {count}, got {actual}"


@then('a WARNING log message should contain "{text}"')
def step_warning_log_contains(context: Any, text: str) -> None:
    """Assert that at least one WARNING log message contains the given text."""
    warning_messages = [
        r.getMessage() for r in context.log_records if r.levelno >= logging.WARNING
    ]
    assert any(text in msg for msg in warning_messages), (
        f"Expected a WARNING log message containing {text!r}, "
        f"but got: {warning_messages}"
    )


@then("no WARNING log messages should be emitted")
def step_no_warning_logs(context: Any) -> None:
    """Assert that no WARNING log messages were emitted."""
    warning_messages = [
        r.getMessage() for r in context.log_records if r.levelno >= logging.WARNING
    ]
    assert not warning_messages, (
        f"Expected no WARNING log messages, but got: {warning_messages}"
    )
