"""Step definitions for tdd_cli_incomplete_subcommand_registration.feature.

This test captures bug #2604: _register_subcommands() in
src/cleveragents/cli/main.py catches all exceptions during subcommand
import and registration, then silently returns instead of raising.
This violates CONTRIBUTING.md error-handling standards.

The fix replaces the bare ``return`` with ``raise SystemExit(1)`` so the
CLI exits with a non-zero exit code instead of entering a partially
initialized state.

Once the fix for #2604 is merged, the ``@tdd_expected_fail`` scenario
(which asserts the old silent-return behaviour) must be removed so the
remaining scenarios run as regression guards.
"""

from __future__ import annotations

from unittest.mock import patch

from behave import given, then, when  # type: ignore[import-untyped]
from behave.runner import Context  # type: ignore[import-untyped]
from typer.testing import CliRunner

runner = CliRunner()


@given("the CLI subcommand import raises an ImportError during registration")
def step_given_import_error(context: Context) -> None:
    """Arrange: patch the subcommand import to raise ImportError."""
    context.import_error = ImportError("simulated subcommand import failure #2604")


@when("the CLI is invoked with any command")
def step_when_cli_invoked(context: Context) -> None:
    """Act: invoke the CLI while the subcommand import is broken."""
    import cleveragents.cli.main as cli_main

    # Reset the registration flag so _register_subcommands() runs again.
    original_flag = cli_main._subcommands_registered
    cli_main._subcommands_registered = False

    try:
        with patch(
            "cleveragents.cli.main._register_subcommands",
            side_effect=context.import_error,
        ):
            # We patch _register_subcommands itself to raise so we can test
            # the caller's behaviour without needing to break real imports.
            # The real fix is inside _register_subcommands, but the caller
            # (ensure_cli_commands_registered / the app callback) must also
            # propagate the SystemExit.
            from cleveragents.cli.main import app

            result = runner.invoke(app, ["--help"], catch_exceptions=True)
            context.cli_result = result
    finally:
        cli_main._subcommands_registered = original_flag


@then("the CLI exits with a non-zero exit code")
def step_then_non_zero_exit(context: Context) -> None:
    """Assert: the CLI must not exit with 0 when registration fails."""
    assert context.cli_result.exit_code != 0, (
        f"Bug #2604: CLI exited with code 0 after subcommand registration "
        f"failure. The CLI must exit with a non-zero code to signal the "
        f"error. Got exit_code={context.cli_result.exit_code}. "
        f"Output: {context.cli_result.output!r}"
    )


@then('the error output contains "Failed to register subcommands"')
def step_then_error_message(context: Context) -> None:
    """Assert: the error message must be present in the output."""
    # CliRunner with mix_stderr=False separates stdout and stderr.
    # The error is printed to stderr via get_err_console().
    combined = (context.cli_result.output or "") + (
        getattr(context.cli_result, "stderr", "") or ""
    )
    # When _register_subcommands itself is patched to raise, the message
    # won't appear (it's inside the function). We test the exit code path
    # separately. For the message test, we need to invoke the real function
    # with a broken import.
    import cleveragents.cli.main as cli_main

    original_flag = cli_main._subcommands_registered
    cli_main._subcommands_registered = False

    try:
        # Patch the internal import inside _register_subcommands by making
        # the first import inside the try block raise.
        with patch(
            "cleveragents.cli.commands.project",
            side_effect=ImportError("simulated import failure #2604"),
        ):
            from cleveragents.cli.main import app

            result = runner.invoke(app, ["--help"], catch_exceptions=True)
            combined = (result.output or "") + (getattr(result, "stderr", "") or "")
    finally:
        cli_main._subcommands_registered = original_flag

    assert "Failed to register subcommands" in combined, (
        f"Bug #2604: Expected 'Failed to register subcommands' in CLI output "
        f"when subcommand registration fails, but got: {combined!r}"
    )


@then("the CLI exits with exit code 0")
def step_then_zero_exit(context: Context) -> None:
    """Assert the OLD (buggy) behaviour: CLI exits with 0 on registration error.

    This scenario is tagged @tdd_expected_fail because it asserts the bug.
    Once the fix is merged, this scenario must be removed.
    """
    assert context.cli_result.exit_code == 0, (
        f"Expected exit code 0 (old buggy behaviour), "
        f"got {context.cli_result.exit_code}"
    )
