"""Step definitions for security_template_coverage_boost.feature.

Covers uncovered paths in skills/registry.py, cli/formatting.py,
cli/commands/system.py, cli/commands/session.py, cli/commands/cleanup.py,
cli/commands/audit.py, and application/container.py.

All step patterns use unique prefixes to avoid AmbiguousStep collisions.
"""

from __future__ import annotations

import contextlib
import os
import tempfile
from io import StringIO
from pathlib import Path
from typing import Any
from unittest.mock import MagicMock, patch

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

__all__: list[str] = []


# ===========================================================================
# Helpers
# ===========================================================================


def _make_mock_tool_registry(tools: dict[str, Any] | None = None) -> Any:
    """Return a mock tool registry that resolves tool names."""
    reg = MagicMock()
    tools = tools or {}

    def _get_tool(name: str) -> Any:
        return tools.get(name)

    reg.get_tool = _get_tool
    return reg


def _make_skill_definition(
    name: str = "test/skill",
    description: str = "A test skill",
    tool_refs: list[str] | None = None,
    inline_tools: list[Any] | None = None,
    includes: list[Any] | None = None,
) -> Any:
    """Build a minimal SkillDefinition for registry validation."""
    from cleveragents.domain.models.core.skill import Skill
    from cleveragents.skills.protocol import SkillDefinition, SkillMetadata

    skill = Skill(
        name=name,
        description=description,
        tool_refs=tool_refs or [],
        anonymous_tools=inline_tools or [],
        includes=includes or [],
    )
    metadata = SkillMetadata(
        name=name,
        description=description,
        tool_count=len(tool_refs or []) + len(inline_tools or []),
    )
    return SkillDefinition(skill=skill, metadata=metadata)


def _mock_settings(**overrides: Any) -> Any:
    """Create mock settings with sensible defaults for CLI tests."""
    tmpdir = tempfile.mkdtemp()
    s = MagicMock()
    s.database_url = overrides.get("database_url", f"sqlite:///{tmpdir}/test.db")
    s.data_dir = overrides.get("data_dir", Path(tmpdir))
    s.storage_path = overrides.get("storage_path", Path(tmpdir))
    s.config_path = overrides.get("config_path", Path(tmpdir) / "config.toml")
    s.log_dir = overrides.get("log_dir", Path(tmpdir) / "logs")
    s.default_automation_profile = "auto"
    s.has_provider_configured = MagicMock(return_value=False)
    s.configured_providers = []
    s.debug_enabled = False
    return s


# ===========================================================================
# skills/registry.py — validate_skill  (lines 200-213)
# ===========================================================================


@given("a skill registry with a mock tool registry")
def step_skill_reg_with_tools(context: Context) -> None:
    from cleveragents.skills.registry import SkillRegistry

    context.cov_tool_registry = _make_mock_tool_registry(tools={})
    context.cov_skill_registry = SkillRegistry(
        tool_registry=context.cov_tool_registry,
    )
    context.cov_validation_errors = []


@given("a skill registry with no tool registry")
def step_skill_reg_no_tools(context: Context) -> None:
    from cleveragents.skills.registry import SkillRegistry

    context.cov_skill_registry = SkillRegistry(tool_registry=None)
    context.cov_validation_errors = []


@given('a skill definition with tool ref "{ref}"')
def step_skill_def_tool_ref(context: Context, ref: str) -> None:
    context.cov_skill_def = _make_skill_definition(tool_refs=[ref])


@given("a skill definition with an inline tool missing its description")
def step_skill_def_inline_no_desc(context: Context) -> None:
    from cleveragents.domain.models.core.skill import SkillInlineTool
    from cleveragents.domain.models.core.tool import ToolSource

    # Use model_construct to bypass pydantic min-length validation
    inline = SkillInlineTool.model_construct(
        description="",
        source=ToolSource.CUSTOM,
        timeout=300,
        resource_slots=[],
    )
    context.cov_skill_def = _make_skill_definition(inline_tools=[inline])


@given('a skill definition that includes "{include_name}"')
def step_skill_def_include(context: Context, include_name: str) -> None:
    from cleveragents.domain.models.core.skill import SkillInclude

    inc = SkillInclude(name=include_name)
    context.cov_skill_def = _make_skill_definition(includes=[inc])


@given("a skill definition with no tool refs or includes")
def step_skill_def_empty(context: Context) -> None:
    context.cov_skill_def = _make_skill_definition()


@when("I validate the skill definition via the registry")
def step_validate_skill(context: Context) -> None:
    context.cov_validation_errors = context.cov_skill_registry.validate_skill(
        context.cov_skill_def,
    )


@then('the skill validation errors should mention "{text}"')
def step_skill_val_mention(context: Context, text: str) -> None:
    combined = " ".join(context.cov_validation_errors)
    assert text in combined, (
        f"Expected '{text}' in validation errors: {context.cov_validation_errors}"
    )


@then("the skill validation errors should be empty")
def step_skill_val_empty(context: Context) -> None:
    assert context.cov_validation_errors == [], (
        f"Expected no errors, got: {context.cov_validation_errors}"
    )


# ===========================================================================
# cli/formatting.py — format_output, format_output_session  (lines 121,128,197,231-236)
# ===========================================================================


@given('sample formatting data with key "{key}" and value "{value}"')
def step_fmt_data_dict(context: Context, key: str, value: str) -> None:
    context.cov_format_data = {key: value}
    context.cov_format_output = ""
    context.cov_session_output = ""


@given("sample formatting data as a list of two items")
def step_fmt_data_list(context: Context) -> None:
    context.cov_format_data = [{"a": "1", "b": "2"}, {"a": "3", "b": "4"}]
    context.cov_format_output = ""
    context.cov_session_output = ""


@given("sample formatting data as a list with mismatched keys")
def step_fmt_data_mismatched(context: Context) -> None:
    context.cov_format_data = [
        {"col_a": "val1"},
        {"col_a": "val2", "extra_col": "val3"},
    ]
    context.cov_format_output = ""
    context.cov_session_output = ""


@given("sample formatting data as an empty list")
def step_fmt_data_empty(context: Context) -> None:
    context.cov_format_data = []
    context.cov_format_output = ""
    context.cov_session_output = ""


@when('I format the data with format type "{fmt}"')
def step_fmt_format(context: Context, fmt: str) -> None:
    from contextlib import redirect_stdout
    from io import StringIO

    from cleveragents.cli.formatting import format_output

    buf = StringIO()
    with redirect_stdout(buf):
        result = format_output(context.cov_format_data, fmt)
    context.cov_format_output = result or buf.getvalue().rstrip("\n")


@when('I format the data via format_output_session with format "{fmt}"')
def step_fmt_session(context: Context, fmt: str) -> None:
    from cleveragents.cli.formatting import format_output_session

    context.cov_session_output = format_output_session(context.cov_format_data, fmt)


@then("the formatted output should contain {text}")
def step_fmt_output_contains(context: Context, text: str) -> None:
    text = text.strip('"')
    assert text in context.cov_format_output, (
        f"Expected '{text}' in output:\n{context.cov_format_output}"
    )


@then('the formatted output should be "{expected}"')
def step_fmt_output_exact(context: Context, expected: str) -> None:
    assert context.cov_format_output.strip() == expected, (
        f"Expected '{expected}', got: '{context.cov_format_output.strip()}'"
    )


@then("the formatted session output should not be empty")
def step_fmt_session_not_empty(context: Context) -> None:
    assert len(context.cov_session_output.strip()) > 0


@then('the formatted session output should contain "{text}"')
def step_fmt_session_contains(context: Context, text: str) -> None:
    assert text in context.cov_session_output, (
        f"Expected '{text}' in session output:\n{context.cov_session_output}"
    )


# ===========================================================================
# cli/commands/system.py — health check functions  (lines 108-118 etc.)
# ===========================================================================


@given("a system health check environment")
def step_sys_health_env(context: Context) -> None:
    context.cov_health_result = {}
    context.cov_info_data = {}
    context.cov_tmpdir = tempfile.mkdtemp()


@given("a config file that exists and is readable")
def step_sys_config_exists(context: Context) -> None:
    config_path = Path(context.cov_tmpdir) / "config.toml"
    config_path.write_text("[settings]\nkey = 'value'\n")
    context.cov_config_path = config_path


@given("a data dir that does not exist")
def step_sys_data_dir_missing(context: Context) -> None:
    context.cov_data_dir = Path(context.cov_tmpdir) / "nonexistent_data_dir"


@given("a writable data dir")
def step_sys_writable_data_dir(context: Context) -> None:
    data_dir = Path(context.cov_tmpdir) / "data"
    data_dir.mkdir(parents=True, exist_ok=True)
    context.cov_data_dir = data_dir


@given("a database file that exists")
def step_sys_db_exists(context: Context) -> None:
    db_path = Path(context.cov_tmpdir) / "test.db"
    db_path.write_bytes(b"\x00" * 1024)
    context.cov_db_path = db_path


@given("a log directory with files")
def step_sys_log_dir(context: Context) -> None:
    log_dir = Path(context.cov_tmpdir) / "logs"
    log_dir.mkdir(parents=True, exist_ok=True)
    (log_dir / "test.log").write_text("log content\n")
    context.cov_log_dir = log_dir


@when("I run the config file health check")
def step_sys_check_config(context: Context) -> None:
    from cleveragents.cli.commands.system import _check_config_file

    with patch.dict(
        os.environ,
        {"CLEVERAGENTS_CONFIG_PATH": str(context.cov_config_path)},
    ):
        context.cov_health_result = _check_config_file()


@when("I run the data dir health check")
def step_sys_check_data_dir(context: Context) -> None:
    from cleveragents.cli.commands.system import _check_data_dir

    ms = _mock_settings(data_dir=context.cov_data_dir)
    with patch("cleveragents.config.settings.get_settings", return_value=ms):
        context.cov_health_result = _check_data_dir()


@when("I run the disk space health check")
def step_sys_check_disk(context: Context) -> None:
    from unittest.mock import patch

    from cleveragents.cli.commands.system import _check_disk_space

    # Mock disk_usage to return 10 GB free so the test is environment-independent
    fake_usage = type(
        "Usage", (), {"total": 50 * 1024**3, "used": 40 * 1024**3, "free": 10 * 1024**3}
    )()
    with patch(
        "cleveragents.cli.commands.system.shutil.disk_usage", return_value=fake_usage
    ):
        context.cov_health_result = _check_disk_space()


@when("I run the git availability health check")
def step_sys_check_git(context: Context) -> None:
    from cleveragents.cli.commands.system import _check_git

    context.cov_health_result = _check_git()


@when("I run the file permissions health check")
def step_sys_check_perms(context: Context) -> None:
    from cleveragents.cli.commands.system import _check_file_permissions

    data_dir = getattr(context, "cov_data_dir", Path(context.cov_tmpdir))
    ms = _mock_settings(data_dir=data_dir)
    with patch("cleveragents.config.settings.get_settings", return_value=ms):
        context.cov_health_result = _check_file_permissions()


@when("I build info data")
def step_sys_build_info(context: Context) -> None:
    from cleveragents.cli.commands.system import build_info_data

    db_path = getattr(context, "cov_db_path", Path(context.cov_tmpdir) / "test.db")
    log_dir = getattr(context, "cov_log_dir", Path(context.cov_tmpdir) / "logs")

    ms = _mock_settings(
        database_url=f"sqlite:///{db_path}",
        log_dir=log_dir,
    )
    with patch("cleveragents.config.settings.get_settings", return_value=ms):
        context.cov_info_data = build_info_data()


@then('the health check status should be "{expected}"')
def step_sys_health_status(context: Context, expected: str) -> None:
    from cleveragents.cli.commands.system import CheckStatus

    status_map = {
        "ok": CheckStatus.OK,
        "warn": CheckStatus.WARN,
        "error": CheckStatus.ERROR,
    }
    assert context.cov_health_result["status"] == status_map[expected], (
        f"Expected '{expected}', got: {context.cov_health_result['status']}"
    )


@then('the health check details should be "{expected}"')
def step_sys_health_details(context: Context, expected: str) -> None:
    assert context.cov_health_result["details"] == expected, (
        f"Expected '{expected}', got: '{context.cov_health_result['details']}'"
    )


@then('the health check details should contain "{text}"')
def step_sys_health_details_contains(context: Context, text: str) -> None:
    details = context.cov_health_result["details"]
    assert text in details, f"Expected '{text}' in: '{details}'"


@then("the info data should contain a db_size value")
def step_sys_info_db_size(context: Context) -> None:
    assert "storage" in context.cov_info_data
    assert "db_size" in context.cov_info_data["storage"]
    assert context.cov_info_data["storage"]["db_size"] != "unknown"


@then("the info data should contain a logs value")
def step_sys_info_logs(context: Context) -> None:
    assert "storage" in context.cov_info_data
    assert "logs" in context.cov_info_data["storage"]


# ===========================================================================
# cli/commands/session.py — session CLI  (lines 56-69, 75, 157-159, etc.)
# ===========================================================================


@given("a session CLI test environment")
def step_sess_env(context: Context) -> None:
    context.cov_sess_output = ""
    context.cov_sess_error = None
    context.cov_sess_aborted = False
    context.cov_sess_exit_code = 0
    context.cov_sess_tmpdir = tempfile.mkdtemp()


@given("a mock session service that returns a created session")
def step_sess_mock_create(context: Context) -> None:
    from datetime import UTC, datetime

    mock_session = MagicMock()
    mock_session.session_id = "01TEST000000000000000000001"
    mock_session.actor_name = "test-actor"
    mock_session.namespace = "default"
    mock_session.created_at = datetime(2026, 1, 1, tzinfo=UTC)
    mock_session.updated_at = datetime(2026, 1, 1, tzinfo=UTC)
    mock_session.message_count = 0
    mock_session.messages = []
    mock_session.linked_plan_ids = []

    svc = MagicMock()
    svc.create.return_value = mock_session
    context.cov_mock_sess_svc = svc


@given("a mock session service that raises SessionNotFoundError on create")
def step_sess_mock_create_err(context: Context) -> None:
    from cleveragents.domain.models.core.session import SessionNotFoundError

    svc = MagicMock()
    svc.create.side_effect = SessionNotFoundError("test-id")
    context.cov_mock_sess_svc = svc


@given("a mock session service that returns no sessions")
def step_sess_mock_list_empty(context: Context) -> None:
    svc = MagicMock()
    svc.list.return_value = []
    context.cov_mock_sess_svc = svc


@given("a mock session service that returns a session")
def step_sess_mock_get(context: Context) -> None:
    from datetime import UTC, datetime

    mock_session = MagicMock()
    mock_session.session_id = "01TEST000000000000000000001"
    mock_session.actor_name = "test-actor"
    mock_session.namespace = "default"
    mock_session.created_at = datetime(2026, 1, 1, tzinfo=UTC)
    mock_session.updated_at = datetime(2026, 1, 1, tzinfo=UTC)
    mock_session.message_count = 0
    mock_session.messages = []
    mock_session.linked_plan_ids = []

    svc = MagicMock()
    svc.get.return_value = mock_session
    svc.delete.return_value = None
    context.cov_mock_sess_svc = svc


@given("a mock session service that can export")
def step_sess_mock_export(context: Context) -> None:
    svc = MagicMock()
    svc.export_session.return_value = {
        "session_id": "01TEST000000000000000000001",
        "messages": [],
    }
    context.cov_mock_sess_svc = svc


@given("a temporary output file that already exists")
def step_sess_temp_file(context: Context) -> None:
    out_path = Path(context.cov_sess_tmpdir) / "existing_export.json"
    out_path.write_text("{}")
    context.cov_output_path = out_path


@when("I call get_session_service")
def step_sess_get_service(context: Context) -> None:
    from cleveragents.cli.commands import session as session_mod

    mock_container = MagicMock()
    mock_db = MagicMock()
    mock_container.db.return_value = mock_db

    original_service = session_mod._service
    try:
        session_mod._service = None
        with (
            patch(
                "cleveragents.application.container.get_container",
                return_value=mock_container,
            ),
            patch(
                "cleveragents.application.services.session_service.PersistentSessionService",
            ) as mock_cls,
        ):
            mock_cls.return_value = MagicMock()
            result = session_mod._get_session_service()
            context.cov_sess_svc_result = result
    finally:
        session_mod._service = original_service


@when("I call reset_session_service")
def step_sess_reset_service(context: Context) -> None:
    from cleveragents.cli.commands import session as session_mod

    original = session_mod._service
    session_mod._service = MagicMock()  # set something
    session_mod._reset_session_service()
    context.cov_sess_cache_cleared = session_mod._service is None
    session_mod._service = original  # restore


@when('I invoke session create with format "{fmt}"')
def step_sess_create_fmt(context: Context, fmt: str) -> None:
    from contextlib import redirect_stdout

    from cleveragents.cli.commands import session as session_mod

    buf = StringIO()
    with patch.object(
        session_mod,
        "_get_session_service",
        return_value=context.cov_mock_sess_svc,
    ):
        try:
            with (
                patch("typer.echo", side_effect=lambda x: buf.write(str(x))),
                redirect_stdout(buf),
            ):
                session_mod.create(fmt=fmt)
        except SystemExit:
            pass
    context.cov_sess_output = buf.getvalue()


@when("I invoke session create expecting an error")
def step_sess_create_err(context: Context) -> None:
    from cleveragents.cli.commands import session as session_mod

    with patch.object(
        session_mod,
        "_get_session_service",
        return_value=context.cov_mock_sess_svc,
    ):
        try:
            session_mod.create(fmt="json")
        except (SystemExit, Exception) as exc:
            context.cov_sess_error = exc
            context.cov_sess_exit_code = (
                getattr(exc, "code", 1) if isinstance(exc, SystemExit) else 1
            )


@when('I invoke session list with format "{fmt}"')
def step_sess_list_fmt(context: Context, fmt: str) -> None:
    from cleveragents.cli.commands import session as session_mod

    buf = StringIO()
    with (
        patch.object(
            session_mod,
            "_get_session_service",
            return_value=context.cov_mock_sess_svc,
        ),
        patch.object(
            session_mod.console,
            "print",
            side_effect=lambda x="", **kw: buf.write(str(x) + "\n"),
        ),
    ):
        session_mod.list_sessions(fmt=fmt)
    context.cov_sess_output = buf.getvalue()


@when("I invoke session delete without confirming")
def step_sess_delete_no_confirm(context: Context) -> None:
    from cleveragents.cli.commands import session as session_mod

    with patch.object(
        session_mod,
        "_get_session_service",
        return_value=context.cov_mock_sess_svc,
    ):
        try:
            with patch("typer.confirm", return_value=False):
                session_mod.delete(
                    session_id="01TEST000000000000000000001",
                    yes=False,
                )
        except (typer.Abort, SystemExit):
            context.cov_sess_aborted = True


@when("I invoke session delete with yes flag")
def step_sess_delete_yes(context: Context) -> None:
    from cleveragents.cli.commands import session as session_mod

    buf_lines: list[str] = []
    with (
        patch.object(
            session_mod,
            "_get_session_service",
            return_value=context.cov_mock_sess_svc,
        ),
        patch.object(
            session_mod.console,
            "print",
            side_effect=lambda x="", **kw: buf_lines.append(str(x)),
        ),
    ):
        session_mod.delete(
            session_id="01TEST000000000000000000001",
            yes=True,
        )
    context.cov_sess_output = "\n".join(buf_lines)


@when("I invoke session export to stdout")
def step_sess_export_stdout(context: Context) -> None:
    from cleveragents.cli.commands import session as session_mod

    buf = StringIO()
    with (
        patch.object(
            session_mod,
            "_get_session_service",
            return_value=context.cov_mock_sess_svc,
        ),
        patch("typer.echo", side_effect=lambda x: buf.write(str(x))),
    ):
        session_mod.export_session(
            session_id="01TEST000000000000000000001",
            output=None,
            force=False,
        )
    context.cov_sess_output = buf.getvalue()


@when("I invoke session export to that file without force")
def step_sess_export_no_force(context: Context) -> None:
    from cleveragents.cli.commands import session as session_mod

    with patch.object(
        session_mod,
        "_get_session_service",
        return_value=context.cov_mock_sess_svc,
    ):
        try:
            session_mod.export_session(
                session_id="01TEST000000000000000000001",
                output=context.cov_output_path,
                force=False,
            )
        except (SystemExit, Exception) as exc:
            context.cov_sess_exit_code = getattr(exc, "code", 1)
            context.cov_sess_error = exc


@then("a session service should be returned")
def step_sess_svc_returned(context: Context) -> None:
    assert context.cov_sess_svc_result is not None


@then("the session service cache should be cleared")
def step_sess_cache_cleared(context: Context) -> None:
    assert context.cov_sess_cache_cleared is True


@then('the covboost session output should contain "{text}"')
def step_sess_output_has(context: Context, text: str) -> None:
    assert text in context.cov_sess_output, (
        f"Expected '{text}' in:\n{context.cov_sess_output}"
    )


@then("the session CLI should have exited with error")
def step_sess_exit_err(context: Context) -> None:
    assert context.cov_sess_error is not None or context.cov_sess_exit_code != 0, (
        "Expected an error exit"
    )


@then("the session CLI should have been aborted")
def step_sess_aborted(context: Context) -> None:
    assert context.cov_sess_aborted is True, "Expected abort"


# ===========================================================================
# cli/commands/cleanup.py — error handling  (lines 49-58, 94)
# ===========================================================================


@given("a cleanup CLI test environment")
def step_clean_env(context: Context) -> None:
    context.cov_clean_result = None
    context.cov_active_plans = frozenset()


@given("a cleanup service that raises OSError on sandbox scan")
def step_clean_oserror(context: Context) -> None:
    svc = MagicMock()
    svc._get_sandbox_dirs.side_effect = OSError("Permission denied")
    context.cov_clean_svc = svc


@given("a mock cleanup service")
def step_clean_mock_svc(context: Context) -> None:
    svc = MagicMock()
    svc._get_sandbox_dirs.return_value = []
    svc.extract_plan_id_from_sandbox.return_value = None

    report = MagicMock()
    report.stale_items = []
    report.purged_items = []
    report.total_bytes_freed = 0
    svc.scan.return_value = report
    svc.purge.return_value = report
    context.cov_clean_svc = svc


@when("I detect active plans")
def step_clean_detect(context: Context) -> None:
    from cleveragents.cli.commands.cleanup import _detect_active_plan_ids

    context.cov_active_plans = _detect_active_plan_ids(context.cov_clean_svc)


@when("I invoke cleanup prune")
def step_clean_prune(context: Context) -> None:
    from cleveragents.cli.commands.cleanup import scan

    with (
        patch(
            "cleveragents.cli.commands.cleanup._get_cleanup_service",
            return_value=context.cov_clean_svc,
        ),
        contextlib.suppress(SystemExit),
    ):
        scan()
    context.cov_clean_result = "ok"


@then("the active plans set should be empty")
def step_clean_empty(context: Context) -> None:
    assert context.cov_active_plans == frozenset(), (
        f"Expected empty, got: {context.cov_active_plans}"
    )


@then("the cleanup should have run without error")
def step_clean_ok(context: Context) -> None:
    assert context.cov_clean_result == "ok"


# ===========================================================================
# cli/commands/audit.py — audit service creation  (lines 29-32, 110-111)
# ===========================================================================


@given("an audit CLI test environment")
def step_audit_env(context: Context) -> None:
    context.cov_audit_svc = None
    context.cov_audit_output = ""


@given("a mock audit service with no entries")
def step_audit_mock_empty(context: Context) -> None:
    svc = MagicMock()
    svc.list_entries.return_value = []
    svc.__enter__ = MagicMock(return_value=svc)
    svc.__exit__ = MagicMock(return_value=False)
    context.cov_mock_audit_svc = svc


@when("I create the audit service")
def step_audit_create(context: Context) -> None:
    from cleveragents.cli.commands.audit import _get_audit_service

    ms = _mock_settings()
    with (
        patch("cleveragents.config.settings.get_settings", return_value=ms),
        patch(
            "cleveragents.application.services.audit_service.AuditService.__init__",
            return_value=None,
        ),
    ):
        context.cov_audit_svc = _get_audit_service()


@when("I invoke audit list")
def step_audit_list(context: Context) -> None:
    from cleveragents.cli.commands.audit import list_entries

    buf = StringIO()
    console_mock = MagicMock()
    console_mock.print = lambda x="", **kw: buf.write(str(x) + "\n")

    with (
        patch(
            "cleveragents.cli.commands.audit._get_audit_service",
            return_value=context.cov_mock_audit_svc,
        ),
        patch(
            "cleveragents.cli.commands.audit.get_console",
            return_value=console_mock,
        ),
    ):
        list_entries()
    context.cov_audit_output = buf.getvalue()


@then("an audit service instance should be returned")
def step_audit_returned(context: Context) -> None:
    assert context.cov_audit_svc is not None


@then('the covboost audit output should contain "{text}"')
def step_audit_output_has(context: Context, text: str) -> None:
    assert text in context.cov_audit_output, (
        f"Expected '{text}' in: {context.cov_audit_output}"
    )


# ===========================================================================
# application/container.py — container init  (lines 66-69, 125-130)
# ===========================================================================


@given("a container test environment")
def step_ctr_env(context: Context) -> None:
    context.cov_container = None
    context.cov_ctr_cleared = False


@when("I get the application container")
def step_ctr_get(context: Context) -> None:
    from cleveragents.application.container import get_container, reset_container

    reset_container()
    context.cov_container = get_container()


@when("I reset the container singleton")
def step_ctr_reset(context: Context) -> None:
    import cleveragents.application.container as container_mod
    from cleveragents.application.container import get_container, reset_container

    _ = get_container()
    reset_container()
    context.cov_ctr_cleared = container_mod._container is None


@then("the container should provide a database session")
def step_ctr_has_db(context: Context) -> None:
    assert context.cov_container is not None
    # Container has a 'db' provider attribute
    assert hasattr(context.cov_container, "database_url")


@then("the container cache should be cleared")
def step_ctr_cleared(context: Context) -> None:
    assert context.cov_ctr_cleared is True
