"""Step definitions for automation_profile_cli_coverage_boost_r2.feature.

These scenarios validate that ``automation_profile._get_service()`` delegates
to the DI container's ``automation_profile_service`` provider.
"""

from __future__ import annotations

import contextlib
from unittest.mock import MagicMock, patch

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

from cleveragents.application.services.automation_profile_service import (
    AutomationProfileService,
)
from cleveragents.cli.commands import automation_profile as ap_mod
from cleveragents.infrastructure.database.repositories import (
    AutomationProfileRepository,
)

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


@given("the automation profile CLI module is imported for r2 coverage")
def step_import_ap_module(context: Context) -> None:
    """Ensure the automation_profile CLI module is importable."""
    assert ap_mod is not None
    assert ap_mod._get_service is not None


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_mock_container(service: AutomationProfileService) -> MagicMock:
    """Return a mock container whose provider returns *service*."""
    mock_container = MagicMock(name="container")
    mock_container.automation_profile_service.return_value = service
    return mock_container


def _ensure_tables(database_url: str) -> None:
    """Create all ORM tables in the given database (usually in-memory SQLite)."""
    from sqlalchemy import create_engine

    from cleveragents.infrastructure.database.models import Base

    engine = create_engine(database_url, echo=False)
    Base.metadata.create_all(engine)


@given("a mock container returning an automation profile service instance")
def step_mock_container_service(context: Context) -> None:
    context.r2_service_instance = AutomationProfileService()
    context.r2_mock_container = _make_mock_container(context.r2_service_instance)


@given("a mock container returning a working db-backed automation profile service")
def step_mock_container_working_db_service(context: Context) -> None:
    # Use a file-based temp db so the tables persist across connections
    import os
    import tempfile

    fd, path = tempfile.mkstemp(suffix=".db")
    os.close(fd)
    db_url = f"sqlite:///{path}"
    _ensure_tables(db_url)
    context.r2_temp_db_path = path

    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker

    from cleveragents.infrastructure.database.repositories import (
        AutomationProfileRepository,
    )

    engine = create_engine(db_url, echo=False)
    session_factory = sessionmaker(bind=engine, expire_on_commit=False)
    repo = AutomationProfileRepository(
        session_factory=session_factory, auto_commit=True
    )
    context.r2_service_instance = AutomationProfileService(repo=repo)
    context.r2_mock_container = _make_mock_container(context.r2_service_instance)

    def cleanup():
        import contextlib

        with contextlib.suppress(OSError):
            os.unlink(path)

    context.add_cleanup(cleanup)


@when("I call _get_service directly")
def step_call_get_service(context: Context) -> None:
    # Patch at the source module because _get_service uses a local import
    with patch(
        "cleveragents.application.container.get_container",
        return_value=context.r2_mock_container,
    ):
        context.r2_service = ap_mod._get_service()


@when("I call _get_service directly without mocking container access")
def step_call_get_service_without_mock(context: Context) -> None:
    context.r2_service = ap_mod._get_service()


# ---------------------------------------------------------------------------
# Then: verify returned service
# ---------------------------------------------------------------------------


@then("the returned object should be an AutomationProfileService instance")
def step_verify_service_type(context: Context) -> None:
    assert isinstance(context.r2_service, AutomationProfileService), (
        f"Expected AutomationProfileService, got {type(context.r2_service)}"
    )


@then("the returned service should be the same object provided by the container")
def step_verify_same_service(context: Context) -> None:
    assert context.r2_service is context.r2_service_instance


@then("the container automation profile service provider should be called once")
def step_verify_provider_called_once(context: Context) -> None:
    context.r2_mock_container.automation_profile_service.assert_called_once_with()


@then("the container database_url provider should not be called")
def step_verify_database_url_not_called(context: Context) -> None:
    context.r2_mock_container.database_url.assert_not_called()


@then("the returned service should be able to list profiles without error")
def step_verify_service_list_profiles(context: Context) -> None:
    profiles = context.r2_service.list_profiles()
    # Should at least contain the built-in profiles
    assert isinstance(profiles, list)
    assert len(profiles) > 0, "Expected at least one built-in profile"


@given("a real DI container configured with a temporary sqlite database")
def step_real_container_configured(context: Context) -> None:
    import os
    import tempfile

    from dependency_injector import providers
    from sqlalchemy import create_engine

    from cleveragents.application.container import get_container, reset_container
    from cleveragents.infrastructure.database.models import (
        AutomationProfileModel,
        Base,
    )

    fd, path = tempfile.mkstemp(suffix=".db")
    os.close(fd)
    db_url = f"sqlite:///{path}"

    engine = create_engine(db_url, echo=False)
    Base.metadata.create_all(engine, tables=[AutomationProfileModel.__table__])

    reset_container()
    container = get_container()
    container.database_url.override(providers.Object(db_url))

    context.r2_temp_db_path = path
    context.r2_real_container = container

    def cleanup() -> None:
        reset_container()
        with contextlib.suppress(OSError):
            os.unlink(path)

    context.add_cleanup(cleanup)


@then(
    "the resolved service should include a database-backed automation profile repository"
)
def step_verify_db_backed_repository(context: Context) -> None:
    repo = context.r2_service._repo
    assert isinstance(repo, AutomationProfileRepository)
