"""Step definitions for the CleverAgents CLI feature - Fixed for Typer."""

from __future__ import annotations

import contextlib
import io
from collections.abc import Sequence
from typing import Any
from unittest.mock import patch

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

from cleveragents.cli.main import (
    app,
    convert_exit_code,
    ensure_cli_commands_registered,
    main,
)


def _run_cli(context: Any, args: Sequence[str]) -> None:
    buffer = io.StringIO()
    with contextlib.redirect_stdout(buffer):
        exit_code = main(list(args))
    context.result = {
        "exit_code": exit_code,
        "output": buffer.getvalue(),
    }
    # Also set context.output for compatibility with shared steps
    context.output = buffer.getvalue()
    context.command_output = (
        buffer.getvalue()
    )  # For cli_plan_context_commands_steps compatibility
    context.exit_code = exit_code


@when('I run the CleverAgents CLI with "--help"')
def step_run_help(context: Any) -> None:
    _run_cli(context, ["--help"])


# Removed specific @then - using generic one from cli_coverage_steps.py instead


@when('I run the CleverAgents CLI with "--version"')
def step_run_version(context: Any) -> None:
    _run_cli(context, ["--version"])


# Removed specific @then - using generic one from cli_coverage_steps.py instead


# New generic steps for other commands
@when('I run the CleverAgents CLI with "{args}"')
def step_run_cli_with_args(context, args):
    """Execute the CLI with specific arguments."""
    ensure_cli_commands_registered()
    runner = CliRunner()
    result = runner.invoke(app, args.split())
    # Combine stdout and stderr for error checking
    output = result.stdout
    try:
        if result.stderr:
            output += result.stderr
    except (ValueError, AttributeError):
        pass
    context.result = {
        "exit_code": result.exit_code,
        "output": output,
        "runner_result": result,
    }
    # Also set context.output for compatibility with shared steps
    context.output = output
    context.command_output = output  # For cli_plan_context_commands_steps compatibility
    context.exit_code = result.exit_code


@then('the CLI output should contain "{text}"')
def step_cli_output_contains(context, text):
    output = getattr(context, "output", "") or context.result.get("output", "")
    assert text in output, f"Expected '{text}' in output: {output}"


@then('the CLI output should not contain "{text}"')
def step_cli_output_not_contains(context, text):
    output = getattr(context, "output", "") or context.result.get("output", "")
    assert text not in output, f"Did not expect '{text}' in output: {output}"


# Removed duplicate @then - using generic one from cli_coverage_steps.py instead


@then("the CLI command should fail")
def step_cli_command_fails(context):
    """Verify the CLI command failed."""
    assert context.result["exit_code"] != 0, (
        f"Expected non-zero exit code, got {context.result['exit_code']}"
    )


@then('the error should contain "{text}"')
def step_error_should_contain(context, text):
    """Verify the error output contains expected text."""
    # For Typer, errors may also be in stdout
    output = context.result.get("output", "")
    assert text.lower() in output.lower(), (
        f"Expected '{text}' not found in output: {output}"
    )


# Exit code conversion steps
@then("the exit code should be {expected:d}")
def step_exit_code_should_be(context, expected):
    """Check exit code."""
    # Check multiple possible attributes for backward compatibility
    if hasattr(context, "last_exit_code"):
        actual = context.last_exit_code
    elif hasattr(context, "exit_code"):
        actual = context.exit_code
    elif hasattr(context, "result") and isinstance(context.result, dict):
        actual = context.result.get("exit_code", 0)
    else:
        actual = 0
    assert actual == expected, f"Expected exit code {expected}, got {actual}"


@when("I convert exit code {code}")
def step_convert_exit_code(context, code):
    """Convert an exit code."""
    if code == "None":
        context.converted_code = convert_exit_code(None)
    else:
        context.converted_code = convert_exit_code(int(code))


@then("the result should be {expected}")
def step_result_should_be(context, expected):
    """Verify the conversion result."""
    assert context.converted_code == int(expected)


@when("I convert a Click Exit exception with code {code}")
def step_convert_click_exit(context, code):
    """Convert a Click Exit exception."""
    exit_exc = click.exceptions.Exit(int(code))
    context.converted_code = convert_exit_code(exit_exc)


# Main function steps
@when('I call main with arguments "{args}"')
def step_call_main_with_args(context, args):
    """Call main function with arguments."""
    # Directly call main function
    context.main_result = main(args.split())


@then("main should process the version flag")
def step_main_processes_version(context):
    """Verify main processed the version flag."""
    assert context.main_result == 0


@when("I call main with no arguments")
def step_call_main_no_args(context):
    """Call main with no arguments."""
    # Capture output to check if help is displayed
    buffer = io.StringIO()
    err_buffer = io.StringIO()

    with contextlib.redirect_stdout(buffer), contextlib.redirect_stderr(err_buffer):
        try:
            context.main_result = main([])
            context.main_output = buffer.getvalue()
            context.main_stderr = err_buffer.getvalue()
        except SystemExit as e:
            context.main_result = e.code
            context.main_output = buffer.getvalue()
            context.main_stderr = err_buffer.getvalue()


@then("main should display help")
def step_main_displays_help(context):
    """Verify main displays help."""
    # Check that help text was displayed
    output = context.main_output + context.main_stderr
    assert "Usage:" in output or "Commands:" in output or "help" in output.lower(), (
        f"Expected help output, got: {output}"
    )


@when("I call main and CLI raises SystemExit {code}")
def step_main_system_exit(context, code):
    """Call main with SystemExit."""
    # We need to mock at a lower level since Typer doesn't use __call__ directly
    # Mock the version command to raise SystemExit

    # Use an invalid command that will trigger error handling
    # but mock the error handling to raise SystemExit with our code
    with patch("cleveragents.cli.main.err_console.print") as mock_print:
        # Make the function raise SystemExit when error is printed
        def raise_exit(*args, **kwargs):
            raise SystemExit(int(code))

        mock_print.side_effect = raise_exit

        buffer = io.StringIO()
        with contextlib.redirect_stdout(buffer):
            # Use an invalid command to trigger the error path
            context.main_result = main(["invalid-test-command-xyz"])


@then("main should return {expected}")
def step_main_returns_code(context, expected):
    """Verify main returns expected code."""
    assert context.main_result == int(expected), (
        f"Expected {expected}, got {context.main_result}"
    )
