"""Step definitions for CLI main.py coverage round 3 (lines 104-110, 582-590, 593+)."""

from __future__ import annotations

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

from behave import given, then, when

# ---------------------------------------------------------------------------
# Lines 104-110: _register_subcommands exception handler
# ---------------------------------------------------------------------------


@given("cmcov3 the _subcommands_registered flag is reset to False")
def step_reset_subcommands_flag(context: Any) -> None:
    """Reset the module-level flag so _register_subcommands runs fresh."""
    import importlib

    mod = importlib.import_module("cleveragents.cli.main")
    context.cmcov3_mod = mod
    context.cmcov3_orig_registered = mod._subcommands_registered
    mod._subcommands_registered = False

    # Restore the flag after the scenario to avoid polluting other tests.
    def _restore() -> None:
        mod._subcommands_registered = context.cmcov3_orig_registered

    context.add_cleanup(_restore)


@when("cmcov3 _register_subcommands is called and an import raises an error")
def step_register_subcommands_import_error(context: Any) -> None:
    """Patch a subcommand import so _register_subcommands hits its except block."""
    import builtins

    mod = context.cmcov3_mod
    mock_err_console = MagicMock()
    original_import = builtins.__import__

    def failing_import(name: str, *args: Any, **kwargs: Any) -> Any:
        if name == "cleveragents.cli.commands":
            raise ImportError("cmcov3 forced import failure")
        return original_import(name, *args, **kwargs)

    patcher_console = patch.object(
        mod, "get_err_console", return_value=mock_err_console
    )
    patcher_console.start()
    context.add_cleanup(patcher_console.stop)

    patcher_builtins = patch.object(builtins, "__import__", side_effect=failing_import)
    patcher_builtins.start()

    try:
        mod._register_subcommands()
    except Exception as exc:
        context.cmcov3_error = exc
    finally:
        patcher_builtins.stop()

    context.cmcov3_err_console = mock_err_console


@then("cmcov3 the error console should have printed the failure message")
def step_check_failure_message(context: Any) -> None:
    """Verify the error console received the failure message."""
    mock_console = context.cmcov3_err_console
    assert mock_console.print.called, "Expected err console print to be called"
    all_calls = " ".join(str(c) for c in mock_console.print.call_args_list)
    assert "Failed to register subcommands" in all_calls, (
        f"Expected 'Failed to register subcommands' in output, got: {all_calls}"
    )


@then("cmcov3 the _subcommands_registered flag should still be False")
def step_check_flag_still_false(context: Any) -> None:
    """The flag must remain False since registration failed."""
    mod = context.cmcov3_mod
    assert mod._subcommands_registered is False, (
        f"Expected _subcommands_registered to be False, got {mod._subcommands_registered}"
    )


# ---------------------------------------------------------------------------
# Lines 582-590: completion command with subprocess.run
# ---------------------------------------------------------------------------


@given('cmcov3 subprocess.run is mocked to return stdout "{stdout_text}"')
def step_mock_subprocess_with_stdout(context: Any, stdout_text: str) -> None:
    """Set up a mock for subprocess.run that returns the given stdout."""
    mock_result = MagicMock()
    mock_result.stdout = stdout_text
    mock_result.stderr = ""
    mock_result.returncode = 0

    patcher = patch("subprocess.run", return_value=mock_result)
    context.cmcov3_subprocess_mock = patcher.start()
    context.add_cleanup(patcher.stop)


@given("cmcov3 subprocess.run is mocked to return empty stdout")
def step_mock_subprocess_empty_stdout(context: Any) -> None:
    """Set up a mock for subprocess.run that returns empty stdout."""
    mock_result = MagicMock()
    mock_result.stdout = ""
    mock_result.stderr = ""
    mock_result.returncode = 0

    patcher = patch("subprocess.run", return_value=mock_result)
    context.cmcov3_subprocess_mock = patcher.start()
    context.add_cleanup(patcher.stop)


@when('cmcov3 the completion command is invoked with shell "{shell}"')
def step_invoke_completion(context: Any, shell: str) -> None:
    """Invoke the completion CLI command with the given shell argument."""
    from typer.testing import CliRunner

    from cleveragents.cli.main import app

    runner = CliRunner()
    result = runner.invoke(app, ["completion", shell])
    context.cmcov3_cli_result = result


@then('cmcov3 the output should contain "{expected}"')
def step_check_output_contains(context: Any, expected: str) -> None:
    """Verify the CLI output contains the expected text."""
    output = context.cmcov3_cli_result.output
    assert expected in output, (
        f"Expected output to contain {expected!r}, got: {output!r}"
    )


# ---------------------------------------------------------------------------
# Lines 593+: convert_exit_code function
# ---------------------------------------------------------------------------


@when("cmcov3 convert_exit_code is called with None")
def step_convert_exit_code_none(context: Any) -> None:
    """Call convert_exit_code with None."""
    from cleveragents.cli.main import convert_exit_code

    try:
        context.cmcov3_exit_code_result = convert_exit_code(None)
    except Exception as exc:
        context.cmcov3_error = exc


@when("cmcov3 convert_exit_code is called with an object having exit_code {code:d}")
def step_convert_exit_code_object(context: Any, code: int) -> None:
    """Call convert_exit_code with an object that has an exit_code attribute."""
    from cleveragents.cli.main import convert_exit_code

    obj = MagicMock()
    obj.exit_code = code
    try:
        context.cmcov3_exit_code_result = convert_exit_code(obj)
    except Exception as exc:
        context.cmcov3_error = exc


@when("cmcov3 convert_exit_code is called with integer {code:d}")
def step_convert_exit_code_int(context: Any, code: int) -> None:
    """Call convert_exit_code with an integer."""
    from cleveragents.cli.main import convert_exit_code

    try:
        context.cmcov3_exit_code_result = convert_exit_code(code)
    except Exception as exc:
        context.cmcov3_error = exc


@when("cmcov3 convert_exit_code is called with a non-convertible value")
def step_convert_exit_code_nonconvertible(context: Any) -> None:
    """Call convert_exit_code with a value that cannot be converted to int."""
    from cleveragents.cli.main import convert_exit_code

    try:
        context.cmcov3_exit_code_result = convert_exit_code("not_a_number")
    except Exception as exc:
        context.cmcov3_error = exc


@then("cmcov3 the exit code result should be {expected:d}")
def step_check_exit_code_result(context: Any, expected: int) -> None:
    """Verify convert_exit_code returned the expected value."""
    if hasattr(context, "cmcov3_error"):
        raise AssertionError(
            f"convert_exit_code raised an exception: {context.cmcov3_error}"
        )
    actual = context.cmcov3_exit_code_result
    assert actual == expected, (
        f"Expected convert_exit_code to return {expected}, got {actual}"
    )
