"""Step definitions for application_container_coverage_r3.feature.

Targets remaining uncovered lines in
``cleveragents.application.container``:

  - Lines 117, 119: ``_build_stream_router`` function body
  - Lines 124, 126: ``_build_langgraph_bridge`` function body
  - Lines 131, 133: ``_build_route_bridge`` function body
  - Lines 138, 142: ``_lazy_register_registry_agents`` function body
  - Lines 386-391, 393: ``_build_skill_service`` ImportError fallback
  - Lines 851-857: ``get_container`` audit_event_subscriber exception
"""

from __future__ import annotations

import builtins
import sys
from typing import Any
from unittest.mock import MagicMock, patch

from behave import given, then, when

from cleveragents.application.container import (
    _build_langgraph_bridge,
    _build_route_bridge,
    _build_skill_service,
    _build_stream_router,
    _lazy_register_registry_agents,
    get_container,
    reset_container,
)

_IN_MEMORY_URL = "sqlite:///:memory:"


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


@given("acncov3 a clean container state")
def step_acncov3_clean_container(context: Any) -> None:
    """Reset the global container to avoid cross-test interference."""
    reset_container()


# -------------------------------------------------------------------
# _build_stream_router  (lines 117, 119)
# -------------------------------------------------------------------


@when("acncov3 I call _build_stream_router")
def step_acncov3_call_build_stream_router(context: Any) -> None:
    """Call _build_stream_router which lazily imports ReactiveStreamRouter."""
    try:
        context.acncov3_stream_router_result = _build_stream_router()
        context.acncov3_error = None
    except Exception as exc:
        context.acncov3_stream_router_result = None
        context.acncov3_error = exc


@then("acncov3 the result should be a ReactiveStreamRouter instance")
def step_acncov3_verify_stream_router(context: Any) -> None:
    """Assert the returned object is a ReactiveStreamRouter."""
    from cleveragents.reactive.stream_router import ReactiveStreamRouter

    assert context.acncov3_error is None, (
        f"Expected no error, got: {context.acncov3_error}"
    )
    result = context.acncov3_stream_router_result
    assert isinstance(result, ReactiveStreamRouter), (
        f"Expected ReactiveStreamRouter, got {type(result).__name__}"
    )


# -------------------------------------------------------------------
# _build_langgraph_bridge  (lines 124, 126)
# -------------------------------------------------------------------


@given("acncov3 a mock stream router")
def step_acncov3_mock_stream_router(context: Any) -> None:
    """Create a mock stream router for injection."""
    context.acncov3_mock_stream_router = MagicMock(
        name="MockReactiveStreamRouter",
    )


@when("acncov3 I call _build_langgraph_bridge with the mock stream router")
def step_acncov3_call_build_langgraph_bridge(context: Any) -> None:
    """Call _build_langgraph_bridge with the mock stream router."""
    try:
        context.acncov3_bridge_result = _build_langgraph_bridge(
            context.acncov3_mock_stream_router,
        )
        context.acncov3_error = None
    except Exception as exc:
        context.acncov3_bridge_result = None
        context.acncov3_error = exc


@then("acncov3 the result should be a RxPyLangGraphBridge instance")
def step_acncov3_verify_langgraph_bridge(context: Any) -> None:
    """Assert the returned object is a RxPyLangGraphBridge."""
    from cleveragents.langgraph.bridge import RxPyLangGraphBridge

    assert context.acncov3_error is None, (
        f"Expected no error, got: {context.acncov3_error}"
    )
    result = context.acncov3_bridge_result
    assert isinstance(result, RxPyLangGraphBridge), (
        f"Expected RxPyLangGraphBridge, got {type(result).__name__}"
    )


@then("acncov3 the bridge should reference the mock stream router")
def step_acncov3_verify_bridge_router_ref(context: Any) -> None:
    """Assert the bridge stores the stream router we injected."""
    bridge = context.acncov3_bridge_result
    assert bridge.stream_router is context.acncov3_mock_stream_router, (
        "Expected the bridge to reference the mock stream router"
    )


# -------------------------------------------------------------------
# _build_route_bridge  (lines 131, 133)
# -------------------------------------------------------------------


@given("acncov3 a mock agents registry")
def step_acncov3_mock_agents(context: Any) -> None:
    """Create a mock agents / actor registry for injection."""
    context.acncov3_mock_agents = MagicMock(name="MockActorRegistry")


@when("acncov3 I call _build_route_bridge with the mock stream router and agents")
def step_acncov3_call_build_route_bridge(context: Any) -> None:
    """Call _build_route_bridge with mock stream_router and agents."""
    try:
        context.acncov3_route_bridge_result = _build_route_bridge(
            context.acncov3_mock_stream_router,
            context.acncov3_mock_agents,
        )
        context.acncov3_error = None
    except Exception as exc:
        context.acncov3_route_bridge_result = None
        context.acncov3_error = exc


@then("acncov3 the result should be a RouteBridge instance")
def step_acncov3_verify_route_bridge(context: Any) -> None:
    """Assert the returned object is a RouteBridge."""
    from cleveragents.reactive.route_bridge import RouteBridge

    assert context.acncov3_error is None, (
        f"Expected no error, got: {context.acncov3_error}"
    )
    result = context.acncov3_route_bridge_result
    assert isinstance(result, RouteBridge), (
        f"Expected RouteBridge, got {type(result).__name__}"
    )


@then("acncov3 the route bridge should reference the mock stream router")
def step_acncov3_verify_route_bridge_router_ref(context: Any) -> None:
    """Assert the route bridge stores the stream router we injected."""
    bridge = context.acncov3_route_bridge_result
    assert bridge.stream_router is context.acncov3_mock_stream_router, (
        "Expected the route bridge to reference the mock stream router"
    )


# -------------------------------------------------------------------
# _lazy_register_registry_agents  (lines 138, 142)
# -------------------------------------------------------------------


@given("acncov3 a mock stream router for registration")
def step_acncov3_mock_stream_router_for_reg(context: Any) -> None:
    """Create a mock stream router for the registration call."""
    context.acncov3_reg_stream_router = MagicMock(
        name="MockStreamRouterForReg",
    )


@given("acncov3 a mock route bridge for registration")
def step_acncov3_mock_route_bridge_for_reg(context: Any) -> None:
    """Create a mock route bridge for the registration call."""
    context.acncov3_reg_route_bridge = MagicMock(name="MockRouteBridgeForReg")


@given("acncov3 a mock actor registry for registration")
def step_acncov3_mock_actor_registry_for_reg(context: Any) -> None:
    """Create a mock actor registry that returns an empty list."""
    mock_registry = MagicMock(name="MockActorRegistryForReg")
    mock_registry.list_actors.return_value = []
    context.acncov3_reg_actor_registry = mock_registry


@when("acncov3 I call _lazy_register_registry_agents")
def step_acncov3_call_lazy_register(context: Any) -> None:
    """Call _lazy_register_registry_agents with mock dependencies.

    We patch ``register_registry_agents`` in the adapter module so we
    can verify it was called without side-effects, while still
    exercising the lazy import path inside the container function.
    """
    patcher = patch(
        "cleveragents.application.reactive_registry_adapter.register_registry_agents",
    )
    mock_register = patcher.start()
    context.add_cleanup(patcher.stop)
    context.acncov3_mock_register_fn = mock_register

    try:
        context.acncov3_reg_result = _lazy_register_registry_agents(
            context.acncov3_reg_stream_router,
            context.acncov3_reg_route_bridge,
            context.acncov3_reg_actor_registry,
        )
        context.acncov3_error = None
    except Exception as exc:
        context.acncov3_reg_result = None
        context.acncov3_error = exc


@then("acncov3 register_registry_agents should have been invoked")
def step_acncov3_verify_register_called(context: Any) -> None:
    """Assert that register_registry_agents was called with our mocks."""
    assert context.acncov3_error is None, (
        f"Expected no error, got: {context.acncov3_error}"
    )
    context.acncov3_mock_register_fn.assert_called_once_with(
        context.acncov3_reg_stream_router,
        context.acncov3_reg_route_bridge,
        context.acncov3_reg_actor_registry,
    )


# -------------------------------------------------------------------
# _build_skill_service ImportError  (lines 386-391, 393)
# -------------------------------------------------------------------


@when("acncov3 I call _build_skill_service with SQLAlchemy import blocked")
def step_acncov3_skill_service_import_blocked(context: Any) -> None:
    """Force an ImportError for sqlalchemy inside _build_skill_service.

    We temporarily remove sqlalchemy entries from ``sys.modules`` and
    install a custom ``__import__`` that raises ``ImportError`` for
    sqlalchemy.  This exercises the except-ImportError fallback path
    (lines 386-393).
    """
    # 1. Save and remove all sqlalchemy entries from sys.modules
    saved_modules: dict[str, Any] = {}
    for key in list(sys.modules.keys()):
        if key == "sqlalchemy" or key.startswith("sqlalchemy."):
            saved_modules[key] = sys.modules.pop(key)

    # 2. Install a blocking __import__
    original_import = builtins.__import__

    def blocking_import(name: str, *args: Any, **kwargs: Any) -> Any:
        if name == "sqlalchemy" or name.startswith("sqlalchemy."):
            raise ImportError(f"acncov3-mock: No module named '{name}'")
        return original_import(name, *args, **kwargs)

    builtins.__import__ = blocking_import  # type: ignore[assignment]

    try:
        context.acncov3_skill_result = _build_skill_service(_IN_MEMORY_URL)
        context.acncov3_error = None
    except Exception as exc:
        context.acncov3_skill_result = None
        context.acncov3_error = exc
    finally:
        # 3. Restore original __import__ and sys.modules
        builtins.__import__ = original_import  # type: ignore[assignment]
        sys.modules.update(saved_modules)


@then("acncov3 the result should be an in-memory SkillService")
def step_acncov3_verify_inmemory_skill(context: Any) -> None:
    """Assert the returned object is a SkillService (in-memory fallback)."""
    from cleveragents.application.services.skill_service import SkillService

    assert context.acncov3_error is None, (
        f"Expected no error from _build_skill_service, got: {context.acncov3_error}"
    )
    result = context.acncov3_skill_result
    assert isinstance(result, SkillService), (
        f"Expected SkillService, got {type(result).__name__}"
    )


@then("acncov3 the in-memory SkillService should have no repository")
def step_acncov3_verify_skill_no_repo(context: Any) -> None:
    """Assert the fallback SkillService has no DB-backed repository."""
    assert context.acncov3_skill_result._skill_repo is None, (
        "Expected no SkillRepository for the ImportError fallback"
    )


# -------------------------------------------------------------------
# get_container audit_event_subscriber exception  (lines 851-857)
# -------------------------------------------------------------------


@given("acncov3 the container is reset")
def step_acncov3_container_reset(context: Any) -> None:
    """Ensure the global container is None so get_container creates fresh."""
    reset_container()


@given("acncov3 audit_event_subscriber is patched to raise an error")
def step_acncov3_patch_audit_subscriber(context: Any) -> None:
    """Patch AuditEventSubscriber.__init__ to raise RuntimeError.

    When get_container() calls ``_container.audit_event_subscriber()``
    the Singleton provider tries to construct AuditEventSubscriber.
    Making its ``__init__`` raise exercises the except branch (lines 851-857).
    """
    patcher = patch(
        "cleveragents.application.services.audit_event_subscriber"
        ".AuditEventSubscriber.__init__",
        side_effect=RuntimeError("acncov3-mock: DB not initialised"),
    )
    patcher.start()
    context.add_cleanup(patcher.stop)


@when("acncov3 I call get_container")
def step_acncov3_call_get_container(context: Any) -> None:
    """Call get_container which should catch the audit subscriber error."""
    try:
        context.acncov3_container = get_container()
        context.acncov3_error = None
    except Exception as exc:
        context.acncov3_container = None
        context.acncov3_error = exc


@then("acncov3 a valid container should be returned despite the error")
def step_acncov3_verify_container_returned(context: Any) -> None:
    """Assert get_container returned a Container even though the subscriber failed.

    dependency-injector's ``DeclarativeContainer()`` returns a
    ``DynamicContainer`` at runtime, so we check against the base
    ``containers.Container`` type.
    """
    from dependency_injector import containers as di_containers

    assert context.acncov3_error is None, (
        f"Expected no error from get_container, got: {context.acncov3_error}"
    )
    assert context.acncov3_container is not None, (
        "Expected a container instance, got None"
    )
    assert isinstance(context.acncov3_container, di_containers.Container), (
        f"Expected a DI Container, got {type(context.acncov3_container).__name__}"
    )
    # Verify it has the expected providers
    assert hasattr(context.acncov3_container, "settings"), (
        "Container should have a 'settings' provider"
    )
    assert hasattr(context.acncov3_container, "audit_event_subscriber"), (
        "Container should have an 'audit_event_subscriber' provider"
    )


@then("acncov3 the container should be cached as the global singleton")
def step_acncov3_verify_container_cached(context: Any) -> None:
    """Assert the returned container is cached (second call returns same)."""
    second_call = get_container()
    assert second_call is context.acncov3_container, (
        "Expected the same container instance on second call (singleton)"
    )
    # Clean up: reset for other tests
    reset_container()
