"""Step definitions for provider_fixes.feature."""

from __future__ import annotations

import logging
import os
from typing import Any
from unittest.mock import MagicMock

from behave import given, then, when  # type: ignore[import-untyped]


def _make_pf_mock_settings(
    *,
    openai: str | None = None,
    mock_providers: bool = False,
    env: str = "test",
) -> MagicMock:
    """Create a lightweight Settings-like mock."""
    settings = MagicMock()
    settings.openai_api_key = openai
    settings.anthropic_api_key = None
    settings.google_api_key = None
    settings.gemini_api_key = None
    settings.azure_api_key = None
    settings.openrouter_api_key = None
    settings.cohere_api_key = None
    settings.groq_api_key = None
    settings.together_api_key = None
    settings.default_provider = None
    settings.default_model = None
    settings.azure_openai_endpoint = None
    settings.azure_openai_api_version = None
    settings.azure_openai_deployment = None
    settings.openrouter_organization = None
    settings.mock_providers = mock_providers
    settings.env = env
    return settings


# ── Given steps ──────────────────────────────────────────────────────


@given("a provider-fix settings instance with openai configured")
def step_pf_settings_with_openai(context: Any) -> None:
    context.pf_settings = _make_pf_mock_settings(openai="sk-test-key")


@given("a provider-fix settings instance with no providers configured")
def step_pf_settings_no_providers(context: Any) -> None:
    context.pf_settings = _make_pf_mock_settings()


@given("provider-fix mock_providers is disabled")
def step_pf_mock_disabled(context: Any) -> None:
    context.pf_settings.mock_providers = False


@given("provider-fix mock_providers is disabled on that instance")
def step_pf_mock_disabled_on_instance(context: Any) -> None:
    context.pf_settings.mock_providers = False


@given("a provider-fix settings instance with mock_providers enabled")
def step_pf_settings_mock(context: Any) -> None:
    context.pf_settings = _make_pf_mock_settings(mock_providers=True)


@given('the provider-fix environment is set to "{env_name}"')
def step_pf_set_env(context: Any, env_name: str) -> None:
    context.pf_settings.env = env_name


@given("I create a provider-fix fresh settings instance")
def step_pf_fresh_settings(context: Any) -> None:
    from cleveragents.config.settings import Settings

    saved: dict[str, str | None] = {}
    for key in ("CLEVERAGENTS_MOCK_PROVIDERS",):
        saved[key] = os.environ.pop(key, None)
    try:
        Settings._instance = None  # type: ignore[attr-defined]
        context.pf_fresh_settings = Settings()
    finally:
        for key, val in saved.items():
            if val is not None:
                os.environ[key] = val


@given("I have a provider-fix FakeListLLM instance for testing")
def step_pf_create_fake_llm(context: Any) -> None:
    from langchain_community.llms import FakeListLLM

    context.pf_test_llm = FakeListLLM(
        responses=[
            "Dependencies: ['os', 'sys']",
            "Relevance: High",
            "Summary: test summary",
        ]
    )


# ── When steps ───────────────────────────────────────────────────────


@when("I create a provider-fix registry from those settings")
def step_pf_create_registry(context: Any) -> None:
    from cleveragents.providers.registry import ProviderRegistry

    context.pf_registry = ProviderRegistry(settings=context.pf_settings)


@when("I call provider-fix validate_provider_availability")
def step_pf_validate(context: Any) -> None:
    from cleveragents.config.settings import Settings

    # Clear all provider env vars so Settings sees no providers
    provider_env_keys = [
        "OPENAI_API_KEY",
        "ANTHROPIC_API_KEY",
        "GOOGLE_API_KEY",
        "GOOGLE_GENAI_API_KEY",
        "AZURE_OPENAI_API_KEY",
        "AZURE_API_KEY",
        "OPENROUTER_API_KEY",
        "GEMINI_API_KEY",
        "GOOGLE_GEMINI_API_KEY",
        "HF_TOKEN",
        "HUGGINGFACEHUB_API_TOKEN",
        "HUGGING_FACE_HUB_TOKEN",
        "COHERE_API_KEY",
        "PERPLEXITY_API_KEY",
        "GROQ_API_KEY",
        "TOGETHER_API_KEY",
    ]
    saved_env: dict[str, str | None] = {}
    if not context.pf_settings.openai_api_key:
        for key in provider_env_keys:
            saved_env[key] = os.environ.pop(key, None)

    # Set env vars for Settings to pick up
    env_overrides: dict[str, str] = {}
    if context.pf_settings.mock_providers:
        env_overrides["CLEVERAGENTS_MOCK_PROVIDERS"] = "true"
    if hasattr(context.pf_settings, "env"):
        env_overrides["CLEVERAGENTS_ENV"] = context.pf_settings.env
    for k, v in env_overrides.items():
        os.environ[k] = v

    try:
        Settings._instance = None  # type: ignore[attr-defined]
        real = Settings()
        if context.pf_settings.openai_api_key:
            real.openai_api_key = context.pf_settings.openai_api_key

        context.pf_error = None
        context.pf_warning_logged = False

        class _Catcher(logging.Handler):
            def __init__(self) -> None:
                super().__init__()
                self.warnings: list[str] = []

            def emit(self, record: logging.LogRecord) -> None:
                if record.levelno >= logging.WARNING:
                    self.warnings.append(record.getMessage())

        handler = _Catcher()
        log = logging.getLogger("cleveragents.config.settings")
        log.addHandler(handler)
        try:
            real.validate_provider_availability()
        except ValueError as exc:
            context.pf_error = exc
        finally:
            log.removeHandler(handler)
        context.pf_warning_logged = len(handler.warnings) > 0
    finally:
        for key, val in saved_env.items():
            if val is not None:
                os.environ[key] = val
            else:
                os.environ.pop(key, None)
        for k in env_overrides:
            os.environ.pop(k, None)


@when("I build the provider-fix AI provider from the container helper")
def step_pf_build_provider(context: Any) -> None:
    from cleveragents.application.container import get_ai_provider
    from cleveragents.providers.registry import (
        ProviderRegistry,
        reset_provider_registry,
    )

    reset_provider_registry()
    registry = ProviderRegistry(settings=context.pf_settings)
    context.pf_provider_result = get_ai_provider(
        settings=context.pf_settings,
        provider_registry=registry,
    )


@when("I attempt to create a provider-fix PlanGenerationGraph without an LLM")
def step_pf_plan_gen_no_llm(context: Any) -> None:
    from cleveragents.agents.graphs.plan_generation import (
        PlanGenerationGraph,
    )

    context.pf_error = None
    try:
        PlanGenerationGraph(llm=None)
    except ValueError as exc:
        context.pf_error = exc


@when("I attempt to create a provider-fix ContextAnalysisAgent without an LLM")
def step_pf_ctx_no_llm(context: Any) -> None:
    from cleveragents.agents.graphs.context_analysis import (
        ContextAnalysisAgent,
    )

    context.pf_error = None
    try:
        ContextAnalysisAgent(llm=None)
    except ValueError as exc:
        context.pf_error = exc


@when("I attempt to create a provider-fix AutoDebugAgent without an LLM")
def step_pf_debug_no_llm(context: Any) -> None:
    from cleveragents.agents.graphs.auto_debug import AutoDebugAgent

    context.pf_error = None
    try:
        AutoDebugAgent(llm=None)
    except ValueError as exc:
        context.pf_error = exc


@when("I create a provider-fix registry and request the default provider")
def step_pf_registry_default(context: Any) -> None:
    from cleveragents.providers.registry import ProviderRegistry

    context.pf_registry = ProviderRegistry(settings=context.pf_settings)
    context.pf_default = context.pf_registry.get_default_provider_type()


@when('I provider-fix resolve provider by name "{name}"')
def step_pf_resolve_by_name(context: Any, name: str) -> None:
    from cleveragents.providers.registry import (
        reset_provider_registry,
        resolve_provider_by_name,
    )

    reset_provider_registry()
    context.pf_error = None
    context.pf_resolved = None
    try:
        context.pf_resolved = resolve_provider_by_name(
            name, settings=context.pf_settings
        )
    except ValueError as exc:
        context.pf_error = exc


@when("I call provider-fix get_ai_provider with those settings")
def step_pf_get_ai_provider(context: Any) -> None:
    from cleveragents.application.container import get_ai_provider
    from cleveragents.providers.registry import (
        ProviderRegistry,
        reset_provider_registry,
    )

    reset_provider_registry()
    registry = ProviderRegistry(settings=context.pf_settings)
    context.pf_provider_result = get_ai_provider(
        settings=context.pf_settings,
        provider_registry=registry,
    )


@when("I create a provider-fix PlanGenerationGraph with that LLM")
def step_pf_create_graph(context: Any) -> None:
    from cleveragents.agents.graphs.plan_generation import (
        PlanGenerationGraph,
    )

    context.pf_error = None
    try:
        context.pf_graph = PlanGenerationGraph(llm=context.pf_test_llm)
    except Exception as exc:
        context.pf_error = exc


@when("I create a provider-fix ContextAnalysisAgent with that LLM")
def step_pf_create_ctx_agent(context: Any) -> None:
    from cleveragents.agents.graphs.context_analysis import (
        ContextAnalysisAgent,
    )

    context.pf_error = None
    try:
        context.pf_agent = ContextAnalysisAgent(llm=context.pf_test_llm)
    except Exception as exc:
        context.pf_error = exc


@when("I create a provider-fix AutoDebugAgent with that LLM")
def step_pf_create_debug_agent(context: Any) -> None:
    from cleveragents.agents.graphs.auto_debug import AutoDebugAgent

    context.pf_error = None
    try:
        context.pf_agent = AutoDebugAgent(llm=context.pf_test_llm)
    except Exception as exc:
        context.pf_error = exc


# ── Then steps ───────────────────────────────────────────────────────


@then('the provider-fix default provider type should be "{expected}"')
def step_pf_check_default(context: Any, expected: str) -> None:
    from cleveragents.providers.registry import ProviderType

    default = context.pf_registry.get_default_provider_type()
    assert default is not None, "Expected a default provider"
    assert default == ProviderType(expected), f"Expected {expected}, got {default}"


@then('a provider-fix ValueError should be raised containing "{fragment}"')
def step_pf_check_error(context: Any, fragment: str) -> None:
    assert context.pf_error is not None, (
        f"Expected ValueError with '{fragment}' but no error"
    )
    assert fragment in str(context.pf_error), (
        f"Expected '{fragment}' in: {context.pf_error}"
    )


@then("a provider-fix mock AI provider should be returned")
def step_pf_check_mock(context: Any) -> None:
    if context.pf_provider_result is not None:
        name = getattr(context.pf_provider_result, "_name", "")
        cls = type(context.pf_provider_result).__name__.lower()
        assert "mock" in name.lower() or "mock" in cls


@then("the provider-fix selection should be logged")
def step_pf_check_logging(context: Any) -> None:
    assert context.pf_default is not None


@then("I should receive a provider-fix AI provider instance")
def step_pf_check_instance(context: Any) -> None:
    assert context.pf_resolved is not None


@then("no provider-fix error should be raised")
def step_pf_no_error(context: Any) -> None:
    assert context.pf_error is None, f"Expected no error but got: {context.pf_error}"


@then("a provider-fix warning should be logged about mock_providers")
def step_pf_check_warning(context: Any) -> None:
    assert context.pf_warning_logged, "Expected a warning"


@then("provider-fix mock_providers should be False by default")
def step_pf_check_default_mock(context: Any) -> None:
    assert context.pf_fresh_settings.mock_providers is False


@then("a provider-fix mock provider or None should be returned")
def step_pf_mock_or_none(context: Any) -> None:
    result = context.pf_provider_result
    if result is not None:
        cls = type(result).__name__.lower()
        assert "mock" in cls or hasattr(result, "_name")


@then("the provider-fix graph should be created successfully")
def step_pf_graph_ok(context: Any) -> None:
    assert context.pf_error is None, f"Expected no error but got: {context.pf_error}"
    assert context.pf_graph is not None


@then("the provider-fix agent should be created successfully")
def step_pf_agent_ok(context: Any) -> None:
    assert context.pf_error is None, f"Expected no error but got: {context.pf_error}"
    assert context.pf_agent is not None
