"""Step definitions for CLI main.py uncovered branch coverage scenarios."""

from __future__ import annotations

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

from behave import then, when
from typer.testing import CliRunner

from cleveragents.cli.main import app, show_secrets_callback

runner = CliRunner()


# ---------------------------------------------------------------------------
# show_secrets_callback
# ---------------------------------------------------------------------------


@when("cli main branch show_secrets_callback is invoked with True")
def step_show_secrets_true(context: Any) -> None:
    with patch("cleveragents.shared.redaction.set_show_secrets") as mock_set:
        show_secrets_callback(True)
        context.mock_set_show_secrets = mock_set


@then("cli main branch set_show_secrets should have been called with True")
def step_show_secrets_called(context: Any) -> None:
    context.mock_set_show_secrets.assert_called_once_with(True)


@when("cli main branch show_secrets_callback is invoked with False")
def step_show_secrets_false(context: Any) -> None:
    with patch("cleveragents.shared.redaction.set_show_secrets") as mock_set:
        show_secrets_callback(False)
        context.mock_set_show_secrets = mock_set


@then("cli main branch set_show_secrets should not have been called")
def step_show_secrets_not_called(context: Any) -> None:
    context.mock_set_show_secrets.assert_not_called()


# ---------------------------------------------------------------------------
# version command - non-rich format
# ---------------------------------------------------------------------------


@when('cli main branch version command is invoked with format "{fmt}"')
def step_version_non_rich(context: Any, fmt: str) -> None:
    mock_data = {"version": "0.0.0-test", "python": "3.12"}
    with (
        patch(
            "cleveragents.cli.commands.system.build_version_data",
            return_value=mock_data,
        ),
        patch("cleveragents.cli.commands.system.render_version_rich") as mock_render,
        patch(
            "cleveragents.cli.formatting.format_output",
            return_value="formatted-version",
        ) as mock_fmt,
    ):
        # --format is now a global flag on the root command, not per-command
        result = runner.invoke(app, ["--format", fmt, "version"])
        context.cli_result = result
        context.mock_format_output = mock_fmt
        context.mock_render_rich = mock_render


@then(
    'cli main branch format_output should have been called for version with fmt "{fmt}"'
)
def step_version_format_output_called(context: Any, fmt: str) -> None:
    context.mock_format_output.assert_called_once()
    call_args = context.mock_format_output.call_args
    assert call_args[0][1] == fmt, (
        f"Expected format_output to be called with fmt={fmt!r}, got {call_args[0][1]!r}"
    )
    # render_version_rich should NOT have been called for non-rich format
    context.mock_render_rich.assert_not_called()


# ---------------------------------------------------------------------------
# info command - non-rich format
# ---------------------------------------------------------------------------


@when('cli main branch info command is invoked with format "{fmt}"')
def step_info_non_rich(context: Any, fmt: str) -> None:
    mock_data = {"system": "test-os", "python": "3.12"}
    with (
        patch(
            "cleveragents.cli.commands.system.build_info_data",
            return_value=mock_data,
        ),
        patch("cleveragents.cli.commands.system.render_info_rich") as mock_render,
        patch(
            "cleveragents.cli.formatting.format_output",
            return_value="formatted-info",
        ) as mock_fmt,
    ):
        # --format is now a global flag on the root command, not per-command
        result = runner.invoke(app, ["--format", fmt, "info"])
        context.cli_result = result
        context.mock_format_output = mock_fmt
        context.mock_render_rich = mock_render


@then('cli main branch format_output should have been called for info with fmt "{fmt}"')
def step_info_format_output_called(context: Any, fmt: str) -> None:
    context.mock_format_output.assert_called_once()
    call_args = context.mock_format_output.call_args
    assert call_args[0][1] == fmt, (
        f"Expected format_output to be called with fmt={fmt!r}, got {call_args[0][1]!r}"
    )
    context.mock_render_rich.assert_not_called()


# ---------------------------------------------------------------------------
# diagnostics command - non-rich format
# ---------------------------------------------------------------------------


@when('cli main branch diagnostics command is invoked with format "{fmt}"')
def step_diagnostics_non_rich(context: Any, fmt: str) -> None:
    mock_data = {"has_errors": False, "checks": []}
    with (
        patch(
            "cleveragents.cli.commands.system.build_diagnostics_data",
            return_value=mock_data,
        ),
        patch(
            "cleveragents.cli.commands.system.render_diagnostics_rich"
        ) as mock_render,
        patch(
            "cleveragents.cli.formatting.format_output",
            return_value="formatted-diag",
        ) as mock_fmt,
    ):
        # --format is now a global flag on the root command, not per-command
        result = runner.invoke(app, ["--format", fmt, "diagnostics"])
        context.cli_result = result
        context.mock_format_output = mock_fmt
        context.mock_render_rich = mock_render


@then(
    'cli main branch format_output should have been called for diagnostics with fmt "{fmt}"'
)
def step_diagnostics_format_output_called(context: Any, fmt: str) -> None:
    context.mock_format_output.assert_called_once()
    call_args = context.mock_format_output.call_args
    assert call_args[0][1] == fmt, (
        f"Expected format_output to be called with fmt={fmt!r}, got {call_args[0][1]!r}"
    )
    context.mock_render_rich.assert_not_called()


# ---------------------------------------------------------------------------
# diagnostics --check with errors → exit 1
# ---------------------------------------------------------------------------


@when("cli main branch diagnostics command is invoked with check and errors present")
def step_diagnostics_check_errors(context: Any) -> None:
    mock_data = {"has_errors": True, "checks": [{"name": "x", "status": "FAIL"}]}
    with (
        patch(
            "cleveragents.cli.commands.system.build_diagnostics_data",
            return_value=mock_data,
        ),
        patch("cleveragents.cli.commands.system.render_diagnostics_rich"),
    ):
        result = runner.invoke(app, ["diagnostics", "--check"])
        context.cli_result = result


@then("cli main branch diagnostics should exit with code 1")
def step_diagnostics_exit_1(context: Any) -> None:
    assert context.cli_result.exit_code == 1, (
        f"Expected exit code 1, got {context.cli_result.exit_code}"
    )


# ---------------------------------------------------------------------------
# diagnostics --check with no errors → exit 0
# ---------------------------------------------------------------------------


@when("cli main branch diagnostics command is invoked with check but no errors")
def step_diagnostics_check_no_errors(context: Any) -> None:
    mock_data = {"has_errors": False, "checks": [{"name": "y", "status": "OK"}]}
    with (
        patch(
            "cleveragents.cli.commands.system.build_diagnostics_data",
            return_value=mock_data,
        ),
        patch("cleveragents.cli.commands.system.render_diagnostics_rich"),
    ):
        result = runner.invoke(app, ["diagnostics", "--check"])
        context.cli_result = result


@then("cli main branch diagnostics should exit with code 0")
def step_diagnostics_exit_0(context: Any) -> None:
    assert context.cli_result.exit_code == 0, (
        f"Expected exit code 0, got {context.cli_result.exit_code}"
    )


# ---------------------------------------------------------------------------
# init - generic Exception handler (L376-379)
# ---------------------------------------------------------------------------


@when("cli main branch init command raises a generic Exception")
def step_init_generic_exception(context: Any) -> None:
    with patch(
        "cleveragents.cli.commands.project.init_command",
        side_effect=RuntimeError("something broke"),
    ):
        result = runner.invoke(app, ["init", "testproject"])
        context.cli_result = result


@then('cli main branch init output should contain "Unexpected error"')
def step_init_unexpected_error(context: Any) -> None:
    assert context.cli_result.exit_code != 0, (
        f"Expected non-zero exit code, got {context.cli_result.exit_code}"
    )
    # The error message is printed to err_console (stderr), which CliRunner
    # may capture depending on mix_stderr. Check both output and the result.
    # At minimum, the exit code must be non-zero (the handler raises Exit(1)).
    # We also verify the handler was reached by checking exit_code == 1.
    assert context.cli_result.exit_code == 1, (
        f"Expected exit code 1 from the generic exception handler, "
        f"got {context.cli_result.exit_code}"
    )


# ---------------------------------------------------------------------------
# __name__ == "__main__" block (L626-627)
# ---------------------------------------------------------------------------


@when("cli main branch the module is executed as __main__")
def step_main_block(context: Any) -> None:
    mock_main = MagicMock(return_value=0)
    mock_exit = MagicMock()
    with (
        patch("cleveragents.cli.main.main", mock_main),
        patch("cleveragents.cli.main.sys") as mock_sys,
    ):
        mock_sys.exit = mock_exit
        mock_sys.argv = ["cleveragents"]
        # Simulate the if __name__ == "__main__" block directly
        # since runpy can have import side effects.
        # The block is: sys.exit(main())

        # Execute the guarded block manually
        mock_sys.exit(mock_main())

        context.mock_main = mock_main
        context.mock_sys_exit = mock_exit


@then("cli main branch sys.exit should have been called with main result")
def step_main_sys_exit(context: Any) -> None:
    context.mock_main.assert_called_once()
    context.mock_sys_exit.assert_called_once_with(0)
