"""Step definitions for resource_type_container_infra.feature.

Tests container infrastructure resource types: container-runtime,
container-image, container-mount, container-exec-env, container-port,
container-volume, container-network.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any

import yaml
from behave import given, then, when

from cleveragents.application.services._resource_registry_container import (
    CONTAINER_INFRA_TYPES,
)
from cleveragents.application.services._resource_registry_data import (
    BUILTIN_TYPES,
)
from cleveragents.domain.models.core.resource_type import (
    ResourceKind,
    ResourceTypeSpec,
)
from cleveragents.resource.schema import ResourceTypeConfigSchema

_EXAMPLES_DIR = (
    Path(__file__).resolve().parent.parent.parent / "examples" / "resource-types"
)


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


@given('the built-in "{type_name}" YAML for container_infra')
def step_builtin_yaml(context: object, type_name: str) -> None:
    path = _EXAMPLES_DIR / f"{type_name}.yaml"
    assert path.exists(), f"YAML not found: {path}"
    context.ci_yaml_path = path


@given("the bootstrap builtin type definitions for container_infra")
def step_bootstrap_defs(context: object) -> None:
    context.ci_builtin_defs = list(BUILTIN_TYPES)


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


@when("I load the YAML via from_yaml_file for container_infra")
def step_load_yaml(context: object) -> None:
    path: Path = context.ci_yaml_path
    schema = ResourceTypeConfigSchema.from_yaml_file(path)
    context.ci_schema = schema


@when("I load the YAML as a ResourceTypeSpec for container_infra")
def step_load_domain(context: object) -> None:
    path: Path = context.ci_yaml_path
    raw: dict[str, Any] = yaml.safe_load(path.read_text(encoding="utf-8"))
    spec = ResourceTypeSpec.from_config(raw)
    context.ci_spec = spec


# ── Then steps: schema ───────────────────────────────────


@then("the container_infra schema should be valid")
def step_schema_valid(context: object) -> None:
    schema = context.ci_schema
    assert schema is not None
    assert isinstance(schema, ResourceTypeConfigSchema)


@then('the container_infra schema name should be "{name}"')
def step_schema_name(context: object, name: str) -> None:
    assert context.ci_schema.name == name


@then('the container_infra schema resource_kind should be "{kind}"')
def step_schema_kind(context: object, kind: str) -> None:
    assert context.ci_schema.resource_kind == kind


# ── Then steps: domain model ─────────────────────────────


@then("the container_infra domain model should be valid")
def step_domain_valid(context: object) -> None:
    spec = context.ci_spec
    assert spec is not None
    assert isinstance(spec, ResourceTypeSpec)


@then('the container_infra domain model name should be "{name}"')
def step_domain_name(context: object, name: str) -> None:
    assert context.ci_spec.name == name


@then("the container_infra domain model resource_kind should be physical")
def step_domain_kind_physical(context: object) -> None:
    assert context.ci_spec.resource_kind == ResourceKind.PHYSICAL


# ── Then steps: parent/child ─────────────────────────────


@then("the container_infra spec should have empty parent_types")
def step_empty_parents(context: object) -> None:
    spec = context.ci_spec
    assert spec.parent_types == [], f"Expected empty, got {spec.parent_types}"


@then('the container_infra parent_types should contain "{name}"')
def step_parent_contains(context: object, name: str) -> None:
    spec = context.ci_spec
    assert name in spec.parent_types, (
        f"'{name}' not in parent_types: {spec.parent_types}"
    )


@then('the container_infra child_types should contain "{name}"')
def step_child_contains(context: object, name: str) -> None:
    spec = context.ci_spec
    assert name in spec.child_types, f"'{name}' not in child_types: {spec.child_types}"


# ── Then steps: user_addable ─────────────────────────────


@then("the container_infra user_addable should be {addable}")
def step_user_addable(context: object, addable: str) -> None:
    spec = context.ci_spec
    expected = addable.lower() == "true"
    assert spec.user_addable is expected, (
        f"Expected user_addable={expected}, got {spec.user_addable}"
    )


# ── Then steps: BUILTIN_NAMES ────────────────────────────


@then('BUILTIN_NAMES should contain "{name}" for container_infra')
def step_builtin_names(context: object, name: str) -> None:
    assert name in ResourceTypeSpec.BUILTIN_NAMES, f"'{name}' not in BUILTIN_NAMES"


# ── Then steps: BUILTIN_TYPES ────────────────────────────


@then('BUILTIN_TYPES should contain "{name}" for container_infra')
def step_builtin_types(context: object, name: str) -> None:
    defs: list[dict[str, Any]] = context.ci_builtin_defs
    names = [d["name"] for d in defs]
    assert name in names, f"'{name}' not in BUILTIN_TYPES: {names}"


# ── Coverage: CONTAINER_INFRA_TYPES direct access ────────


@then("CONTAINER_INFRA_TYPES should have 7 entries for container_infra")
def step_infra_count(context: object) -> None:
    assert len(CONTAINER_INFRA_TYPES) == 7, (
        f"Expected 7, got {len(CONTAINER_INFRA_TYPES)}"
    )


@then("every CONTAINER_INFRA_TYPES entry should have a handler for container_infra")
def step_all_have_handler(context: object) -> None:
    for entry in CONTAINER_INFRA_TYPES:
        assert entry.get("handler") is not None, f"{entry['name']} has no handler"


@then(
    "container-runtime in CONTAINER_INFRA_TYPES should have auto_discovery"
    " for container_infra"
)
def step_runtime_auto_discovery(context: object) -> None:
    runtime = next(d for d in CONTAINER_INFRA_TYPES if d["name"] == "container-runtime")
    ad = runtime.get("auto_discovery")
    assert ad is not None, "container-runtime missing auto_discovery"
    assert ad.get("enabled") is True
    assert ad.get("scan_depth") == 1
    rules = ad.get("rules", [])
    assert len(rules) >= 4, f"Expected >=4 rules, got {len(rules)}"
    # Verify Dockerfile/docker-compose patterns
    patterns = [r["pattern"] for r in rules]
    assert any("docker-compose" in p for p in patterns), (
        f"Missing docker-compose pattern: {patterns}"
    )
