"""Behave steps for complete coverage of config_service.py."""

from __future__ import annotations

import tempfile
from pathlib import Path
from typing import Any
from unittest.mock import patch

import tomlkit
from behave import given, then, when

from cleveragents.application.services.config_service import (
    _REGISTRY,
    ConfigEntry,
    ConfigLevel,
    ConfigService,
    ResolvedValue,
    _env_name,
    _register,
)

# ---------------------------------------------------------------------------
# ConfigLevel enum
# ---------------------------------------------------------------------------


@then("ConfigLevel should have values CLI_FLAG ENV_VAR LOCAL PROJECT GLOBAL DEFAULT")
def step_config_level_enum(context: Any) -> None:
    assert ConfigLevel.CLI_FLAG.value == "cli_flag"
    assert ConfigLevel.ENV_VAR.value == "env_var"
    assert ConfigLevel.LOCAL.value == "local"
    assert ConfigLevel.PROJECT.value == "project"
    assert ConfigLevel.GLOBAL.value == "global"
    assert ConfigLevel.DEFAULT.value == "default"
    assert len(ConfigLevel) == 6


# ---------------------------------------------------------------------------
# ConfigEntry dataclass
# ---------------------------------------------------------------------------


@given('a ConfigEntry with key "test.key" and type str')
def step_create_config_entry(context: Any) -> None:
    context.entry = ConfigEntry(
        key="test.key",
        python_type=str,
        default="default_val",
        env_var="CLEVERAGENTS_TEST_KEY",
        project_scopable=True,
        description="A test key.",
        section="test",
    )


@then("the ConfigEntry fields should match the provided values")
def step_verify_config_entry(context: Any) -> None:
    e = context.entry
    assert e.key == "test.key"
    assert e.python_type is str
    assert e.default == "default_val"
    assert e.env_var == "CLEVERAGENTS_TEST_KEY"
    assert e.project_scopable is True
    assert e.description == "A test key."
    assert e.section == "test"


# ---------------------------------------------------------------------------
# ResolvedValue dataclass
# ---------------------------------------------------------------------------


@given('a ResolvedValue with key "test.key" and source DEFAULT')
def step_create_resolved_value(context: Any) -> None:
    context.resolved = ResolvedValue(
        key="test.key",
        value="default_val",
        source=ConfigLevel.DEFAULT,
    )


@then("the ResolvedValue fields should match including an empty chain by default")
def step_verify_resolved_value(context: Any) -> None:
    rv = context.resolved
    assert rv.key == "test.key"
    assert rv.value == "default_val"
    assert rv.source == ConfigLevel.DEFAULT
    assert rv.chain == []


# ---------------------------------------------------------------------------
# _env_name helper
# ---------------------------------------------------------------------------


@when('I call _env_name with section "core" and key "log_level"')
def step_call_env_name(context: Any) -> None:
    context.env_name_result = _env_name("core", "log_level")


@then('the env name should be "CLEVERAGENTS_CORE_LOG_LEVEL"')
def step_verify_env_name(context: Any) -> None:
    assert context.env_name_result == "CLEVERAGENTS_CORE_LOG_LEVEL"


# ---------------------------------------------------------------------------
# _register
# ---------------------------------------------------------------------------


@when('I register a custom key "custom.test_key" with type int and default 42')
def step_register_custom_key(context: Any) -> None:
    _register("custom", "test_key", int, 42, description="Custom test key.")


@then('the registry should contain "custom.test_key" with default 42')
def step_verify_custom_key(context: Any) -> None:
    entry = _REGISTRY.get("custom.test_key")
    assert entry is not None
    assert entry.default == 42
    assert entry.python_type is int
    assert entry.env_var == "CLEVERAGENTS_CUSTOM_TEST_KEY"


@when('I register a key "custom.explicit_env" with explicit env_var "MY_CUSTOM_VAR"')
def step_register_explicit_env(context: Any) -> None:
    _register(
        "custom",
        "explicit_env",
        str,
        "",
        env_var="MY_CUSTOM_VAR",
        description="Explicit env var.",
    )


@then('the entry "custom.explicit_env" should have env_var "MY_CUSTOM_VAR"')
def step_verify_explicit_env(context: Any) -> None:
    entry = _REGISTRY.get("custom.explicit_env")
    assert entry is not None
    assert entry.env_var == "MY_CUSTOM_VAR"


# ---------------------------------------------------------------------------
# _build_catalog
# ---------------------------------------------------------------------------


@then(
    "the registry should contain keys from sections core plan provider sandbox context index"
)
def step_verify_catalog_sections(context: Any) -> None:
    sections_found: set[str] = set()
    for entry in _REGISTRY.values():
        sections_found.add(entry.section)
    for expected in ("core", "plan", "provider", "sandbox", "context", "index"):
        assert expected in sections_found, f"Section '{expected}' not found in registry"


@then("the registry should contain at least 20 keys")
def step_verify_catalog_count(context: Any) -> None:
    # _build_catalog registers ~105 keys from the source
    assert len(_REGISTRY) >= 20, f"Expected >=20 keys, got {len(_REGISTRY)}"


# ---------------------------------------------------------------------------
# ConfigService instantiation
# ---------------------------------------------------------------------------


@given("a temporary directory for config service")
def step_temp_dir(context: Any) -> None:
    context.temp_dir = Path(tempfile.mkdtemp())
    context.config_dir = context.temp_dir / "config"
    context.config_path = context.config_dir / "config.toml"
    context.svc = None
    context.result = None
    context.error = None


@when("I create a ConfigService with custom paths")
def step_create_service_custom(context: Any) -> None:
    context.config_dir.mkdir(parents=True, exist_ok=True)
    context.svc = ConfigService(
        config_dir=context.config_dir,
        config_path=context.config_path,
    )


@then("the service should use the custom config directory and path")
def step_verify_custom_paths(context: Any) -> None:
    assert context.svc._config_dir == context.config_dir
    assert context.svc._config_path == context.config_path


@when("I create a ConfigService with default paths")
def step_create_service_default(context: Any) -> None:
    context.svc = ConfigService()


@then('the service config path should end with ".cleveragents/config.toml"')
def step_verify_default_path(context: Any) -> None:
    path_str = str(context.svc._config_path)
    assert path_str.endswith(".cleveragents/config.toml"), f"Got: {path_str}"


# ---------------------------------------------------------------------------
# registry(), get_entry(), registered_keys()
# ---------------------------------------------------------------------------


@when("I call ConfigService.registry")
def step_call_registry(context: Any) -> None:
    context.result = ConfigService.registry()


@then("it should return a dict with the same keys as the internal registry")
def step_verify_registry_copy(context: Any) -> None:
    assert isinstance(context.result, dict)
    assert set(context.result.keys()) == set(_REGISTRY.keys())
    # Verify it's a copy, not the same object
    assert context.result is not _REGISTRY


@when('I call ConfigService.get_entry with "core.log.level"')
def step_call_get_entry_known(context: Any) -> None:
    context.result = ConfigService.get_entry("core.log.level")


@then('it should return a ConfigEntry with key "core.log.level"')
def step_verify_get_entry_known(context: Any) -> None:
    assert isinstance(context.result, ConfigEntry)
    assert context.result.key == "core.log.level"


@when('I call ConfigService.get_entry with "nonexistent.key"')
def step_call_get_entry_unknown(context: Any) -> None:
    context.result = ConfigService.get_entry("nonexistent.key")


@then("it should return None")
def step_verify_get_entry_none(context: Any) -> None:
    assert context.result is None


@when("I call ConfigService.registered_keys")
def step_call_registered_keys(context: Any) -> None:
    context.result = ConfigService.registered_keys()


@then('it should return a sorted list containing "core.log.level"')
def step_verify_registered_keys(context: Any) -> None:
    assert isinstance(context.result, list)
    assert context.result == sorted(context.result)
    assert "core.log.level" in context.result


# ---------------------------------------------------------------------------
# read_config
# ---------------------------------------------------------------------------


@when("I create a ConfigService pointing to a missing config file")
def step_service_missing_file(context: Any) -> None:
    context.svc = ConfigService(
        config_dir=context.config_dir,
        config_path=context.config_dir / "nonexistent.toml",
    )


@then("read_config should return an empty dict")
def step_verify_read_empty(context: Any) -> None:
    data = context.svc.read_config()
    assert data == {}


@given('a TOML config file with key "core.log.level" set to "DEBUG"')
def step_write_toml_log_level(context: Any) -> None:
    context.config_dir.mkdir(parents=True, exist_ok=True)
    doc = tomlkit.document()
    doc["core.log.level"] = "DEBUG"
    with open(context.config_path, "w") as fh:
        tomlkit.dump(doc, fh)


@when("I create a ConfigService pointing to that TOML file")
def step_service_existing_file(context: Any) -> None:
    context.svc = ConfigService(
        config_dir=context.config_dir,
        config_path=context.config_path,
    )


@then('read_config should return a dict with "core.log.level" equal to "DEBUG"')
def step_verify_read_debug(context: Any) -> None:
    data = context.svc.read_config()
    assert data.get("core.log.level") == "DEBUG"


# ---------------------------------------------------------------------------
# write_config
# ---------------------------------------------------------------------------


@when("I create a ConfigService with a nested non-existent config directory")
def step_service_nested_dir(context: Any) -> None:
    nested = context.temp_dir / "a" / "b" / "c"
    context.config_dir = nested
    context.config_path = nested / "config.toml"
    context.svc = ConfigService(
        config_dir=nested,
        config_path=context.config_path,
    )


@when('I call write_config with key "greeting" value "hello"')
def step_call_write_config(context: Any) -> None:
    context.svc.write_config({"greeting": "hello"})


@then('the TOML file should exist and contain key "greeting" with value "hello"')
def step_verify_written_toml(context: Any) -> None:
    assert context.config_path.exists()
    data = context.svc.read_config()
    assert data["greeting"] == "hello"


@given('a TOML config file with key "existing" set to "value1"')
def step_write_toml_existing(context: Any) -> None:
    context.config_dir.mkdir(parents=True, exist_ok=True)
    doc = tomlkit.document()
    doc["existing"] = "value1"
    with open(context.config_path, "w") as fh:
        tomlkit.dump(doc, fh)


@when('I call write_config with key "new_key" value "value2"')
def step_call_write_config_merge(context: Any) -> None:
    context.svc.write_config({"new_key": "value2"})


@then('read_config should return a dict containing both "existing" and "new_key"')
def step_verify_merged_toml(context: Any) -> None:
    data = context.svc.read_config()
    assert data["existing"] == "value1"
    assert data["new_key"] == "value2"


# ---------------------------------------------------------------------------
# set_value
# ---------------------------------------------------------------------------


@when('I call set_value with key "mykey" and value "myval"')
def step_call_set_value(context: Any) -> None:
    context.svc.set_value("mykey", "myval")


@then('read_config should return a dict with "mykey" equal to "myval"')
def step_verify_set_value(context: Any) -> None:
    data = context.svc.read_config()
    assert data["mykey"] == "myval"


# ---------------------------------------------------------------------------
# validate_key
# ---------------------------------------------------------------------------


@when('I call validate_key with "core.log.level"')
def step_call_validate_key_known(context: Any) -> None:
    try:
        context.result = ConfigService.validate_key("core.log.level")
        context.error = None
    except Exception as exc:
        context.error = exc


@then('it should return the ConfigEntry for "core.log.level" without error')
def step_verify_validate_key_ok(context: Any) -> None:
    assert context.error is None
    assert isinstance(context.result, ConfigEntry)
    assert context.result.key == "core.log.level"


@when('I call validate_key with "totally.unknown"')
def step_call_validate_key_unknown(context: Any) -> None:
    try:
        context.result = ConfigService.validate_key("totally.unknown")
        context.error = None
    except ValueError as exc:
        context.error = exc


@then('a ValueError should be raised mentioning "{text}"')
def step_then_generic_valueerror_raised_mentioning(context: Any, text: str) -> None:
    """Shared @then step: check for a ValueError stored in common context attrs."""
    err = None
    for attr in ("error", "lsp_error", "caught_error", "ontology_error"):
        err = getattr(context, attr, None)
        if err is not None:
            break
    assert err is not None, "Expected a ValueError but none was raised"
    assert isinstance(err, ValueError), f"Expected ValueError, got {type(err).__name__}"
    assert text in str(err), f"Expected '{text}' in error message: {err}"


@then('a ValidationError should be raised mentioning "{text}"')
def step_then_generic_validation_error_raised_mentioning(
    context: Any, text: str
) -> None:
    """Shared @then step: check for a ValidationError stored in common context attrs."""
    err = None
    for attr in ("error", "lsp_error", "caught_error", "ontology_error"):
        err = getattr(context, attr, None)
        if err is not None:
            break
    assert err is not None, "Expected a ValidationError but none was raised"
    assert text in str(err), f"Expected '{text}' in error message: {err}"


# ---------------------------------------------------------------------------
# validate_type
# ---------------------------------------------------------------------------


@when('I call validate_type with key "core.log.level" and value "INFO"')
def step_validate_type_same(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.level", "INFO")


@then('it should return "INFO" unchanged')
def step_verify_type_same(context: Any) -> None:
    assert context.result == "INFO"
    assert isinstance(context.result, str)


@when('I call validate_type with key "plan.concurrency" and string value "9090"')
def step_validate_type_str_to_int(context: Any) -> None:
    context.result = ConfigService.validate_type("plan.concurrency", "9090")


@then("it should return the integer 9090")
def step_verify_int_coercion(context: Any) -> None:
    assert context.result == 9090
    assert isinstance(context.result, int)


@when(
    'I call validate_type with key "plan.budget.warn-threshold" and string value "0.5"'
)
def step_validate_type_str_to_float(context: Any) -> None:
    context.result = ConfigService.validate_type("plan.budget.warn-threshold", "0.5")


@then("it should return the float 0.5")
def step_verify_float_coercion(context: Any) -> None:
    assert context.result == 0.5
    assert isinstance(context.result, float)


@when('I call validate_type with key "core.log.level" and integer value 123')
def step_validate_type_int_to_str(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.level", 123)


@then('it should return the string "123"')
def step_verify_str_coercion(context: Any) -> None:
    assert context.result == "123"
    assert isinstance(context.result, str)


@when('I call validate_type with key "core.log.file-enabled" and string value "true"')
def step_validate_type_bool_true(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.file-enabled", "true")


@then("it should return boolean True")
def step_verify_bool_true(context: Any) -> None:
    assert context.result is True


@when('I call validate_type with key "core.log.file-enabled" and string value "1"')
def step_validate_type_bool_one(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.file-enabled", "1")


@when('I call validate_type with key "core.log.file-enabled" and string value "yes"')
def step_validate_type_bool_yes(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.file-enabled", "yes")


@when('I call validate_type with key "core.log.file-enabled" and string value "false"')
def step_validate_type_bool_false(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.file-enabled", "false")


@then("it should return boolean False")
def step_verify_bool_false(context: Any) -> None:
    assert context.result is False


@when('I call validate_type with key "core.log.file-enabled" and string value "0"')
def step_validate_type_bool_zero(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.file-enabled", "0")


@when('I call validate_type with key "core.log.file-enabled" and string value "no"')
def step_validate_type_bool_no(context: Any) -> None:
    context.result = ConfigService.validate_type("core.log.file-enabled", "no")


@when('I call validate_type with key "core.log.file-enabled" and string value "maybe"')
def step_validate_type_bool_invalid(context: Any) -> None:
    try:
        context.result = ConfigService.validate_type("core.log.file-enabled", "maybe")
        context.error = None
    except TypeError as exc:
        context.error = exc


@then('a TypeError should be raised mentioning "Cannot convert"')
def step_verify_bool_invalid_error(context: Any) -> None:
    assert context.error is not None
    assert isinstance(context.error, TypeError)
    assert "Cannot convert" in str(context.error)


@when(
    'I call validate_type with key "plan.concurrency" and string value "not_a_number"'
)
def step_validate_type_int_invalid(context: Any) -> None:
    try:
        context.result = ConfigService.validate_type("plan.concurrency", "not_a_number")
        context.error = None
    except TypeError as exc:
        context.error = exc


@then('a TypeError should be raised mentioning "Type mismatch"')
def step_verify_type_mismatch_error(context: Any) -> None:
    assert context.error is not None
    assert isinstance(context.error, TypeError)
    assert "Type mismatch" in str(context.error)


@when('I call validate_type with key "core.log.file-enabled" and a list value')
def step_validate_type_bool_list(context: Any) -> None:
    try:
        context.result = ConfigService.validate_type("core.log.file-enabled", [1, 2, 3])
        context.error = None
    except TypeError as exc:
        context.error = exc


@when('I call validate_type with unknown key "fake.unknown" and value "x"')
def step_validate_type_unknown_key(context: Any) -> None:
    try:
        context.result = ConfigService.validate_type("fake.unknown", "x")
        context.error = None
    except ValueError as exc:
        context.error = exc


@then('the raised ValueError should mention "Cannot validate type for unknown key"')
def step_verify_validate_type_unknown_key_error(context: Any) -> None:
    assert context.error is not None
    assert isinstance(context.error, ValueError)
    assert "Cannot validate type for unknown key" in str(context.error)


# ---------------------------------------------------------------------------
# resolve - six-level chain
# ---------------------------------------------------------------------------


@given("a ConfigService with empty config for resolve tests")
def step_service_empty_for_resolve(context: Any) -> None:
    context.config_dir.mkdir(parents=True, exist_ok=True)
    context.svc = ConfigService(
        config_dir=context.config_dir,
        config_path=context.config_path,
    )


@when('I resolve key "core.log.level" with no overrides')
def step_resolve_no_overrides(context: Any) -> None:
    context.result = context.svc.resolve("core.log.level")


@then('the resolved value should be "FATAL" from source DEFAULT')
def step_verify_resolve_default(context: Any) -> None:
    assert context.result.value == "FATAL"
    assert context.result.source == ConfigLevel.DEFAULT


@when('I resolve key "core.log.level" with cli_value "TRACE"')
def step_resolve_cli(context: Any) -> None:
    context.result = context.svc.resolve("core.log.level", cli_value="TRACE")


@then('the resolved value should be "TRACE" from source CLI_FLAG')
def step_verify_resolve_cli(context: Any) -> None:
    assert context.result.value == "TRACE"
    assert context.result.source == ConfigLevel.CLI_FLAG
    # Clean up env patch if present (from CLI-over-env scenario)
    if hasattr(context, "_env_patch"):
        context._env_patch.stop()


@given('the env var "{name}" is injected with "{value}"')
def step_inject_env_var(context: Any, name: str, value: str) -> None:
    context._env_patch = patch.dict("os.environ", {name: value})
    context._env_patch.start()


@then('the resolved value should be "WARNING" from source ENV_VAR')
def step_verify_resolve_env(context: Any) -> None:
    assert context.result.value == "WARNING"
    assert context.result.source == ConfigLevel.ENV_VAR
    # Clean up the patch
    if hasattr(context, "_env_patch"):
        context._env_patch.stop()


@given(
    'a ConfigService with project-scoped config for key "core.automation-profile" value "PROJECT_DEBUG" under project "myproj"'
)
def step_service_project_scoped(context: Any) -> None:
    context.config_dir.mkdir(parents=True, exist_ok=True)
    doc = tomlkit.document()
    project_table = tomlkit.table()
    myproj_table = tomlkit.table()
    myproj_table["core.automation-profile"] = "PROJECT_DEBUG"
    project_table["myproj"] = myproj_table
    doc["project"] = project_table
    with open(context.config_path, "w") as fh:
        tomlkit.dump(doc, fh)
    context.svc = ConfigService(
        config_dir=context.config_dir,
        config_path=context.config_path,
    )


@when('I resolve key "core.automation-profile" with project_name "myproj"')
def step_resolve_project(context: Any) -> None:
    context.result = context.svc.resolve(
        "core.automation-profile", project_name="myproj"
    )


@then('the resolved value should be "PROJECT_DEBUG" from source PROJECT')
def step_verify_resolve_project(context: Any) -> None:
    assert context.result.value == "PROJECT_DEBUG"
    assert context.result.source == ConfigLevel.PROJECT


@given('a ConfigService with global config key "core.log.level" set to "GLOBAL_WARN"')
def step_service_global_config(context: Any) -> None:
    context.config_dir.mkdir(parents=True, exist_ok=True)
    doc = tomlkit.document()
    doc["core.log.level"] = "GLOBAL_WARN"
    with open(context.config_path, "w") as fh:
        tomlkit.dump(doc, fh)
    context.svc = ConfigService(
        config_dir=context.config_dir,
        config_path=context.config_path,
    )


@then('the resolved value should be "GLOBAL_WARN" from source GLOBAL')
def step_verify_resolve_global(context: Any) -> None:
    assert context.result.value == "GLOBAL_WARN"
    assert context.result.source == ConfigLevel.GLOBAL


@when('I resolve key "core.log.level" with verbose True and no overrides')
def step_resolve_verbose(context: Any) -> None:
    context.result = context.svc.resolve("core.log.level", verbose=True)


@then("the resolved chain should have 6 entries covering all levels")
def step_verify_verbose_chain_length(context: Any) -> None:
    chain = context.result.chain
    assert len(chain) == 6, f"Expected 6 chain entries, got {len(chain)}"
    sources = [entry["source"] for entry in chain]
    assert sources == [
        ConfigLevel.CLI_FLAG.value,
        ConfigLevel.ENV_VAR.value,
        ConfigLevel.LOCAL.value,
        ConfigLevel.PROJECT.value,
        ConfigLevel.GLOBAL.value,
        ConfigLevel.DEFAULT.value,
    ]


@then(
    'the verbose chain ENV_VAR entry should include env_name "CLEVERAGENTS_LOG_LEVEL"'
)
def step_verify_verbose_env_name(context: Any) -> None:
    chain = context.result.chain
    env_entry = next(e for e in chain if e["source"] == ConfigLevel.ENV_VAR.value)
    assert env_entry["env_name"] == "CLEVERAGENTS_LOG_LEVEL"


@then("the verbose chain GLOBAL entry should include the config file path")
def step_verify_verbose_global_path(context: Any) -> None:
    chain = context.result.chain
    global_entry = next(e for e in chain if e["source"] == ConfigLevel.GLOBAL.value)
    assert "path" in global_entry
    assert str(context.config_path) in global_entry["path"]


# -- project scope skip for non-scopable keys --


@given(
    'a ConfigService with project-scoped config for key "core.data-dir" value "/proj/data" under project "myproj"'
)
def step_service_project_scoped_nonscopable(context: Any) -> None:
    context.config_dir.mkdir(parents=True, exist_ok=True)
    doc = tomlkit.document()
    project_table = tomlkit.table()
    myproj_table = tomlkit.table()
    myproj_table["core.data-dir"] = "/proj/data"
    project_table["myproj"] = myproj_table
    doc["project"] = project_table
    with open(context.config_path, "w") as fh:
        tomlkit.dump(doc, fh)
    context.svc = ConfigService(
        config_dir=context.config_dir,
        config_path=context.config_path,
    )


@when(
    'I resolve key "core.automation-profile" with project_name "myproj" and verbose True'
)
def step_resolve_project_verbose(context: Any) -> None:
    context.result = context.svc.resolve(
        "core.automation-profile", project_name="myproj", verbose=True
    )


@then('the verbose chain PROJECT entry should have value "PROJECT_DEBUG"')
def step_verify_verbose_project_value(context: Any) -> None:
    chain = context.result.chain
    proj_entry = next(e for e in chain if e["source"] == ConfigLevel.PROJECT.value)
    assert proj_entry["value"] == "PROJECT_DEBUG"


@then('the verbose chain GLOBAL entry should have value "GLOBAL_WARN"')
def step_verify_verbose_global_value(context: Any) -> None:
    chain = context.result.chain
    global_entry = next(e for e in chain if e["source"] == ConfigLevel.GLOBAL.value)
    assert global_entry["value"] == "GLOBAL_WARN"


@when('I resolve key "core.data-dir" with project_name "myproj"')
def step_resolve_nonscopable(context: Any) -> None:
    context.result = context.svc.resolve("core.data-dir", project_name="myproj")


@then('the resolved value should not be "/proj/data"')
def step_verify_not_project_value(context: Any) -> None:
    assert context.result.value != "/proj/data"


@then("the resolved source should be DEFAULT or GLOBAL")
def step_verify_source_not_project(context: Any) -> None:
    assert context.result.source in (ConfigLevel.DEFAULT, ConfigLevel.GLOBAL)


# ---------------------------------------------------------------------------
# env_var_for_key
# ---------------------------------------------------------------------------


@when('I call env_var_for_key with "core.log.level"')
def step_call_env_var_for_key_valid(context: Any) -> None:
    context.result = ConfigService.env_var_for_key("core.log.level")


@then('it should return "CLEVERAGENTS_LOG_LEVEL"')
def step_verify_env_var_for_key(context: Any) -> None:
    assert context.result == "CLEVERAGENTS_LOG_LEVEL"


@when('I call env_var_for_key with "nonexistent.key"')
def step_call_env_var_for_key_unknown(context: Any) -> None:
    try:
        context.result = ConfigService.env_var_for_key("nonexistent.key")
        context.error = None
    except ValueError as exc:
        context.error = exc


# The "a ValueError should be raised mentioning ..." @then step is defined
# above (step_then_generic_valueerror_raised_mentioning) and reused here.


# ---------------------------------------------------------------------------
# resolve_all
# ---------------------------------------------------------------------------


@when("I call resolve_all with no overrides")
def step_call_resolve_all(context: Any) -> None:
    context.result = context.svc.resolve_all()


@then("the result should contain an entry for every registered key")
def step_verify_resolve_all_keys(context: Any) -> None:
    for key in _REGISTRY:
        assert key in context.result, f"Missing resolved key: {key}"
        assert isinstance(context.result[key], ResolvedValue)


@when('I call resolve_all with cli_override "core.log.level" set to "FATAL"')
def step_call_resolve_all_with_overrides(context: Any) -> None:
    context.result = context.svc.resolve_all(cli_overrides={"core.log.level": "FATAL"})


@then(
    'the resolve_all result for "core.log.level" should have value "FATAL" and source CLI_FLAG'
)
def step_verify_resolve_all_override(context: Any) -> None:
    rv = context.result["core.log.level"]
    assert rv.value == "FATAL"
    assert rv.source == ConfigLevel.CLI_FLAG
