"""Step definitions for resolver_handler_coverage_boost.feature.

Targets uncovered lines in src/cleveragents/resource/handlers/resolver.py:
  - Lines 72-73: empty module_path or class_name after rsplit on ':'
  - Lines 84-87: generic Exception (not ModuleNotFoundError) during import
  - Lines 99-102: Exception during handler class __init__
  - Lines 106-107: instance does not satisfy ResourceHandler protocol

"""

from __future__ import annotations

import types
from typing import Any
from unittest.mock import patch

from behave import given, then, when  # type: ignore[import-untyped]

from cleveragents.resource.handlers.resolver import (
    HandlerResolutionError,
    clear_handler_cache,
    resolve_handler,
)

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


@given("the handler cache is cleared")
def step_clear_cache(context: Any) -> None:
    clear_handler_cache()
    context.resolver_error: HandlerResolutionError | None = None
    context.rigged_ref: str | None = None


# ---------------------------------------------------------------------------
# Given steps - prepare mock modules for injection via importlib patch
# ---------------------------------------------------------------------------


@given("a module import that raises a generic RuntimeError")
def step_import_raises_runtime_error(context: Any) -> None:
    """Prepare a handler reference whose import will raise RuntimeError (not
    ModuleNotFoundError), exercising lines 84-87."""
    context.rigged_ref = "fake_boom_module:FakeHandler"

    original_import = __import__("importlib").import_module

    def _patched_import(name: str, *args: Any, **kwargs: Any) -> types.ModuleType:
        if name == "fake_boom_module":
            raise RuntimeError("simulated import explosion")
        return original_import(name, *args, **kwargs)

    context._import_patcher = patch(
        "cleveragents.resource.handlers.resolver.importlib.import_module",
        side_effect=_patched_import,
    )
    context._import_patcher.start()


@given("a module whose handler class raises TypeError on init")
def step_class_init_raises(context: Any) -> None:
    """Prepare a handler reference whose class __init__ raises, exercising
    lines 99-102."""
    context.rigged_ref = "fake_init_module:BrokenHandler"

    # Build a synthetic module with a class whose __init__ always fails
    fake_module = types.ModuleType("fake_init_module")

    class BrokenHandler:
        def __init__(self) -> None:
            raise TypeError("cannot construct this handler")

    fake_module.BrokenHandler = BrokenHandler  # type: ignore[attr-defined]

    original_import = __import__("importlib").import_module

    def _patched_import(name: str, *args: Any, **kwargs: Any) -> types.ModuleType:
        if name == "fake_init_module":
            return fake_module
        return original_import(name, *args, **kwargs)

    context._import_patcher = patch(
        "cleveragents.resource.handlers.resolver.importlib.import_module",
        side_effect=_patched_import,
    )
    context._import_patcher.start()


@given("a module whose handler class does not satisfy ResourceHandler")
def step_class_not_protocol(context: Any) -> None:
    """Prepare a handler reference whose class does NOT implement the
    ResourceHandler protocol, exercising lines 106-107."""
    context.rigged_ref = "fake_proto_module:NotAHandler"

    fake_module = types.ModuleType("fake_proto_module")

    class NotAHandler:
        """Has no ``resolve`` method - fails the runtime_checkable check."""

        pass

    fake_module.NotAHandler = NotAHandler  # type: ignore[attr-defined]

    original_import = __import__("importlib").import_module

    def _patched_import(name: str, *args: Any, **kwargs: Any) -> types.ModuleType:
        if name == "fake_proto_module":
            return fake_module
        return original_import(name, *args, **kwargs)

    context._import_patcher = patch(
        "cleveragents.resource.handlers.resolver.importlib.import_module",
        side_effect=_patched_import,
    )
    context._import_patcher.start()


# ---------------------------------------------------------------------------
# When steps
# ---------------------------------------------------------------------------


@when('I try to resolve handler ref "{ref}"')
def step_try_resolve_ref(context: Any, ref: str) -> None:
    """Attempt to resolve a literal handler reference string, expecting failure."""
    try:
        resolve_handler(ref)
        context.resolver_error = None
    except HandlerResolutionError as exc:
        context.resolver_error = exc


@when("I try to resolve the rigged handler reference")
def step_try_resolve_rigged(context: Any) -> None:
    """Resolve the handler reference prepared by a Given step that patches
    importlib.import_module."""
    assert context.rigged_ref is not None, "No rigged reference was configured"
    try:
        resolve_handler(context.rigged_ref)
        context.resolver_error = None
    except HandlerResolutionError as exc:
        context.resolver_error = exc
    finally:
        # Always stop the patcher after use
        if hasattr(context, "_import_patcher") and context._import_patcher is not None:
            context._import_patcher.stop()
            context._import_patcher = None


# ---------------------------------------------------------------------------
# Then steps
# ---------------------------------------------------------------------------


@then('a handler resolution error should be raised with message "{fragment}"')
def step_assert_resolution_error_contains(context: Any, fragment: str) -> None:
    assert context.resolver_error is not None, (
        "Expected HandlerResolutionError but no error was raised"
    )
    assert isinstance(context.resolver_error, HandlerResolutionError), (
        f"Expected HandlerResolutionError, got {type(context.resolver_error).__name__}"
    )
    error_text = str(context.resolver_error)
    assert fragment.lower() in error_text.lower(), (
        f"Expected '{fragment}' in error message, got: {error_text}"
    )
