"""Behave steps for built-in actor v3 YAML format."""

from __future__ import annotations

import yaml
from behave import given, then, when
from behave.runner import Context

from cleveragents.actor.schema import ActorConfigSchema, is_v3_yaml
from cleveragents.providers.registry import (
    ProviderCapabilities,
    ProviderInfo,
    ProviderType,
)


@given("a configured provider registry with {provider_model}")
def step_configured_provider(context: Context, provider_model: str) -> None:
    """Set up a stubbed provider registry with a single provider.

    Args:
        context: Behave context.
        provider_model: Provider/model pair (e.g., "openai/gpt-4").
    """
    parts = provider_model.split("/")
    provider_name = parts[0]
    model_name = parts[1] if len(parts) > 1 else "default"

    from features.steps.actor_registry_steps import (
        _StubActorService,
        _StubProviderRegistry,
        _StubSettings,
        _default_defaults,
    )

    context.actor_service = _StubActorService()
    context.provider_registry = _StubProviderRegistry(
        [
            ProviderInfo(
                provider_type=ProviderType.OPENAI
                if provider_name == "openai"
                else ProviderType.ANTHROPIC,
                name=provider_name,
                api_key_env_var="ENV",
                default_model=model_name,
                capabilities=ProviderCapabilities(supports_tool_calls=True),
                is_configured=True,
            )
        ]
    )
    context.settings = _StubSettings(_default_defaults())

    from cleveragents.actor.registry import ActorRegistry

    context.registry = ActorRegistry(
        actor_service=context.actor_service,
        provider_registry=context.provider_registry,
        settings=context.settings,
    )


@given("the built-in actors have been ensured")
def step_builtin_actors_ensured_as_given(context: Context) -> None:
    """Resolve virtual built-in actors as a precondition and set current actor.

    After issue #10923, built-in actors are virtual (never persisted to DB).
    We use _resolve_virtual_builtin_actors() instead of ensure_built_in_actors().
    """
    virtual_actors = context.registry._resolve_virtual_builtin_actors()
    if virtual_actors:
        context.current_actor = virtual_actors[0]


@when("built-in actors are ensured")
def step_ensure_builtin_actors(context: Context) -> None:
    """Resolve virtual built-in actors via the registry.

    After issue #10923, built-in actors are virtual (never persisted to DB).
    """
    context.generated_actors = context.registry._resolve_virtual_builtin_actors()
    if context.generated_actors:
        context.current_actor = context.generated_actors[0]


@when('I call agents actor run with mock "{actor_name}"')
def step_call_actor_run_with_mock(context: Context, actor_name: str) -> None:
    """Simulate the actor-lookup phase of ``agents actor run`` using registry.

    This step intentionally does NOT invoke the real CLI or any LLM.  It only
    exercises the actor-lookup path: virtual built-in actors are resolved
    from the provider registry (not from the stub service's actors dict).
    """
    actor = context.registry.get_actor(actor_name)
    assert actor is not None, f"Actor '{actor_name}' not found via registry.get_actor()"
    context.current_actor = actor


@then("the {actor_name} actor should have yaml_text")
def step_actor_has_yaml_text(context: Context, actor_name: str) -> None:
    """Verify the virtual built-in actor has yaml_text populated."""
    normalized_name = actor_name.replace("/", "/")
    actor = context.registry.get_actor(normalized_name)
    assert actor is not None, f"Actor {normalized_name} not found via registry"
    assert actor.yaml_text is not None, f"Actor {normalized_name} has no yaml_text"
    context.current_actor = actor


@then('the yaml_text should contain "{expected_text}"')
def step_yaml_text_contains(context: Context, expected_text: str) -> None:
    """Verify the yaml_text contains expected substring."""
    actor = getattr(context, "current_actor", None)
    assert actor is not None, "No current actor set"
    assert actor.yaml_text is not None, "Actor has no yaml_text"
    assert expected_text in actor.yaml_text, (
        f"Expected '{expected_text}' in yaml_text, got: {actor.yaml_text}"
    )


@then('the yaml_text should not contain "{unexpected_text}"')
def step_yaml_text_not_contains(context: Context, unexpected_text: str) -> None:
    """Verify the yaml_text does not contain the given substring."""
    actor = getattr(context, "current_actor", None)
    assert actor is not None, "No current actor set"
    assert actor.yaml_text is not None, "Actor has no yaml_text"
    assert unexpected_text not in actor.yaml_text, (
        f"Expected '{unexpected_text}' NOT to be in yaml_text, but found it in: {actor.yaml_text}"
    )


@then('the {actor_name} actor yaml_text should contain "{expected_text}"')
def step_specific_actor_yaml_contains(
    context: Context, actor_name: str, expected_text: str
) -> None:
    """Verify a specific virtual built-in actor's yaml_text contains expected substring."""
    normalized_name = actor_name.replace("/", "/")
    actor = context.registry.get_actor(normalized_name)
    assert actor is not None, f"Actor {normalized_name} not found via registry"
    assert actor.yaml_text is not None, f"Actor {normalized_name} has no yaml_text"
    assert expected_text in actor.yaml_text, (
        f"Expected '{expected_text}' in yaml_text, got: {actor.yaml_text}"
    )
    context.current_actor = actor


@then("the command should not fail with v3 format error")
def step_no_v3_format_error(context: Context) -> None:
    """Verify the actor would not fail due to missing v3 YAML format."""
    actor = getattr(context, "current_actor", None)
    assert actor is not None, "No current actor set"
    assert actor.yaml_text is not None, (
        "Actor has no yaml_text - would fail with v3 format error"
    )
    config_blob = yaml.safe_load(actor.yaml_text)
    assert is_v3_yaml(config_blob), (
        f"Actor yaml_text is not v3 format - would fail with v3 format error: {config_blob}"
    )


@then("the config parser should recognize the actor as v3 format")
def step_parser_recognizes_v3(context: Context) -> None:
    """Verify the config parser recognizes the actor as v3 format."""
    actor = getattr(context, "current_actor", None)
    assert actor is not None, "No current actor set"

    # Parse the yaml_text and check if it's recognized as v3
    if actor.yaml_text:
        config_blob = yaml.safe_load(actor.yaml_text)
        assert is_v3_yaml(config_blob), f"Config not recognized as v3: {config_blob}"


@then("the generated yaml_text should be valid v3 ActorConfigSchema")
def step_yaml_valid_v3_schema(context: Context) -> None:
    """Verify the generated YAML is valid v3 ActorConfigSchema."""
    actor = getattr(context, "current_actor", None)
    assert actor is not None, "No current actor set"
    assert actor.yaml_text is not None, "Actor has no yaml_text"

    # Parse and validate against schema
    config_blob = yaml.safe_load(actor.yaml_text)
    try:
        ActorConfigSchema.model_validate(config_blob)
    except Exception as exc:
        raise AssertionError(f"YAML is not valid v3 ActorConfigSchema: {exc}") from exc


@then("the yaml_text should parse without errors")
def step_yaml_parses_without_errors(context: Context) -> None:
    """Verify the yaml_text can be parsed as valid YAML."""
    actor = getattr(context, "current_actor", None)
    assert actor is not None, "No current actor set"
    assert actor.yaml_text is not None, "Actor has no yaml_text"

    try:
        yaml.safe_load(actor.yaml_text)
    except yaml.YAMLError as exc:
        raise AssertionError(f"YAML failed to parse: {exc}") from exc
