"""Step definitions for application_container_coverage_boost_r2.feature.

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

  - Lines 201-206: ``_build_repo_indexing_service``
  - Lines 213-218: ``_build_resource_registry_service``
  - Lines 221-230: ``_build_namespaced_project_repo``
  - Lines 269-278: ``_resolve_auto_reindex``
  - Lines 283-287: ``_build_analyzer_registry``
  - Lines 290-298: ``_build_resource_file_watcher``
  - Lines 316-357: ``_build_session_service``

All step text uses the ``r2cov-`` prefix to avoid collisions with
existing step definitions.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any
from unittest.mock import MagicMock, patch

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

from cleveragents.application.container import (
    _build_analyzer_registry,
    _build_namespaced_project_repo,
    _build_repo_indexing_service,
    _build_resource_file_watcher,
    _build_resource_registry_service,
    _build_session_service,
    _resolve_auto_reindex,
    reset_container,
)
from cleveragents.application.services.repo_indexing_service import RepoIndexingService
from cleveragents.application.services.resource_file_watcher import ResourceFileWatcher
from cleveragents.application.services.resource_registry_service import (
    ResourceRegistryService,
)
from cleveragents.application.services.session_service import PersistentSessionService
from cleveragents.domain.models.acms.analyzers import AnalyzerRegistry
from cleveragents.infrastructure.database.repositories import (
    NamespacedProjectRepository,
)

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


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


@given("a clean container state for r2 coverage tests")
def step_clean_container_r2(context: Context) -> None:
    """Reset the global container to avoid cross-test interference."""
    reset_container()


# -------------------------------------------------------------------
# _build_repo_indexing_service  (lines 201-206)
# -------------------------------------------------------------------


@when("r2cov- I build a repo indexing service with an in-memory database")
def step_build_repo_indexing_service(context: Context) -> None:
    """Call _build_repo_indexing_service with in-memory SQLite.

    We patch RepoIndexingService to avoid needing real DB tables
    (cleanup_stale_indexing runs in __init__), while still executing
    the engine/sessionmaker creation on lines 201-206.
    """
    with patch("cleveragents.application.container.RepoIndexingService") as mock_cls:
        # Make the mock constructor return a stub that remembers the factory
        def capture_factory(session_factory: Any) -> MagicMock:
            inst = MagicMock(spec=RepoIndexingService)
            inst._session_factory = session_factory
            inst._mock_marker = True
            return inst

        mock_cls.side_effect = capture_factory
        context.r2cov_repo_indexing_svc = _build_repo_indexing_service(_IN_MEMORY_URL)


@then("r2cov- the result should be a RepoIndexingService instance")
def step_verify_repo_indexing_svc(context: Context) -> None:
    """Assert a service-like object was returned (mocked to avoid DB)."""
    svc = context.r2cov_repo_indexing_svc
    assert svc is not None, "Expected a service instance, got None"
    assert hasattr(svc, "_session_factory"), "Service should have _session_factory"


@then("r2cov- the service should have a session factory")
def step_verify_repo_indexing_session_factory(context: Context) -> None:
    """Assert the session factory was passed to the service."""
    svc = context.r2cov_repo_indexing_svc
    assert svc._session_factory is not None, "Expected a session_factory, got None"
    assert callable(svc._session_factory), "session_factory should be callable"


# -------------------------------------------------------------------
# _build_resource_registry_service  (lines 213-218)
# -------------------------------------------------------------------


@when("r2cov- I build a resource registry service with an in-memory database")
def step_build_resource_registry_service(context: Context) -> None:
    """Call _build_resource_registry_service with in-memory SQLite.

    Patch ResourceRegistryService constructor to avoid DB table requirements.
    """
    with patch(
        "cleveragents.application.container.ResourceRegistryService"
    ) as mock_cls:

        def capture_factory(session_factory: Any) -> MagicMock:
            inst = MagicMock(spec=ResourceRegistryService)
            inst._session_factory = session_factory
            return inst

        mock_cls.side_effect = capture_factory
        context.r2cov_resource_registry_svc = _build_resource_registry_service(
            _IN_MEMORY_URL
        )


@then("r2cov- the result should be a ResourceRegistryService instance")
def step_verify_resource_registry_svc(context: Context) -> None:
    """Assert a service-like object was returned."""
    svc = context.r2cov_resource_registry_svc
    assert svc is not None, "Expected a service instance, got None"
    assert hasattr(svc, "_session_factory"), "Service should have _session_factory"


# -------------------------------------------------------------------
# _build_namespaced_project_repo  (lines 221-230)
# -------------------------------------------------------------------


@when("r2cov- I build a namespaced project repo with an in-memory database")
def step_build_namespaced_project_repo(context: Context) -> None:
    """Call _build_namespaced_project_repo with in-memory SQLite.

    Patch NamespacedProjectRepository constructor to avoid DB table requirements.
    """
    with patch(
        "cleveragents.application.container.NamespacedProjectRepository"
    ) as mock_cls:

        def capture_factory(session_factory: Any) -> MagicMock:
            inst = MagicMock(spec=NamespacedProjectRepository)
            inst._session_factory = session_factory
            return inst

        mock_cls.side_effect = capture_factory
        context.r2cov_namespaced_project_repo = _build_namespaced_project_repo(
            _IN_MEMORY_URL
        )


@then("r2cov- the result should be a NamespacedProjectRepository instance")
def step_verify_namespaced_project_repo(context: Context) -> None:
    """Assert a repo-like object was returned."""
    repo = context.r2cov_namespaced_project_repo
    assert repo is not None, "Expected a repo instance, got None"
    assert hasattr(repo, "_session_factory"), "Repo should have _session_factory"


# -------------------------------------------------------------------
# _resolve_auto_reindex  (lines 269-278)
# -------------------------------------------------------------------


@dataclass
class _FakeResolvedValue:
    """Minimal stand-in for ConfigService.resolve() return value."""

    value: Any


@given("r2cov- ConfigService resolve returns a truthy value for auto-reindex")
def step_config_resolve_truthy(context: Context) -> None:
    """Patch ConfigService so resolve returns a truthy value."""
    mock_svc = MagicMock()
    mock_svc.resolve.return_value = _FakeResolvedValue(value=True)
    context.r2cov_config_patcher = patch(
        "cleveragents.application.container.ConfigService",
        return_value=mock_svc,
    )
    # Import ConfigService lazily inside _resolve_auto_reindex, so we
    # need to patch it in the container module's import path.
    # The function does:
    #   from cleveragents.application.services.config_service import ConfigService
    # But since it's a local import, we patch the source module.
    context.r2cov_config_patcher = patch(
        "cleveragents.application.services.config_service.ConfigService",
        return_value=mock_svc,
    )
    context.r2cov_config_patcher.start()

    def cleanup() -> None:
        context.r2cov_config_patcher.stop()

    context.add_cleanup(cleanup)


@given("r2cov- ConfigService resolve returns None for auto-reindex")
def step_config_resolve_none(context: Context) -> None:
    """Patch ConfigService so resolve returns None value (defaults to True)."""
    mock_svc = MagicMock()
    mock_svc.resolve.return_value = _FakeResolvedValue(value=None)
    context.r2cov_config_patcher = patch(
        "cleveragents.application.services.config_service.ConfigService",
        return_value=mock_svc,
    )
    context.r2cov_config_patcher.start()

    def cleanup() -> None:
        context.r2cov_config_patcher.stop()

    context.add_cleanup(cleanup)


@given("r2cov- ConfigService resolve returns a falsy value for auto-reindex")
def step_config_resolve_falsy(context: Context) -> None:
    """Patch ConfigService so resolve returns a falsy (False) value."""
    mock_svc = MagicMock()
    mock_svc.resolve.return_value = _FakeResolvedValue(value=False)
    context.r2cov_config_patcher = patch(
        "cleveragents.application.services.config_service.ConfigService",
        return_value=mock_svc,
    )
    context.r2cov_config_patcher.start()

    def cleanup() -> None:
        context.r2cov_config_patcher.stop()

    context.add_cleanup(cleanup)


@given("r2cov- ConfigService raises an exception during resolve")
def step_config_resolve_exception(context: Context) -> None:
    """Patch ConfigService so its constructor raises, hitting the except branch."""
    context.r2cov_config_patcher = patch(
        "cleveragents.application.services.config_service.ConfigService",
        side_effect=RuntimeError("config unavailable"),
    )
    context.r2cov_config_patcher.start()

    def cleanup() -> None:
        context.r2cov_config_patcher.stop()

    context.add_cleanup(cleanup)


@when("r2cov- I call _resolve_auto_reindex")
def step_call_resolve_auto_reindex(context: Context) -> None:
    """Call _resolve_auto_reindex and capture the result."""
    context.r2cov_auto_reindex_result = _resolve_auto_reindex()


@then("r2cov- the result should be True")
def step_verify_result_true(context: Context) -> None:
    """Assert the result is True."""
    assert context.r2cov_auto_reindex_result is True, (
        f"Expected True, got {context.r2cov_auto_reindex_result!r}"
    )


@then("r2cov- the result should be False")
def step_verify_result_false(context: Context) -> None:
    """Assert the result is False."""
    assert context.r2cov_auto_reindex_result is False, (
        f"Expected False, got {context.r2cov_auto_reindex_result!r}"
    )


# -------------------------------------------------------------------
# _build_analyzer_registry  (lines 283-287)
# -------------------------------------------------------------------


@when("r2cov- I build an analyzer registry")
def step_build_analyzer_registry(context: Context) -> None:
    """Call _build_analyzer_registry directly."""
    context.r2cov_analyzer_registry = _build_analyzer_registry()


@then("r2cov- the result should be an AnalyzerRegistry instance")
def step_verify_analyzer_registry(context: Context) -> None:
    """Assert the returned object is an AnalyzerRegistry."""
    assert isinstance(context.r2cov_analyzer_registry, AnalyzerRegistry), (
        f"Expected AnalyzerRegistry, got "
        f"{type(context.r2cov_analyzer_registry).__name__}"
    )


@then("r2cov- the registry should have a PythonAnalyzer for .py files")
def step_verify_python_analyzer_registered(context: Context) -> None:
    """Assert a PythonAnalyzer is registered for .py extension."""
    from cleveragents.domain.models.acms.python_analyzer import PythonAnalyzer

    registry: AnalyzerRegistry = context.r2cov_analyzer_registry
    analyzer = registry.get_for_extension(".py")
    assert analyzer is not None, "Expected an analyzer for .py, got None"
    assert isinstance(analyzer, PythonAnalyzer), (
        f"Expected PythonAnalyzer, got {type(analyzer).__name__}"
    )


# -------------------------------------------------------------------
# _build_resource_file_watcher  (lines 290-298)
# -------------------------------------------------------------------


@given("r2cov- a mock event bus for the file watcher")
def step_mock_event_bus_for_watcher(context: Context) -> None:
    """Create a mock ReactiveEventBus for ResourceFileWatcher construction."""
    context.r2cov_mock_event_bus = MagicMock()


@when("r2cov- I build a resource file watcher with the mock event bus")
def step_build_resource_file_watcher(context: Context) -> None:
    """Call _build_resource_file_watcher with a mock event bus."""
    context.r2cov_file_watcher = _build_resource_file_watcher(
        event_bus=context.r2cov_mock_event_bus,
    )


@then("r2cov- the result should be a ResourceFileWatcher instance")
def step_verify_file_watcher(context: Context) -> None:
    """Assert the returned object is a ResourceFileWatcher."""
    assert isinstance(context.r2cov_file_watcher, ResourceFileWatcher), (
        f"Expected ResourceFileWatcher, got {type(context.r2cov_file_watcher).__name__}"
    )


@then("r2cov- the watcher should have auto_reindex enabled")
def step_verify_watcher_auto_reindex(context: Context) -> None:
    """Assert the watcher was constructed with auto_reindex=True."""
    watcher: ResourceFileWatcher = context.r2cov_file_watcher
    assert watcher._auto_reindex is True, (
        f"Expected auto_reindex=True, got {watcher._auto_reindex!r}"
    )


# -------------------------------------------------------------------
# _build_session_service  (lines 316-357)
# -------------------------------------------------------------------


@given("r2cov- a mock event bus for the session service")
def step_mock_event_bus_for_session(context: Context) -> None:
    """Create a mock ReactiveEventBus for session service construction."""
    context.r2cov_session_event_bus = MagicMock()


@when("r2cov- I build a session service with an in-memory database and no event bus")
def step_build_session_service_no_bus(context: Context) -> None:
    """Call _build_session_service with in-memory SQLite and no event bus."""
    context.r2cov_session_svc = _build_session_service(_IN_MEMORY_URL)


@when(
    "r2cov- I build a session service with an in-memory database and the mock event bus"
)
def step_build_session_service_with_bus(context: Context) -> None:
    """Call _build_session_service with in-memory SQLite and a mock event bus."""
    context.r2cov_session_svc = _build_session_service(
        _IN_MEMORY_URL, event_bus=context.r2cov_session_event_bus
    )


@then("r2cov- the result should be a PersistentSessionService instance")
def step_verify_session_svc(context: Context) -> None:
    """Assert the returned object is a PersistentSessionService."""
    assert isinstance(context.r2cov_session_svc, PersistentSessionService), (
        f"Expected PersistentSessionService, got "
        f"{type(context.r2cov_session_svc).__name__}"
    )
