"""Behave steps for UKO Layer 2 TTL validation and additional coverage.

Covers Turtle file loading, ParadigmVocabulary frozen-model checks,
and miscellaneous validation edge-cases.

All step definitions use the ``for uko_l2`` suffix to avoid
AmbiguousStep collisions with other feature files.
"""

from __future__ import annotations

from pathlib import Path

from _uko_l2_test_helpers import capture_error
from behave import then, when
from behave.runner import Context
from pydantic import ValidationError

from cleveragents.acms.uko.vocabularies import (
    ParadigmVocabulary,
    VocabularyProperty,
    get_oo_vocabulary,
)
from cleveragents.application.services.uko_loader import UKOLoader

_TTL_PATH = Path(__file__).resolve().parents[2] / "docs" / "ontology" / "uko.ttl"


# ---------------------------------------------------------------------------
# Turtle validation
# ---------------------------------------------------------------------------


@when("I load the UKO TTL file for uko_l2")
def step_load_ttl(ctx: Context) -> None:
    loader = UKOLoader()
    ctx.ontology = loader.load(_TTL_PATH)


@then("the ontology should load without errors for uko_l2")
def step_check_ontology_loaded(ctx: Context) -> None:
    assert ctx.ontology is not None
    assert len(ctx.ontology.nodes) > 0


@then("the ontology should contain uko-func prefix for uko_l2")
def step_check_func_prefix(ctx: Context) -> None:
    prefixes = {p.prefix for p in ctx.ontology.prefixes}
    assert "uko-func" in prefixes, f"uko-func not in {prefixes}"


@then("the ontology should contain uko-proc prefix for uko_l2")
def step_check_proc_prefix(ctx: Context) -> None:
    prefixes = {p.prefix for p in ctx.ontology.prefixes}
    assert "uko-proc" in prefixes, f"uko-proc not in {prefixes}"


@then('the ontology should contain node "{label}" for uko_l2')
def step_check_node_exists(ctx: Context, label: str) -> None:
    labels = {n.label for n in ctx.ontology.nodes}
    assert label in labels, f"Node '{label}' not in {labels}"


@then("the ontology should have at least {count:d} layer 2 nodes for uko_l2")
def step_check_layer2_count(ctx: Context, count: int) -> None:
    layer2 = [n for n in ctx.ontology.nodes if n.layer == 2]
    assert len(layer2) >= count, f"Expected >= {count} layer 2 nodes, got {len(layer2)}"


# ---------------------------------------------------------------------------
# ParadigmVocabulary frozen
# ---------------------------------------------------------------------------


@when("I try to mutate the vocabulary prefix for uko_l2")
def step_mutate_vocab_prefix(ctx: Context) -> None:
    with capture_error(ctx, ValidationError):
        ctx.vocabulary.prefix = "changed:"  # type: ignore[misc]  # deliberate mutation to test frozen model


# ---------------------------------------------------------------------------
# Additional coverage steps
# ---------------------------------------------------------------------------


@when("I create a paradigm vocabulary with empty prefix for uko_l2")
def step_create_vocab_empty_prefix(ctx: Context) -> None:
    with capture_error(ctx, ValidationError):
        ParadigmVocabulary(
            prefix="",
            iri="http://test",
            layer=2,
            parent_domain="uko-code:",
            max_depth=0,
        )


@when("I create a paradigm vocabulary with whitespace prefix for uko_l2")
def step_create_vocab_whitespace_prefix(ctx: Context) -> None:
    with capture_error(ctx, ValidationError):
        ParadigmVocabulary(
            prefix="   ",
            iri="http://test",
            layer=2,
            parent_domain="uko-code:",
            max_depth=0,
        )


@when("I create a vocabulary property with whitespace uri for uko_l2")
def step_create_vocab_prop_whitespace_uri(ctx: Context) -> None:
    with capture_error(ctx, ValidationError):
        VocabularyProperty(
            uri="   ",
            label="test",
            domain_uri="https://example.com/d",
            range_uri="https://example.com/r",
        )


@then("the registry should return False for non-string contains for uko_l2")
def step_check_non_string_contains(ctx: Context) -> None:
    assert 42 not in ctx.registry
    assert None not in ctx.registry


@when("I try to register a vocabulary with duplicate IRI for uko_l2")
def step_register_duplicate_iri(ctx: Context) -> None:
    oo = get_oo_vocabulary()
    dup = ParadigmVocabulary(
        prefix="uko-dup:",
        iri=oo.iri,
        layer=2,
        parent_domain="uko-code:",
    )
    with capture_error(ctx, ValueError):
        ctx.registry.register(dup)


@when("I create a paradigm vocabulary with prefix missing colon for uko_l2")
def step_create_paradigm_vocab_no_colon(ctx: Context) -> None:
    with capture_error(ctx, ValidationError):
        ParadigmVocabulary(
            prefix="uko-test",
            iri="https://example.org/test#",
            layer=2,
            parent_domain="uko-code:",
        )
