from __future__ import annotations

from unittest.mock import MagicMock, patch

from behave import then, when

from cleveragents.providers.llm.anthropic_provider import AnthropicChatProvider


def _register_cleanup(context, cleanup):
    if hasattr(context, "add_cleanup"):
        context.add_cleanup(cleanup)
    else:
        cleanup_handlers = getattr(context, "_cleanup_handlers", [])
        cleanup_handlers.append(cleanup)
        context._cleanup_handlers = cleanup_handlers


@when("I attempt to create an Anthropic chat provider without an API key")
def step_attempt_create_without_key(context):
    context.anthropic_creation_error = None
    try:
        AnthropicChatProvider(api_key="", model="claude-3-opus-20240229")
    except Exception as exc:  # pragma: no cover - defensive test guard
        context.anthropic_creation_error = exc


@then('the Anthropic provider creation should fail with "{error_message}"')
def step_assert_creation_failure(context, error_message):
    error = getattr(context, "anthropic_creation_error", None)
    assert error is not None, "Anthropic provider creation should have failed"
    assert isinstance(error, ValueError)
    assert str(error) == error_message


@when(
    'I create an Anthropic chat provider with API key "{api_key}" and model "{model}" with custom overrides'
)
def step_create_anthropic_provider_with_overrides(context, api_key, model):
    patcher = patch("cleveragents.providers.llm.anthropic_provider.ChatAnthropic")
    context.chat_anthropic_patcher = patcher
    mock_chat_class = patcher.start()
    _register_cleanup(context, patcher.stop)
    mock_chat_instance = MagicMock(name="ChatAnthropicInstance")
    mock_chat_instance.get_num_tokens = MagicMock(return_value=0)
    mock_chat_class.return_value = mock_chat_instance

    context.anthropic_overrides = {
        "temperature": 0.15,
        "max_tokens": 2048,
        "default_request_timeout": 12,
    }

    context.provider = AnthropicChatProvider(
        api_key=api_key,
        model=model,
        max_retries=2,
        **context.anthropic_overrides,
    )
    context.chat_anthropic_class = mock_chat_class
    context.chat_anthropic_instance = mock_chat_instance


@when("I request plan generation from the Anthropic provider")
def step_request_plan_generation_anthropic(context):
    patcher = patch(
        "cleveragents.providers.llm.langchain_chat_provider.PlanGenerationGraph"
    )
    context.plan_generation_patcher = patcher
    mock_graph_class = patcher.start()
    _register_cleanup(context, patcher.stop)
    mock_graph_instance = MagicMock(name="PlanGenerationGraphInstance")
    mock_graph_instance.invoke.return_value = {
        "generated_changes": [],
        "validation_result": {"status": "PASS"},
        "error": None,
    }
    mock_graph_instance.stream.return_value = iter(())
    mock_graph_class.return_value = mock_graph_instance

    context.plan_generation_graph = mock_graph_instance
    context.plan_generation_graph_class = mock_graph_class

    context.response = context.provider.generate_changes(
        context.project,
        context.plan,
        context.contexts,
    )
    context.chat_anthropic_call = context.chat_anthropic_class.call_args
    context.plan_generation_graph_call = mock_graph_class.call_args


@then(
    'the Anthropic provider should construct ChatAnthropic with api key "{api_key}" and model "{model}"'
)
def step_assert_chat_anthropic_construction(context, api_key, model):
    call = getattr(context, "chat_anthropic_call", None)
    assert call is not None, "ChatAnthropic should have been constructed"
    call_args, call_kwargs = call
    assert call_args == (), "ChatAnthropic should be called with keyword arguments"
    assert call_kwargs["api_key"] == api_key
    assert call_kwargs["model"] == model

    graph_call = getattr(context, "plan_generation_graph_call", None)
    assert graph_call is not None, "PlanGenerationGraph should be constructed"
    _, graph_kwargs = graph_call
    assert graph_kwargs["llm"] is context.chat_anthropic_instance


@then("the Anthropic provider should apply the custom overrides to ChatAnthropic")
def step_assert_anthropic_overrides(context):
    call = getattr(context, "chat_anthropic_call", None)
    assert call is not None, "ChatAnthropic should have been constructed"
    _, call_kwargs = call
    overrides = getattr(context, "anthropic_overrides", None)
    assert overrides, "Custom overrides should have been captured"

    for key, value in overrides.items():
        assert call_kwargs.get(key) == value, f"Override {key} was not forwarded"


@then(
    'the Anthropic provider metadata should report name "{expected_name}" and model "{expected_model}"'
)
def step_assert_anthropic_metadata(context, expected_name, expected_model):
    provider = getattr(context, "provider", None)
    assert provider is not None, "Anthropic provider should exist in context"
    assert provider.name == expected_name
    assert provider.model_id == expected_model
