"""Step definitions for enum coverage testing."""

from behave import given, then, when

from cleveragents.domain.models.core.enums import (
    FallbackType,
    ModelErrKind,
    ModelProvider,
    ModelPublisher,
)


# ModelPublisher enum tests
@given("I import the ModelPublisher enum")
def step_import_model_publisher(context):
    """Import the ModelPublisher enum."""
    context.enum_class = ModelPublisher
    context.current_enum = ModelPublisher


@when("I check all ModelPublisher values")
def step_check_model_publisher_values(context):
    """Check all ModelPublisher enum values."""
    context.enum_values = {
        "OPENAI": ModelPublisher.OPENAI,
        "ANTHROPIC": ModelPublisher.ANTHROPIC,
        "GOOGLE": ModelPublisher.GOOGLE,
        "DEEPSEEK": ModelPublisher.DEEPSEEK,
        "PERPLEXITY": ModelPublisher.PERPLEXITY,
        "QWEN": ModelPublisher.QWEN,
        "MISTRAL": ModelPublisher.MISTRAL,
    }


@then('ModelPublisher.{member} should equal "{expected}"')
def step_verify_model_publisher_value(context, member, expected):
    """Verify ModelPublisher enum member value."""
    actual = getattr(ModelPublisher, member)
    assert actual.value == expected, f"Expected {expected}, got {actual.value}"


@given("I have a ModelPublisher enum value")
def step_have_model_publisher_value(context):
    """Have a ModelPublisher enum value."""
    context.enum_value = ModelPublisher.OPENAI


@when("I convert ModelPublisher.{member} to string")
def step_convert_model_publisher_to_string(context, member):
    """Convert ModelPublisher member to string."""
    context.enum_value = getattr(ModelPublisher, member)
    context.string_value = str(context.enum_value)


# ModelErrKind enum tests
@given("I import the ModelErrKind enum")
def step_import_model_err_kind(context):
    """Import the ModelErrKind enum."""
    context.enum_class = ModelErrKind
    context.current_enum = ModelErrKind


@when("I check all ModelErrKind values")
def step_check_model_err_kind_values(context):
    """Check all ModelErrKind enum values."""
    context.enum_values = {
        "OVERLOADED": ModelErrKind.OVERLOADED,
        "CONTEXT_TOO_LONG": ModelErrKind.CONTEXT_TOO_LONG,
        "RATE_LIMITED": ModelErrKind.RATE_LIMITED,
        "SUBSCRIPTION_QUOTA_EXHAUSTED": ModelErrKind.SUBSCRIPTION_QUOTA_EXHAUSTED,
        "OTHER": ModelErrKind.OTHER,
        "CACHE_SUPPORT": ModelErrKind.CACHE_SUPPORT,
    }


@then('ModelErrKind.{member} should equal "{expected}"')
def step_verify_model_err_kind_value(context, member, expected):
    """Verify ModelErrKind enum member value."""
    actual = getattr(ModelErrKind, member)
    assert actual.value == expected, f"Expected {expected}, got {actual.value}"


@given("I have a ModelErrKind enum value")
def step_have_model_err_kind_value(context):
    """Have a ModelErrKind enum value."""
    context.enum_value = ModelErrKind.RATE_LIMITED


@when("I convert ModelErrKind.{member} to string")
def step_convert_model_err_kind_to_string(context, member):
    """Convert ModelErrKind member to string."""
    context.enum_value = getattr(ModelErrKind, member)
    context.string_value = str(context.enum_value)


# FallbackType enum tests
@given("I import the FallbackType enum")
def step_import_fallback_type(context):
    """Import the FallbackType enum."""
    context.enum_class = FallbackType
    context.current_enum = FallbackType


@when("I check all FallbackType values")
def step_check_fallback_type_values(context):
    """Check all FallbackType enum values."""
    context.enum_values = {
        "ERROR": FallbackType.ERROR,
        "CONTEXT": FallbackType.CONTEXT,
        "PROVIDER": FallbackType.PROVIDER,
    }


@then('FallbackType.{member} should equal "{expected}"')
def step_verify_fallback_type_value(context, member, expected):
    """Verify FallbackType enum member value."""
    actual = getattr(FallbackType, member)
    assert actual.value == expected, f"Expected {expected}, got {actual.value}"


@given("I have a FallbackType enum value")
def step_have_fallback_type_value(context):
    """Have a FallbackType enum value."""
    context.enum_value = FallbackType.ERROR


@when("I convert FallbackType.{member} to string")
def step_convert_fallback_type_to_string(context, member):
    """Convert FallbackType member to string."""
    context.enum_value = getattr(FallbackType, member)
    context.string_value = str(context.enum_value)


# ModelProvider enum tests
@given("I import the ModelProvider enum")
def step_import_model_provider(context):
    """Import the ModelProvider enum."""
    context.enum_class = ModelProvider
    context.current_enum = ModelProvider


@when("I check all ModelProvider values")
def step_check_model_provider_values(context):
    """Check all ModelProvider enum values."""
    context.enum_values = {
        "OPENROUTER": ModelProvider.OPENROUTER,
        "OPENAI": ModelProvider.OPENAI,
        "ANTHROPIC": ModelProvider.ANTHROPIC,
        "ANTHROPIC_CLAUDE_MAX": ModelProvider.ANTHROPIC_CLAUDE_MAX,
        "GOOGLE_AI_STUDIO": ModelProvider.GOOGLE_AI_STUDIO,
        "GOOGLE_VERTEX": ModelProvider.GOOGLE_VERTEX,
        "AZURE_OPENAI": ModelProvider.AZURE_OPENAI,
        "DEEPSEEK": ModelProvider.DEEPSEEK,
        "PERPLEXITY": ModelProvider.PERPLEXITY,
        "AMAZON_BEDROCK": ModelProvider.AMAZON_BEDROCK,
    }


@then('ModelProvider.{member} should equal "{expected}"')
def step_verify_model_provider_value(context, member, expected):
    """Verify ModelProvider enum member value."""
    actual = getattr(ModelProvider, member)
    assert actual.value == expected, f"Expected {expected}, got {actual.value}"


@given("I have a ModelProvider enum value")
def step_have_model_provider_value(context):
    """Have a ModelProvider enum value."""
    context.enum_value = ModelProvider.OPENAI


@when("I convert ModelProvider.{member} to string")
def step_convert_model_provider_to_string(context, member):
    """Convert ModelProvider member to string."""
    context.enum_value = getattr(ModelProvider, member)
    context.string_value = str(context.enum_value)


# Generic enum tests
@then("it should be a string type")
def step_verify_string_type(context):
    """Verify the value is a string."""
    assert isinstance(context.string_value, str), (
        f"Not a string: {type(context.string_value)}"
    )


@then('it should equal "{expected}"')
def step_verify_string_value(context, expected):
    """Verify the string value matches expected."""
    # For enums that inherit from str, Enum, the value is the string representation
    actual_value = (
        context.enum_value.value
        if hasattr(context, "enum_value")
        else context.string_value
    )
    assert actual_value == expected, f"Expected {expected}, got {actual_value}"


@when("I check enum membership")
def step_check_enum_membership(context):
    """Check enum membership."""
    context.membership_checks = {}


@then('"{value}" should be a valid {enum_name} value')
def step_verify_valid_enum_value(context, value, enum_name):
    """Verify a value is valid for the enum."""
    enum_class = context.current_enum

    # Check if we can create enum from value
    try:
        member = enum_class(value)
        assert member.value == value
        context.membership_checks[value] = True
    except ValueError as exc:
        raise AssertionError(f"{value} is not a valid {enum_name} value") from exc


@then('"{value}" should not be a valid {enum_name} value')
def step_verify_invalid_enum_value(context, value, enum_name):
    """Verify a value is not valid for the enum."""
    enum_class = context.current_enum

    # Check that we cannot create enum from value
    try:
        enum_class(value)
        raise AssertionError(f"{value} should not be a valid {enum_name} value")
    except ValueError:
        context.membership_checks[value] = False


@when("I iterate over all {enum_name} values")
def step_iterate_enum_values(context, enum_name):
    """Iterate over all enum values."""
    context.enum_members = list(context.current_enum)
    context.member_count = len(context.enum_members)


@then("I should get {count:d} enum members")
def step_verify_enum_count(context, count):
    """Verify the number of enum members."""
    assert context.member_count == count, (
        f"Expected {count} members, got {context.member_count}"
    )


@then("each member should be a {enum_name} instance")
def step_verify_enum_instances(context, enum_name):
    """Verify each member is an instance of the enum."""
    for member in context.enum_members:
        assert isinstance(member, context.current_enum), (
            f"{member} is not a {enum_name} instance"
        )


# Enum comparison tests
@given("I have ModelPublisher enum values")
def step_have_model_publisher_values_for_comparison(context):
    """Have ModelPublisher enum values for comparison."""
    context.enum1 = ModelPublisher.OPENAI
    context.enum2 = ModelPublisher.OPENAI
    context.enum3 = ModelPublisher.ANTHROPIC


@when("I compare enum values")
def step_compare_enum_values(context):
    """Compare enum values."""
    context.comparison_results = {
        "equal": context.enum1 == context.enum2,
        "not_equal": context.enum1 != context.enum3,
        "same_is": context.enum1 is ModelPublisher.OPENAI,
    }


@then("ModelPublisher.OPENAI should equal ModelPublisher.OPENAI")
def step_verify_enum_equality(context):
    """Verify enum equality."""
    assert context.comparison_results["equal"], "Enum values should be equal"


@then("ModelPublisher.OPENAI should not equal ModelPublisher.ANTHROPIC")
def step_verify_enum_inequality(context):
    """Verify enum inequality."""
    assert context.comparison_results["not_equal"], "Enum values should not be equal"


# Enum attribute tests
@when("I access the name attribute of ModelPublisher.{member}")
def step_access_enum_name(context, member):
    """Access the name attribute of an enum member."""
    enum_member = getattr(ModelPublisher, member)
    context.enum_name = enum_member.name


@then('the name should be "{expected}"')
def step_verify_enum_name(context, expected):
    """Verify the enum name attribute."""
    assert context.enum_name == expected, (
        f"Expected name {expected}, got {context.enum_name}"
    )


@when("I access the value attribute of ModelPublisher.{member}")
def step_access_enum_value(context, member):
    """Access the value attribute of an enum member."""
    enum_member = getattr(ModelPublisher, member)
    context.enum_value_attr = enum_member.value


@then('the value should be "{expected}"')
def step_verify_enum_value_attr(context, expected):
    """Verify the enum value attribute."""
    assert context.enum_value_attr == expected, (
        f"Expected value {expected}, got {context.enum_value_attr}"
    )


# Creating enum from value
@when('I create ModelProvider from value "{value}"')
def step_create_enum_from_value(context, value):
    """Create enum from string value."""
    try:
        context.created_enum = ModelProvider(value)
        context.creation_successful = True
    except ValueError as e:
        context.created_enum = None
        context.creation_successful = False
        context.creation_error = e


@then("I should get ModelProvider.{member}")
def step_verify_created_enum(context, member):
    """Verify the created enum member."""
    expected = getattr(ModelProvider, member)
    assert context.creation_successful, "Enum creation failed"
    assert context.created_enum == expected, (
        f"Expected {expected}, got {context.created_enum}"
    )
    assert context.created_enum is expected, "Enum instances should be identical"


# Enum hash tests
@given("I have ModelErrKind enum values")
def step_have_model_err_kind_values_for_hash(context):
    """Have ModelErrKind enum values for hash testing."""
    context.enum_values_for_hash = [
        ModelErrKind.OVERLOADED,
        ModelErrKind.RATE_LIMITED,
        ModelErrKind.OTHER,
    ]


@when("I use enum values as dictionary keys")
def step_use_enum_as_dict_keys(context):
    """Use enum values as dictionary keys."""
    context.enum_dict = {}
    for i, enum_val in enumerate(context.enum_values_for_hash):
        context.enum_dict[enum_val] = f"value_{i}"

    # Test hash consistency
    context.hash_values = [hash(e) for e in context.enum_values_for_hash]


@then("the enum values should work as dictionary keys")
def step_verify_enum_dict_keys(context):
    """Verify enums work as dictionary keys."""
    assert len(context.enum_dict) == len(context.enum_values_for_hash)

    # Verify we can retrieve values
    assert context.enum_dict[ModelErrKind.OVERLOADED] == "value_0"
    assert context.enum_dict[ModelErrKind.RATE_LIMITED] == "value_1"
    assert context.enum_dict[ModelErrKind.OTHER] == "value_2"


@then("the hash should be consistent")
def step_verify_hash_consistency(context):
    """Verify hash values are consistent."""
    # Re-hash the same enums
    new_hash_values = [hash(e) for e in context.enum_values_for_hash]

    # Hashes should be the same
    for old_hash, new_hash in zip(context.hash_values, new_hash_values, strict=False):
        assert old_hash == new_hash, "Hash values should be consistent"


# Enum string representation tests
@when("I get the string representation")
def step_get_string_representation(context):
    """Get string representation of enum."""
    context.repr_value = repr(context.enum_value)
    context.str_value = str(context.enum_value)


@then('repr should contain "{expected}"')
def step_verify_repr_contains(context, expected):
    """Verify repr contains expected string."""
    assert expected in context.repr_value, (
        f"'{expected}' not in repr: {context.repr_value}"
    )


@then('str should equal "{expected}"')
def step_verify_str_equals(context, expected):
    """Verify str equals expected."""
    # For str, Enum classes, str() returns the value, not the name
    assert context.enum_value.value == expected, (
        f"Expected str '{expected}', got '{context.enum_value.value}'"
    )
