"""Behave step implementations for the Robot TDD listener guard tests."""

import importlib.util
import sys
from itertools import count
from pathlib import Path
from types import ModuleType, SimpleNamespace
from typing import Any

from behave import given, then, when

# ---------------------------------------------------------------------------
# Load the listener module from the project's ``robot/`` directory using
# ``spec_from_file_location``.  A bare ``importlib.import_module("robot.…")``
# resolves to the *Robot Framework* package instead of the local directory
# because ``robot/`` has no ``__init__.py``.
# ---------------------------------------------------------------------------

_ROBOT_DIR = Path(__file__).resolve().parent.parent.parent / "robot"
_spec = importlib.util.spec_from_file_location(
    "tdd_expected_fail_listener",
    _ROBOT_DIR / "tdd_expected_fail_listener.py",
)
assert _spec is not None and _spec.loader is not None
listener: ModuleType = importlib.util.module_from_spec(_spec)
sys.modules["tdd_expected_fail_listener"] = listener
_spec.loader.exec_module(listener)

_COUNTER = count()


@when('I evaluate the infrastructure guard for message "{message}"')
def step_evaluate_infrastructure_guard(context: Any, message: str) -> None:
    context.infrastructure_guard_result = listener._is_infrastructure_error(message)


@when("I evaluate the infrastructure guard for an empty message")
def step_evaluate_infrastructure_guard_empty(context: Any) -> None:
    """Dedicated step for the empty-string case (Behave parse cannot match ``""`` to ``{message}``)."""
    context.infrastructure_guard_result = listener._is_infrastructure_error("")


@then("the infrastructure guard result is True")
def step_infrastructure_guard_true(context: Any) -> None:
    assert context.infrastructure_guard_result is True, (
        context.infrastructure_guard_result
    )


@then("the infrastructure guard result is False")
def step_infrastructure_guard_false(context: Any) -> None:
    assert context.infrastructure_guard_result is False, (
        context.infrastructure_guard_result
    )


def _build_status_namespace(status: str | None) -> SimpleNamespace | None:
    if status is None:
        return None
    return SimpleNamespace(status=status)


@given(
    'a result with setup status "{setup_status}" and teardown status "{teardown_status}" '
    'and message "{message}"'
)
def step_setup_teardown_result(
    context: Any, setup_status: str, teardown_status: str, message: str
) -> None:
    def _normalize(raw: str) -> str | None:
        return None if raw.upper() == "NONE" else raw

    context.guard_result_input = SimpleNamespace(
        setup=_build_status_namespace(_normalize(setup_status)),
        teardown=_build_status_namespace(_normalize(teardown_status)),
        message=message,
    )


@given(
    'a result with setup status "{setup_status}" and teardown status "{teardown_status}" '
    "and an empty message"
)
def step_setup_teardown_result_empty_message(
    context: Any, setup_status: str, teardown_status: str
) -> None:
    """Dedicated step for the empty-message case (Behave parse cannot match ``""`` to ``{message}``)."""

    def _normalize(raw: str) -> str | None:
        return None if raw.upper() == "NONE" else raw

    context.guard_result_input = SimpleNamespace(
        setup=_build_status_namespace(_normalize(setup_status)),
        teardown=_build_status_namespace(_normalize(teardown_status)),
        message="",
    )


@when("I evaluate the setup teardown guard")
def step_evaluate_setup_teardown_guard(context: Any) -> None:
    context.setup_teardown_guard_result = listener._has_setup_teardown_failure(
        context.guard_result_input
    )


@then("the setup teardown guard result is True")
def step_assert_setup_teardown_true(context: Any) -> None:
    assert context.setup_teardown_guard_result is True, (
        context.setup_teardown_guard_result
    )


@then("the setup teardown guard result is False")
def step_assert_setup_teardown_false(context: Any) -> None:
    assert context.setup_teardown_guard_result is False, (
        context.setup_teardown_guard_result
    )


@given("a tdd listener result with body keyword statuses")
def step_define_result_with_body_statuses(context: Any) -> None:
    body_statuses = [row["status"].strip() for row in context.table]
    context.result = SimpleNamespace(
        full_name=f"behave-listener::{next(_COUNTER)}",
        status="PASS",
        message="",
        setup=None,
        teardown=None,
        body=[SimpleNamespace(status=status) for status in body_statuses],
    )
    context.tags = ["tdd_issue", "tdd_issue_999", "tdd_expected_fail"]


@given('the result initial status is "{status}"')
def step_set_initial_status(context: Any, status: str) -> None:
    context.result.status = status
    if status == "FAIL":
        context.result.message = "Original failure"
    else:
        context.result.message = ""


@when('the listener processes the result with tags "{tags}"')
def step_listener_processes_result(context: Any, tags: str) -> None:
    listener.close()
    tag_list = [tag.strip().lower() for tag in tags.split()] if tags.strip() else []
    data = SimpleNamespace(tags=tag_list)
    context.result.full_name = f"behave-listener::{next(_COUNTER)}"
    listener.end_test(data, context.result)
    context.processed_result = context.result


@then('the processed result status is "{expected}"')
def step_assert_processed_status(context: Any, expected: str) -> None:
    assert context.processed_result.status == expected, context.processed_result.status


@then('the processed result message contains "{fragment}"')
def step_assert_processed_message_contains(context: Any, fragment: str) -> None:
    assert fragment in context.processed_result.message, (
        context.processed_result.message
    )
