"""Step definitions for lsp_capability_enum.feature."""

from __future__ import annotations

from behave import given, then, when

from cleveragents.lsp.models import LspCapability, LspServerConfig
from cleveragents.lsp.tool_adapter import (
    _CAPABILITY_TOOL_MAP,
    LspToolAdapter,
    _input_schema_for,
)

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


@given("the LspCapability enum for lsp_cap")
def step_load_enum_lsp_cap(context: object) -> None:
    """Load the LspCapability enum."""
    context.lsp_cap_enum = LspCapability  # type: ignore[attr-defined]


@given("the lsp_cap capability tool map")
def step_load_tool_map_lsp_cap(context: object) -> None:
    """Load the capability tool map."""
    context.lsp_cap_tool_map = _CAPABILITY_TOOL_MAP  # type: ignore[attr-defined]


@given('an LspServerConfig with capability "{capability}" for lsp_cap')
def step_config_with_capability_lsp_cap(
    context: object,
    capability: str,
) -> None:
    """Create an LspServerConfig with a single capability."""
    cap = LspCapability(capability)
    context.lsp_cap_config = LspServerConfig(  # type: ignore[attr-defined]
        name="test/server",
        languages=["python"],
        command="test-server",
        capabilities=[cap],
    )


@given('the lsp_cap input schema for "{capability}"')
def step_input_schema_lsp_cap(context: object, capability: str) -> None:
    """Get the input schema for a capability."""
    cap = LspCapability(capability)
    context.lsp_cap_schema = _input_schema_for(cap)  # type: ignore[attr-defined]


@given("the lsp_cap stubbed server capabilities")
def step_stubbed_caps_lsp_cap(context: object) -> None:
    """Load the stubbed server capabilities."""
    from cleveragents.lsp.server import _STUBBED_CAPABILITIES

    context.lsp_cap_stubbed = _STUBBED_CAPABILITIES  # type: ignore[attr-defined]


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


@when("I generate tool specs for lsp_cap")
def step_generate_specs_lsp_cap(context: object) -> None:
    """Generate tool specs from the config."""
    config = context.lsp_cap_config  # type: ignore[attr-defined]
    adapter = LspToolAdapter()
    context.lsp_cap_specs = adapter.generate_tool_specs(config)  # type: ignore[attr-defined]


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


@then("the lsp_cap enum should have {count:d} members")
def step_enum_count_lsp_cap(context: object, count: int) -> None:
    """Assert the enum has the expected number of members."""
    members = list(context.lsp_cap_enum)  # type: ignore[attr-defined]
    assert len(members) == count, (
        f"Expected {count} members, got {len(members)}: {[m.value for m in members]}"
    )


@then('the lsp_cap enum should include "{member}"')
def step_enum_includes_lsp_cap(context: object, member: str) -> None:
    """Assert the enum includes the given member value."""
    values = [m.value for m in context.lsp_cap_enum]  # type: ignore[attr-defined]
    assert member in values, f"'{member}' not in enum values: {values}"


@then("the lsp_cap tool map should have {count:d} entries")
def step_tool_map_count_lsp_cap(context: object, count: int) -> None:
    """Assert the tool map has the expected number of entries."""
    tool_map = context.lsp_cap_tool_map  # type: ignore[attr-defined]
    assert len(tool_map) == count, f"Expected {count} entries, got {len(tool_map)}"


@then("the lsp_cap generated specs should not be empty")
def step_specs_not_empty_lsp_cap(context: object) -> None:
    """Assert generated specs are not empty."""
    specs = context.lsp_cap_specs  # type: ignore[attr-defined]
    assert len(specs) > 0, "Expected non-empty tool specs"


@then('the lsp_cap tool spec should have keys "name" and "input_schema"')
def step_spec_has_keys_lsp_cap(context: object) -> None:
    """Assert generated specs have required keys."""
    specs = context.lsp_cap_specs  # type: ignore[attr-defined]
    for spec in specs:
        assert "name" in spec, f"Spec missing 'name': {spec}"
        assert "input_schema" in spec, f"Spec missing 'input_schema': {spec}"


@then('the lsp_cap tool name should end with "{suffix}"')
def step_tool_name_ends_with_lsp_cap(context: object, suffix: str) -> None:
    """Assert at least one tool name ends with the suffix."""
    specs = context.lsp_cap_specs  # type: ignore[attr-defined]
    names = [s["name"] for s in specs]
    assert any(n.endswith(f"/{suffix}") for n in names), (
        f"No tool name ends with '/{suffix}': {names}"
    )


@then('the lsp_cap schema should require "{field}"')
def step_schema_requires_lsp_cap(context: object, field: str) -> None:
    """Assert the schema requires the given field."""
    schema = context.lsp_cap_schema  # type: ignore[attr-defined]
    required = schema.get("required", [])
    assert field in required, f"'{field}' not in required: {required}"


@then('the lsp_cap schema should not require "{field}"')
def step_schema_not_requires_lsp_cap(context: object, field: str) -> None:
    """Assert the schema does not require the given field."""
    schema = context.lsp_cap_schema  # type: ignore[attr-defined]
    required = schema.get("required", [])
    assert field not in required, f"'{field}' should not be in required: {required}"


@then('the lsp_cap stubbed caps should include "{key}"')
def step_stubbed_includes_lsp_cap(context: object, key: str) -> None:
    """Assert the stubbed capabilities dict includes the key."""
    caps = context.lsp_cap_stubbed  # type: ignore[attr-defined]
    assert key in caps, f"'{key}' not in stubbed capabilities: {list(caps)}"


# ── When/Then steps: negative tests ─────────────────────────


@when('I create an LspCapability with value "{value}" for lsp_cap')
def step_create_invalid_capability_lsp_cap(context: object, value: str) -> None:
    """Attempt to create an LspCapability with an invalid value."""
    context.lsp_cap_error = None  # type: ignore[attr-defined]
    try:
        LspCapability(value)
    except ValueError as exc:
        context.lsp_cap_error = exc  # type: ignore[attr-defined]


@then("the lsp_cap creation should raise a ValueError")
def step_should_raise_value_error_lsp_cap(context: object) -> None:
    """Assert that a ValueError was raised."""
    err = context.lsp_cap_error  # type: ignore[attr-defined]
    assert err is not None, "Expected ValueError but none was raised"


@when("I call _input_schema_for with a fake capability for lsp_cap")
def step_call_schema_fake_cap_lsp_cap(context: object) -> None:
    """Call _input_schema_for with a value not in any category tuple."""
    from enum import StrEnum

    class _FakeCap(StrEnum):
        FAKE = "fake_capability"

    context.lsp_cap_schema_error = None  # type: ignore[attr-defined]
    try:
        _input_schema_for(_FakeCap.FAKE)  # type: ignore[arg-type]
    except ValueError as exc:
        context.lsp_cap_schema_error = exc  # type: ignore[attr-defined]


@then("the lsp_cap schema call should raise a ValueError")
def step_schema_should_raise_lsp_cap(context: object) -> None:
    """Assert _input_schema_for raised ValueError."""
    err = context.lsp_cap_schema_error  # type: ignore[attr-defined]
    assert err is not None, "Expected ValueError from _input_schema_for but none raised"
