"""Step definitions for resource_type_lsp.feature."""

from __future__ import annotations

from pathlib import Path

import yaml
from behave import given, then, when

from cleveragents.domain.models.core.resource_type import ResourceTypeSpec
from cleveragents.resource.schema import ResourceTypeConfigSchema

_EXAMPLES_DIR = Path(__file__).resolve().parents[2] / "examples" / "resource-types"


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


@given("the built-in {type_name} YAML file for lsp_rt")
def step_load_yaml_file_lsp_rt(context: object, type_name: str) -> None:
    """Load the built-in YAML file for the given type."""
    yaml_path = _EXAMPLES_DIR / f"{type_name}.yaml"
    assert yaml_path.exists(), f"YAML file not found: {yaml_path}"
    with yaml_path.open() as f:
        context.lsp_rt_config = yaml.safe_load(f)  # type: ignore[attr-defined]


@given("the ResourceTypeSpec BUILTIN_NAMES set for lsp_rt")
def step_load_builtin_names_lsp_rt(context: object) -> None:
    """Load the BUILTIN_NAMES frozenset."""
    context.lsp_rt_builtin_names = ResourceTypeSpec.BUILTIN_NAMES  # type: ignore[attr-defined]


@given("a lsp_rt fresh in-memory resource registry with bootstrap")
def step_fresh_registry_lsp_rt(context: object) -> None:
    """Create an in-memory SQLite DB, bootstrap all built-in types."""
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker

    from cleveragents.application.services.resource_registry_service import (
        ResourceRegistryService,
    )
    from cleveragents.infrastructure.database.models import Base

    engine = create_engine("sqlite:///:memory:", echo=False)
    Base.metadata.create_all(engine)
    factory = sessionmaker(bind=engine, expire_on_commit=False)
    context.lsp_rt_service = ResourceRegistryService(  # type: ignore[attr-defined]
        session_factory=factory,
    )


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


@when("I load the YAML via schema for lsp_rt")
def step_load_schema_lsp_rt(context: object) -> None:
    """Parse the YAML config into a ResourceTypeConfigSchema."""
    config = context.lsp_rt_config  # type: ignore[attr-defined]
    context.lsp_rt_schema = ResourceTypeConfigSchema(**config)  # type: ignore[attr-defined]


@when('I query the lsp_rt registry for type "{name}"')
def step_query_registry_lsp_rt(context: object, name: str) -> None:
    """Query the bootstrapped registry for a specific type."""
    from cleveragents.core.exceptions import NotFoundError

    service = context.lsp_rt_service  # type: ignore[attr-defined]
    try:
        context.lsp_rt_db_spec = service.show_type(name)  # type: ignore[attr-defined]
        context.lsp_rt_db_found = True  # type: ignore[attr-defined]
    except NotFoundError:
        context.lsp_rt_db_spec = None  # type: ignore[attr-defined]
        context.lsp_rt_db_found = False  # type: ignore[attr-defined]


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


@then("the lsp_rt schema should be valid")
def step_schema_valid_lsp_rt(context: object) -> None:
    """Assert the schema loaded successfully."""
    schema = context.lsp_rt_schema  # type: ignore[attr-defined]
    assert schema is not None, "Schema should not be None"
    assert schema.name, "Schema must have a non-empty name"


@then('the lsp_rt schema name should be "{name}"')
def step_schema_name_lsp_rt(context: object, name: str) -> None:
    """Assert the schema name matches."""
    actual = context.lsp_rt_schema.name  # type: ignore[attr-defined]
    assert actual == name, f"Expected name '{name}', got '{actual}'"


@then('the lsp_rt schema resource_kind should be "{kind}"')
def step_schema_kind_lsp_rt(context: object, kind: str) -> None:
    """Assert the resource kind matches."""
    actual = context.lsp_rt_schema.resource_kind  # type: ignore[attr-defined]
    assert actual == kind, f"Expected kind '{kind}', got '{actual}'"


@then('the lsp_rt schema sandbox_strategy should be "{strategy}"')
def step_schema_sandbox_lsp_rt(context: object, strategy: str) -> None:
    """Assert the sandbox strategy matches."""
    actual = context.lsp_rt_schema.sandbox_strategy  # type: ignore[attr-defined]
    assert actual == strategy, f"Expected strategy '{strategy}', got '{actual}'"


@then("the lsp_rt schema user_addable should be true")
def step_user_addable_true_lsp_rt(context: object) -> None:
    """Assert user_addable is True."""
    actual = context.lsp_rt_schema.user_addable  # type: ignore[attr-defined]
    assert actual is True, f"Expected user_addable True, got {actual}"


@then("the lsp_rt schema user_addable should be false")
def step_user_addable_false_lsp_rt(context: object) -> None:
    """Assert user_addable is False."""
    actual = context.lsp_rt_schema.user_addable  # type: ignore[attr-defined]
    assert actual is False, f"Expected user_addable False, got {actual}"


@then("the lsp_rt capability read should be true")
def step_cap_read_true_lsp_rt(context: object) -> None:
    """Assert read capability is True."""
    caps = context.lsp_rt_schema.capabilities  # type: ignore[attr-defined]
    assert caps["read"] is True, f"Expected read=True, got {caps['read']}"


@then("the lsp_rt capability write should be true")
def step_cap_write_true_lsp_rt(context: object) -> None:
    """Assert write capability is True."""
    caps = context.lsp_rt_schema.capabilities  # type: ignore[attr-defined]
    assert caps["write"] is True, f"Expected write=True, got {caps['write']}"


@then("the lsp_rt capability write should be false")
def step_cap_write_false_lsp_rt(context: object) -> None:
    """Assert write capability is False."""
    caps = context.lsp_rt_schema.capabilities  # type: ignore[attr-defined]
    assert caps["write"] is False, f"Expected write=False, got {caps['write']}"


@then('the lsp_rt child_types should contain "{child}"')
def step_child_types_lsp_rt(context: object, child: str) -> None:
    """Assert child_types contains the given type."""
    types = context.lsp_rt_schema.child_types  # type: ignore[attr-defined]
    assert child in types, f"Expected '{child}' in child_types {types}"


@then('the lsp_rt parent_types should contain "{parent}"')
def step_parent_types_lsp_rt(context: object, parent: str) -> None:
    """Assert parent_types contains the given type."""
    types = context.lsp_rt_schema.parent_types  # type: ignore[attr-defined]
    assert parent in types, f"Expected '{parent}' in parent_types {types}"


@then("the lsp_rt auto_discovery should be present")
def step_auto_discovery_present_lsp_rt(context: object) -> None:
    """Assert auto_discovery is present."""
    ad = context.lsp_rt_schema.auto_discovery  # type: ignore[attr-defined]
    assert ad is not None, "auto_discovery should not be None"


@then('the lsp_rt auto_discovery trigger_types should contain "{trigger}"')
def step_auto_discovery_trigger_lsp_rt(context: object, trigger: str) -> None:
    """Assert auto_discovery trigger_types contains the given type."""
    ad = context.lsp_rt_schema.auto_discovery  # type: ignore[attr-defined]
    triggers = ad.get("trigger_types", [])
    assert trigger in triggers, f"Expected '{trigger}' in trigger_types {triggers}"


@then('BUILTIN_NAMES should contain "{name}" for lsp_rt')
def step_builtin_names_contains_lsp_rt(context: object, name: str) -> None:
    """Assert BUILTIN_NAMES contains the given name."""
    names = context.lsp_rt_builtin_names  # type: ignore[attr-defined]
    assert name in names, f"'{name}' not in BUILTIN_NAMES: {sorted(names)}"


@then('the lsp_rt db type "{name}" should exist')
def step_db_type_exists_lsp_rt(context: object, name: str) -> None:
    """Assert the type was found in the DB."""
    assert context.lsp_rt_db_found is True, (  # type: ignore[attr-defined]
        f"Type '{name}' not found after bootstrap"
    )


@then('the lsp_rt db type "{name}" should have kind "{kind}"')
def step_db_type_kind_lsp_rt(context: object, name: str, kind: str) -> None:
    """Assert the DB type has the expected resource kind."""
    spec = context.lsp_rt_db_spec  # type: ignore[attr-defined]
    actual = (
        str(spec.resource_kind.value)
        if hasattr(spec.resource_kind, "value")
        else str(spec.resource_kind)
    )
    assert actual == kind, f"Expected '{name}' kind '{kind}', got '{actual}'"


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


@when('I attempt to manually register "{type_name}" for lsp_rt')
def step_attempt_manual_register_lsp_rt(context: object, type_name: str) -> None:
    """Attempt to register a resource instance of a non-user-addable type."""
    service = context.lsp_rt_service  # type: ignore[attr-defined]
    context.lsp_rt_register_error = None  # type: ignore[attr-defined]
    try:
        service.register_resource(
            type_name=type_name,
            name=f"test-manual-{type_name}",
        )
    except Exception as exc:
        context.lsp_rt_register_error = exc  # type: ignore[attr-defined]


@then("the lsp_rt manual registration should be rejected")
def step_manual_registration_rejected_lsp_rt(context: object) -> None:
    """Assert that manual registration was rejected."""
    err = context.lsp_rt_register_error  # type: ignore[attr-defined]
    assert err is not None, "Expected registration to be rejected but it succeeded"
    assert "not user-addable" in str(err), (
        f"Expected 'not user-addable' in error, got: {err}"
    )
