"""Step definitions for semantic validation suite scenarios.

Loads JSON fixture files covering language-porting mismatches, dependency
graph violations, API surface changes, cross-file symbol resolution,
and circular import detection.  Runs the appropriate semantic validation
rule against each fixture and asserts the expected outcome.

Uses :mod:`features.fixtures.validation.suite_helpers` for shared
loading, lookup, and verification logic.
"""

from __future__ import annotations

import sys
from pathlib import Path
from typing import TYPE_CHECKING, Any

from behave import given, then, when

if TYPE_CHECKING:
    from behave.runner import Context

# Ensure the fixture helpers package is importable
_FIXTURES_PARENT = str(Path(__file__).resolve().parents[1] / "fixtures" / "validation")
if _FIXTURES_PARENT not in sys.path:
    sys.path.insert(0, _FIXTURES_PARENT)

from suite_helpers import (  # noqa: E402
    API_FILENAME,
    CIRCULAR_FILENAME,
    CROSS_FILE_FILENAME,
    DEPENDENCY_FILENAME,
    PORTING_FILENAME,
    find_fixture,
    load_fixtures,
    validate_fixture_schema,
    verify_fixture_result,
)

# ---------------------------------------------------------------------------
# Lazy fixture cache (FLAW-3 fix: avoid module-level loading)
# ---------------------------------------------------------------------------

_fixture_cache: dict[str, list[dict[str, Any]]] = {}


def _get_fixtures(filename: str) -> list[dict[str, Any]]:
    """Lazily load and cache fixtures by filename."""
    if filename not in _fixture_cache:
        _fixture_cache[filename] = load_fixtures(filename)
    return _fixture_cache[filename]


# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------


@given("a semantic validation suite test environment")
def step_suite_env(context: Context) -> None:
    context.suite_fixture = None
    context.suite_result = None


# ---------------------------------------------------------------------------
# Given steps - load individual fixtures
# ---------------------------------------------------------------------------


@given('the porting fixture "{name}"')
def step_porting_fixture(context: Context, name: str) -> None:
    context.suite_fixture = find_fixture(_get_fixtures(PORTING_FILENAME), name)


@given('the dependency fixture "{name}"')
def step_dependency_fixture(context: Context, name: str) -> None:
    context.suite_fixture = find_fixture(_get_fixtures(DEPENDENCY_FILENAME), name)


@given('the API surface fixture "{name}"')
def step_api_fixture(context: Context, name: str) -> None:
    context.suite_fixture = find_fixture(_get_fixtures(API_FILENAME), name)


@given('the cross-file fixture "{name}"')
def step_cross_file_fixture(context: Context, name: str) -> None:
    context.suite_fixture = find_fixture(_get_fixtures(CROSS_FILE_FILENAME), name)


@given('the circular import fixture "{name}"')
def step_circular_fixture(context: Context, name: str) -> None:
    context.suite_fixture = find_fixture(_get_fixtures(CIRCULAR_FILENAME), name)


# ---------------------------------------------------------------------------
# When step - run the expected rule
# ---------------------------------------------------------------------------


@when("the suite runs the expected rule")
def step_run_expected_rule(context: Context) -> None:
    from suite_helpers import RULE_MAP

    fixture = context.suite_fixture
    if fixture is None:
        msg = "No fixture loaded"
        raise RuntimeError(msg)
    rule_name: str = fixture["expected_rule"]
    rule = RULE_MAP[rule_name]
    source: str = fixture["source"]
    filename: str = fixture["filename"]
    context.suite_result = rule.check(source, filename)


# ---------------------------------------------------------------------------
# Then steps - verify outcomes (with data field verification: FLAW-2 fix)
# ---------------------------------------------------------------------------


@then("the suite result matches the fixture expectation")
def step_verify_result(context: Context) -> None:
    fixture = context.suite_fixture
    result = context.suite_result
    if fixture is None or result is None:
        msg = "Fixture or result not set"
        raise RuntimeError(msg)

    verify_fixture_result(fixture, result)


# ---------------------------------------------------------------------------
# Fixture-loading checks (FLAW-4 fix: validate schema, not just len > 0)
# ---------------------------------------------------------------------------


@then("all porting fixtures load without error")
def step_all_porting(context: Context) -> None:
    fixtures = _get_fixtures(PORTING_FILENAME)
    assert len(fixtures) > 0
    for f in fixtures:
        validate_fixture_schema(f)


@then("all dependency fixtures load without error")
def step_all_dependency(context: Context) -> None:
    fixtures = _get_fixtures(DEPENDENCY_FILENAME)
    assert len(fixtures) > 0
    for f in fixtures:
        validate_fixture_schema(f)


@then("all API surface fixtures load without error")
def step_all_api(context: Context) -> None:
    fixtures = _get_fixtures(API_FILENAME)
    assert len(fixtures) > 0
    for f in fixtures:
        validate_fixture_schema(f)


@then("all cross-file fixtures load without error")
def step_all_cross_file(context: Context) -> None:
    fixtures = _get_fixtures(CROSS_FILE_FILENAME)
    assert len(fixtures) > 0
    for f in fixtures:
        validate_fixture_schema(f)


@then("all circular import fixtures load without error")
def step_all_circular(context: Context) -> None:
    fixtures = _get_fixtures(CIRCULAR_FILENAME)
    assert len(fixtures) > 0
    for f in fixtures:
        validate_fixture_schema(f)
