"""Step definitions for sandbox protocol coverage boost.

Targets uncovered lines in src/cleveragents/infrastructure/sandbox/protocol.py:
- Lines 121-123: SandboxStatus.assert_transition raising SandboxStateError
- Lines 223, 228, 233, 248, 263, 278, 290, 301: Sandbox Protocol default stubs

All steps use the ``spcb`` prefix to avoid collisions with other step files.
"""

from behave import given, then, when
from behave.runner import Context

from cleveragents.infrastructure.sandbox.protocol import (
    Sandbox,
    SandboxStateError,
    SandboxStatus,
)

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


class _BareSandbox(Sandbox):
    """Minimal subclass that inherits all Protocol default stubs."""

    pass


# ---------------------------------------------------------------------------
# Given steps
# ---------------------------------------------------------------------------


@given('spcb sandbox status "{from_status}" and target status "{to_status}"')
def step_given_spcb_statuses(
    context: Context, from_status: str, to_status: str
) -> None:
    context.spcb_from_status = SandboxStatus(from_status)
    context.spcb_to_status = SandboxStatus(to_status)
    context.spcb_raised = None


@given("spcb a bare Sandbox protocol subclass instance")
def step_given_spcb_bare_sandbox(context: Context) -> None:
    context.spcb_bare = _BareSandbox()
    context.spcb_result = None
    context.spcb_raised = None


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


@when("spcb assert_transition is called with those statuses")
def step_when_spcb_assert_transition(context: Context) -> None:
    try:
        SandboxStatus.assert_transition(
            context.spcb_from_status, context.spcb_to_status
        )
        context.spcb_raised = None
    except SandboxStateError as exc:
        context.spcb_raised = exc


@when("spcb the sandbox_id property is accessed")
def step_when_spcb_sandbox_id(context: Context) -> None:
    context.spcb_result = context.spcb_bare.sandbox_id


@when("spcb the status property is accessed")
def step_when_spcb_status(context: Context) -> None:
    context.spcb_result = context.spcb_bare.status


@when("spcb the context property is accessed")
def step_when_spcb_context(context: Context) -> None:
    context.spcb_result = context.spcb_bare.context


@when('spcb the create method is called with plan_id "{plan_id}"')
def step_when_spcb_create(context: Context, plan_id: str) -> None:
    context.spcb_result = context.spcb_bare.create(plan_id)


@when('spcb the get_path method is called with path "{path}"')
def step_when_spcb_get_path(context: Context, path: str) -> None:
    context.spcb_result = context.spcb_bare.get_path(path)


@when('spcb the commit method is called with message "{message}"')
def step_when_spcb_commit(context: Context, message: str) -> None:
    context.spcb_result = context.spcb_bare.commit(message)


@when("spcb the rollback method is called")
def step_when_spcb_rollback(context: Context) -> None:
    context.spcb_result = context.spcb_bare.rollback()


@when("spcb the cleanup method is called")
def step_when_spcb_cleanup(context: Context) -> None:
    context.spcb_result = context.spcb_bare.cleanup()


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


@then("spcb a SandboxStateError should be raised")
def step_then_spcb_state_error(context: Context) -> None:
    assert context.spcb_raised is not None, (
        "Expected SandboxStateError but no exception was raised"
    )
    assert isinstance(context.spcb_raised, SandboxStateError), (
        f"Expected SandboxStateError but got {type(context.spcb_raised).__name__}"
    )


@then('spcb the error message should contain "{fragment}"')
def step_then_spcb_error_message_contains(context: Context, fragment: str) -> None:
    assert context.spcb_raised is not None, "No exception was raised"
    msg = str(context.spcb_raised)
    assert fragment in msg, f"Expected '{fragment}' in error message, got: {msg}"


@then("spcb no exception should be raised")
def step_then_spcb_no_exception(context: Context) -> None:
    assert context.spcb_raised is None, (
        f"Expected no exception but got: {context.spcb_raised}"
    )


@then("spcb the result should be None")
def step_then_spcb_result_none(context: Context) -> None:
    assert context.spcb_result is None, (
        f"Expected None but got: {context.spcb_result!r}"
    )
