"""Step definitions for AI models providers unit tests."""

import json

from behave import given, then, when
from pydantic import ValidationError

from cleveragents.domain.models.aimodelsproviders.ai_models_providers import (
    ModelProviderConfigSchema,
    ModelProviderExtraAuthVars,
)
from cleveragents.domain.models.core.enums import ModelProvider


@given("I import the ModelProviderExtraAuthVars class")
def step_import_extra_auth_vars(context):
    """Import ModelProviderExtraAuthVars class."""
    context.model_class = ModelProviderExtraAuthVars


@given("I import the ModelProviderConfigSchema class")
def step_import_config_schema(context):
    """Import ModelProviderConfigSchema class."""
    context.model_class = ModelProviderConfigSchema


@given("I import the ModelProvider enum for provider config")
def step_import_model_provider_enum_for_config(context):
    """Import ModelProvider enum for provider config."""
    context.enum_class = ModelProvider


@when('I create a ModelProviderExtraAuthVars with var "{var}"')
def step_create_extra_auth_vars_with_var(context, var):
    """Create ModelProviderExtraAuthVars with var field."""
    context.instance = ModelProviderExtraAuthVars(var=var)


@when("I set maybeJSONFilePath to {value}")
def step_set_maybe_json_file_path(context, value):
    """Set maybeJSONFilePath field."""
    value_bool = value.lower() == "true"
    context.instance = ModelProviderExtraAuthVars(
        var=context.instance.var, maybe_j_s_o_n_file_path=value_bool
    )


@when("I set required to {value}")
def step_set_required(context, value):
    """Set required field."""
    value_bool = value.lower() == "true"
    context.instance = ModelProviderExtraAuthVars(
        var=context.instance.var,
        maybe_j_s_o_n_file_path=context.instance.maybe_j_s_o_n_file_path,
        required=value_bool,
    )


@when('I set default to "{value}"')
def step_set_default(context, value):
    """Set default field."""
    context.instance = ModelProviderExtraAuthVars(
        var=context.instance.var,
        maybe_j_s_o_n_file_path=context.instance.maybe_j_s_o_n_file_path,
        required=context.instance.required,
        default=value,
    )


@when('I create a ModelProviderExtraAuthVars with only var "{var}"')
def step_create_minimal_extra_auth_vars(context, var):
    """Create ModelProviderExtraAuthVars with only required field."""
    context.instance = ModelProviderExtraAuthVars(var=var)


@when('I create a ModelProviderExtraAuthVars using alias "{alias}"')
def step_create_with_alias(context, alias):
    """Create ModelProviderExtraAuthVars using field alias."""
    context.instance = ModelProviderExtraAuthVars(
        var="TEST_VAR",
        maybeJSONFilePath=True,  # Using alias
    )


@when('I create a ModelProviderConfigSchema with provider "{provider}"')
def step_create_config_schema_with_provider(context, provider):
    """Create ModelProviderConfigSchema with provider."""
    context.instance = ModelProviderConfigSchema(
        provider=provider, base_url="https://api.example.com"
    )


@when('I set base_url to "{url}"')
def step_set_base_url(context, url):
    """Set base_url field."""
    context.instance.base_url = url


@when('I set custom_provider to "{value}"')
def step_set_custom_provider(context, value):
    """Set custom_provider field."""
    context.instance.custom_provider = value


@when("I set has_a_w_s_auth to {value}")
def step_set_has_aws_auth(context, value):
    """Set has_a_w_s_auth field."""
    context.instance.has_a_w_s_auth = value.lower() == "true"


@when("I set has_claude_max_auth to {value}")
def step_set_has_claude_max_auth(context, value):
    """Set has_claude_max_auth field."""
    context.instance.has_claude_max_auth = value.lower() == "true"


@when("I set skip_auth to {value}")
def step_set_skip_auth(context, value):
    """Set skip_auth field."""
    context.instance.skip_auth = value.lower() == "true"


@when("I set local_only to {value}")
def step_set_local_only(context, value):
    """Set local_only field."""
    context.instance.local_only = value.lower() == "true"


@when('I set api_key_env_var to "{value}"')
def step_set_api_key_env_var(context, value):
    """Set api_key_env_var field."""
    context.instance.api_key_env_var = value


@when("I add extra_auth_vars list")
def step_add_extra_auth_vars_list(context):
    """Add extra_auth_vars list."""
    extra_var = ModelProviderExtraAuthVars(var="EXTRA_KEY", required=True)
    context.instance.extra_auth_vars = [extra_var]


@when("I create a ModelProviderConfigSchema with only required fields")
def step_create_minimal_config_schema(context):
    """Create ModelProviderConfigSchema with only required fields."""
    context.instance = ModelProviderConfigSchema(
        provider=ModelProvider.OPENAI, base_url="https://api.openai.com"
    )


@when("I create a ModelProviderConfigSchema with ModelProvider.ANTHROPIC")
def step_create_config_with_enum(context):
    """Create ModelProviderConfigSchema with enum value."""
    context.instance = ModelProviderConfigSchema(
        provider=ModelProvider.ANTHROPIC, base_url="https://api.anthropic.com"
    )


@when("I create a ModelProviderConfigSchema with whitespace in fields")
def step_create_config_with_whitespace(context):
    """Create ModelProviderConfigSchema with whitespace in fields."""
    context.instance = ModelProviderConfigSchema(
        provider=ModelProvider.OPENAI,
        base_url="  https://api.openai.com  ",
        custom_provider="  custom  ",
        api_key_env_var="  API_KEY  ",
    )


@when("I create a ModelProviderConfigSchema using aliases")
def step_create_config_using_aliases(context):
    """Create ModelProviderConfigSchema using field aliases."""
    context.instance = ModelProviderConfigSchema(
        provider=ModelProvider.OPENAI,
        baseUrl="https://api.openai.com",  # alias
        customProvider="custom",  # alias
        hasAWSAuth=True,  # alias
        hasClaudeMaxAuth=False,  # alias
        skipAuth=False,  # alias
        localOnly=False,  # alias
        apiKeyEnvVar="API_KEY",  # alias
        extraAuthVars=[],  # alias
    )


@when("I create a ModelProviderConfigSchema with empty extra_auth_vars list")
def step_create_config_with_empty_list(context):
    """Create ModelProviderConfigSchema with empty extra_auth_vars."""
    context.instance = ModelProviderConfigSchema(
        provider=ModelProvider.OPENAI,
        base_url="https://api.openai.com",
        extra_auth_vars=[],
    )


@when("I create a ModelProviderConfigSchema with multiple extra_auth_vars")
def step_create_config_with_multiple_auth_vars(context):
    """Create ModelProviderConfigSchema with multiple extra_auth_vars."""
    vars_list = [
        ModelProviderExtraAuthVars(var="VAR1", required=True),
        ModelProviderExtraAuthVars(var="VAR2", required=False),
        ModelProviderExtraAuthVars(var="VAR3", default="default_val"),
    ]
    context.instance = ModelProviderConfigSchema(
        provider=ModelProvider.OPENAI,
        base_url="https://api.openai.com",
        extra_auth_vars=vars_list,
    )


@when("I export it to dict with aliases")
def step_export_to_dict_with_aliases(context):
    """Export model to dictionary with aliases."""
    context.dict_output = context.instance.model_dump(by_alias=True)


@when("I serialize it to JSON")
def step_serialize_to_json(context):
    """Serialize model to JSON."""
    context.json_output = context.instance.model_dump_json(by_alias=True)


@when("I deserialize it to ModelProviderConfigSchema")
def step_deserialize_from_json(context):
    """Deserialize JSON to ModelProviderConfigSchema."""
    json_data = {
        "provider": "ModelProviderOpenAI",
        "baseUrl": "https://api.openai.com",
        "customProvider": "custom",
        "hasAWSAuth": True,
        "extraAuthVars": [{"var": "KEY1", "required": True}],
    }
    context.json_string = json.dumps(json_data)
    context.instance = ModelProviderConfigSchema.model_validate_json(
        context.json_string
    )


@when("I check the model configuration")
def step_check_model_config(context):
    """Check model configuration."""
    context.model_config = context.model_class.model_config


@when('I update the var field to "{value}"')
def step_update_var_field(context, value):
    """Update var field."""
    context.instance.var = value


@when('I update the base_url field to "{value}"')
def step_update_base_url_field(context, value):
    """Update base_url field."""
    context.instance.base_url = value


@when("I create a copy with updated fields")
def step_create_copy_with_updates(context):
    """Create a copy with updated fields."""
    context.original = context.instance
    if hasattr(context.instance, "var"):
        context.copy = context.instance.model_copy(update={"var": "NEW_VAR"})
    else:
        context.copy = context.instance.model_copy(
            update={"base_url": "https://new.com"}
        )


@when("I compare them for equality")
def step_compare_equality(context):
    """Compare instances for equality."""
    if (
        hasattr(context, "model_class")
        and context.model_class == ModelProviderExtraAuthVars
    ):
        context.instance1 = ModelProviderExtraAuthVars(var="VAR1")
        context.instance2 = ModelProviderExtraAuthVars(var="VAR1")
        context.instance3 = ModelProviderExtraAuthVars(var="VAR2")
    else:
        context.instance1 = ModelProviderConfigSchema(
            provider=ModelProvider.OPENAI, base_url="https://api.openai.com"
        )
        context.instance2 = ModelProviderConfigSchema(
            provider=ModelProvider.OPENAI, base_url="https://api.openai.com"
        )
        context.instance3 = ModelProviderConfigSchema(
            provider=ModelProvider.ANTHROPIC, base_url="https://api.anthropic.com"
        )


@when("I try to use them as dictionary keys")
def step_try_use_as_dict_keys(context):
    """Try to use instances as dictionary keys."""
    context.instance1 = ModelProviderExtraAuthVars(var="KEY1")
    context.instance2 = ModelProviderExtraAuthVars(var="KEY2")
    context.test_dict = {}
    try:
        context.test_dict[context.instance1] = "value1"
        context.test_dict[context.instance2] = "value2"
        context.can_be_key = True
        context.hash_error = None
    except TypeError as e:
        context.can_be_key = False
        context.hash_error = e


@when("I try to create an instance with missing required fields")
def step_create_with_missing_fields(context):
    """Try to create instance with missing required fields."""
    try:
        context.instance = ModelProviderConfigSchema()
        context.error_raised = False
    except ValidationError as e:
        context.error = e
        context.error_raised = True


@then('the var field should equal "{expected}"')
def step_verify_var_field(context, expected):
    """Verify var field value."""
    assert context.instance.var == expected


@then("the maybe_j_s_o_n_file_path field should be {expected}")
def step_verify_maybe_json_field(context, expected):
    """Verify maybe_j_s_o_n_file_path field."""
    if expected == "None":
        assert context.instance.maybe_j_s_o_n_file_path is None
    else:
        assert context.instance.maybe_j_s_o_n_file_path == (expected.lower() == "true")


@then("the required field should be {expected}")
def step_verify_required_field(context, expected):
    """Verify required field."""
    if expected == "None":
        assert context.instance.required is None
    else:
        assert context.instance.required == (expected.lower() == "true")


@then('the default field should equal "{expected}"')
def step_verify_default_field_string(context, expected):
    """Verify default field with string value."""
    assert context.instance.default == expected


@then("the default field should be {expected}")
def step_verify_default_field(context, expected):
    """Verify default field."""
    if expected == "None":
        assert context.instance.default is None
    else:
        assert context.instance.default == expected


@then("the model should strip whitespace from string fields")
def step_verify_whitespace_stripping(context):
    """Verify whitespace stripping."""
    assert context.instance.var == "TRIMMED_VAR"


@then("the field should be accessible as maybe_j_s_o_n_file_path")
def step_verify_field_accessible(context):
    """Verify field is accessible with snake_case name."""
    assert context.instance.maybe_j_s_o_n_file_path is True


@then("the model should populate by name")
def step_verify_populate_by_name(context):
    """Verify model populates by name."""
    assert context.instance.maybe_j_s_o_n_file_path is True


@then('the dict should contain "{key}" key')
def step_verify_dict_contains_key(context, key):
    """Verify dictionary contains specific key."""
    assert key in context.dict_output


@then('the dict should not contain "{key}" key')
def step_verify_dict_not_contains_key(context, key):
    """Verify dictionary does not contain specific key."""
    assert key not in context.dict_output


@then('the provider field should equal "{expected}"')
def step_verify_provider_field(context, expected):
    """Verify provider field value."""
    assert context.instance.provider == expected


@then('the base_url field should equal "{expected}"')
def step_verify_base_url_field(context, expected):
    """Verify base_url field value."""
    assert context.instance.base_url == expected


@then('the custom_provider field should equal "{expected}"')
def step_verify_custom_provider_field(context, expected):
    """Verify custom_provider field value."""
    assert context.instance.custom_provider == expected


@then("the has_a_w_s_auth field should be {expected}")
def step_verify_has_aws_auth_field(context, expected):
    """Verify has_a_w_s_auth field."""
    assert context.instance.has_a_w_s_auth == (expected.lower() == "true")


@then("the has_claude_max_auth field should be {expected}")
def step_verify_has_claude_max_auth_field(context, expected):
    """Verify has_claude_max_auth field."""
    assert context.instance.has_claude_max_auth == (expected.lower() == "true")


@then("the skip_auth field should be {expected}")
def step_verify_skip_auth_field(context, expected):
    """Verify skip_auth field."""
    assert context.instance.skip_auth == (expected.lower() == "true")


@then("the local_only field should be {expected}")
def step_verify_local_only_field(context, expected):
    """Verify local_only field."""
    assert context.instance.local_only == (expected.lower() == "true")


@then('the api_key_env_var field should equal "{expected}"')
def step_verify_api_key_env_var_field(context, expected):
    """Verify api_key_env_var field value."""
    assert context.instance.api_key_env_var == expected


@then("the extra_auth_vars should be a list")
def step_verify_extra_auth_vars_is_list(context):
    """Verify extra_auth_vars is a list."""
    assert isinstance(context.instance.extra_auth_vars, list)


@then("the provider field should be set")
def step_verify_provider_set(context):
    """Verify provider field is set."""
    assert context.instance.provider is not None


@then("the base_url field should be set")
def step_verify_base_url_set(context):
    """Verify base_url field is set."""
    assert context.instance.base_url is not None


@then("all optional fields should be None")
def step_verify_optional_fields_none(context):
    """Verify all optional fields are None."""
    assert context.instance.custom_provider is None
    assert context.instance.has_a_w_s_auth is None
    assert context.instance.has_claude_max_auth is None
    assert context.instance.skip_auth is None
    assert context.instance.local_only is None
    assert context.instance.api_key_env_var is None
    assert context.instance.extra_auth_vars is None


@then("the model should use enum values")
def step_verify_enum_values(context):
    """Verify model uses enum values."""
    assert context.instance.provider == "ModelProviderAnthropic"


@then("all string fields should have whitespace stripped")
def step_verify_all_whitespace_stripped(context):
    """Verify all string fields have whitespace stripped."""
    assert context.instance.base_url == "https://api.openai.com"
    assert context.instance.custom_provider == "custom"
    assert context.instance.api_key_env_var == "API_KEY"


@then("the model should validate assignment")
def step_verify_validate_assignment(context):
    """Verify model validates assignment."""
    # Assignment validation happens automatically in Pydantic
    assert True


@then('"{alias}" should map to {field}')
def step_verify_alias_mapping(context, alias, field):
    """Verify alias maps to field."""
    assert hasattr(context.instance, field.replace('"', ""))


@then("the dict should use camelCase keys")
def step_verify_camelcase_keys(context):
    """Verify dictionary uses camelCase keys."""
    assert "baseUrl" in context.dict_output


@then('"{key}" should be in the dict')
def step_verify_key_in_dict(context, key):
    """Verify key is in dictionary."""
    assert key in context.dict_output


@then("the extra_auth_vars should be an empty list")
def step_verify_empty_list(context):
    """Verify extra_auth_vars is empty list."""
    assert context.instance.extra_auth_vars == []


@then("the model should accept empty lists")
def step_verify_accepts_empty_lists(context):
    """Verify model accepts empty lists."""
    assert context.instance.extra_auth_vars == []


@then("each extra_auth_var should be a ModelProviderExtraAuthVars instance")
def step_verify_auth_vars_instances(context):
    """Verify each item is ModelProviderExtraAuthVars instance."""
    for item in context.instance.extra_auth_vars:
        assert isinstance(item, ModelProviderExtraAuthVars)


@then("the list should maintain order")
def step_verify_list_order(context):
    """Verify list maintains order."""
    assert context.instance.extra_auth_vars[0].var == "VAR1"
    assert context.instance.extra_auth_vars[1].var == "VAR2"
    assert context.instance.extra_auth_vars[2].var == "VAR3"


@then("the JSON should be valid")
def step_verify_valid_json(context):
    """Verify JSON is valid."""
    parsed = json.loads(context.json_output)
    assert parsed is not None


@then("it should contain all set fields")
def step_verify_json_contains_fields(context):
    """Verify JSON contains all set fields."""
    parsed = json.loads(context.json_output)
    assert "provider" in parsed
    assert "baseUrl" in parsed


@then("it should use aliases in the output")
def step_verify_json_uses_aliases(context):
    """Verify JSON uses aliases."""
    parsed = json.loads(context.json_output)
    assert "baseUrl" in parsed
    assert "base_url" not in parsed


@given("I have a JSON string with provider config")
def step_have_json_string(context):
    """Have a JSON string with provider config."""
    # JSON string is created in the when step
    pass


@then("the object should be correctly populated")
def step_verify_object_populated(context):
    """Verify object is correctly populated."""
    assert context.instance.provider == "ModelProviderOpenAI"
    assert context.instance.base_url == "https://api.openai.com"
    assert context.instance.custom_provider == "custom"
    assert context.instance.has_a_w_s_auth is True


@then("aliases should be resolved to field names")
def step_verify_aliases_resolved(context):
    """Verify aliases are resolved to field names."""
    assert hasattr(context.instance, "base_url")
    assert hasattr(context.instance, "has_a_w_s_auth")
    assert not hasattr(context.instance, "baseUrl")
    assert not hasattr(context.instance, "hasAWSAuth")


@then("str_strip_whitespace should be True")
def step_verify_strip_whitespace_config(context):
    """Verify str_strip_whitespace configuration."""
    assert context.model_config.get("str_strip_whitespace") is True


@then("validate_assignment should be True")
def step_verify_validate_assignment_config(context):
    """Verify validate_assignment configuration."""
    assert context.model_config.get("validate_assignment") is True


@then("arbitrary_types_allowed should be False")
def step_verify_arbitrary_types_config(context):
    """Verify arbitrary_types_allowed configuration."""
    assert context.model_config.get("arbitrary_types_allowed") is False


@then("populate_by_name should be True")
def step_verify_populate_by_name_config(context):
    """Verify populate_by_name configuration."""
    assert context.model_config.get("populate_by_name") is True


@then("use_enum_values should be True")
def step_verify_use_enum_values_config(context):
    """Verify use_enum_values configuration."""
    assert context.model_config.get("use_enum_values") is True


@then("the update should be validated")
def step_verify_update_validated(context):
    """Verify update is validated."""
    # Validation happens automatically in Pydantic
    assert True


@given("I have a ModelProviderExtraAuthVars instance")
def step_have_extra_auth_vars_instance(context):
    """Have a ModelProviderExtraAuthVars instance."""
    context.instance = ModelProviderExtraAuthVars(var="TEST_VAR", required=True)


@given("I have a ModelProviderConfigSchema instance")
def step_have_config_schema_instance(context):
    """Have a ModelProviderConfigSchema instance."""
    context.instance = ModelProviderConfigSchema(
        provider=ModelProvider.OPENAI,
        base_url="https://api.openai.com",
        custom_provider="custom",
    )


@then("the copy should have new values")
def step_verify_copy_new_values(context):
    """Verify copy has new values."""
    if hasattr(context.copy, "var"):
        assert context.copy.var == "NEW_VAR"
    else:
        assert context.copy.base_url == "https://new.com"


@then("the original should remain unchanged")
def step_verify_original_unchanged(context):
    """Verify original remains unchanged."""
    if hasattr(context.original, "var"):
        assert context.original.var != "NEW_VAR"
    else:
        assert context.original.base_url != "https://new.com"


@given("I have two ModelProviderExtraAuthVars instances")
def step_have_two_extra_auth_vars(context):
    """Have two ModelProviderExtraAuthVars instances."""
    context.model_class = ModelProviderExtraAuthVars


@given("I have two ModelProviderConfigSchema instances")
def step_have_two_config_schemas(context):
    """Have two ModelProviderConfigSchema instances."""
    context.model_class = ModelProviderConfigSchema


@then("identical instances should be equal")
def step_verify_identical_equal(context):
    """Verify identical instances are equal."""
    assert context.instance1 == context.instance2


@then("different instances should not be equal")
def step_verify_different_not_equal(context):
    """Verify different instances are not equal."""
    assert context.instance1 != context.instance3


@given("I have ModelProviderExtraAuthVars instances")
def step_have_extra_auth_vars_instances(context):
    """Have ModelProviderExtraAuthVars instances."""
    pass  # Instances created in when step


@then("the instances should not work as dictionary keys")
def step_verify_dict_keys_dont_work(context):
    """Verify instances don't work as dictionary keys."""
    assert context.can_be_key is False
    assert context.hash_error is not None


@then("a TypeError should be raised when hashing")
def step_verify_type_error_hashing(context):
    """Verify TypeError is raised when hashing."""
    assert isinstance(context.hash_error, TypeError)
    assert "unhashable" in str(context.hash_error)


@then("a validation error should be raised for missing fields")
def step_verify_validation_error_raised_missing(context):
    """Verify validation error is raised for missing fields."""
    assert context.error_raised is True


@then("the error should indicate missing fields")
def step_verify_error_indicates_missing(context):
    """Verify error indicates missing fields."""
    error_str = str(context.error)
    assert "provider" in error_str.lower() or "base_url" in error_str.lower()
