"""Step definitions for repositories_coverage.feature.

Targets uncovered lines and branches in ActionRepository and
LifecyclePlanRepository in ``repositories.py``:
  Lines: 924, 943-946, 968, 1077-1078, 1124-1127, 1142-1143, 1160,
         1162-1163, 1188, 1267, 1283-1284, 1296-1298, 1303, 1315-1319,
         1338, 1342-1344, 1383, 1393-1394, 1411-1422
  Branches at: 923, 942, 944, 967, 1122, 1159, 1187, 1266, 1282, 1283,
               1295, 1337, 1382, 1414, 1416
"""

from __future__ import annotations

from datetime import datetime
from typing import Any

from behave import given, then, when
from behave.runner import Context
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError
from sqlalchemy.orm import Session, sessionmaker

from cleveragents.core.exceptions import DatabaseError
from cleveragents.domain.models.core.action import (
    Action,
    ActionArgument,
    ActionState,
    ArgumentRequirement,
    ArgumentType,
)
from cleveragents.domain.models.core.plan import (
    InvariantSource,
    NamespacedName,
    Plan,
    PlanIdentity,
    PlanInvariant,
    PlanPhase,
    PlanTimestamps,
    ProcessingState,
    ProjectLink,
)
from cleveragents.infrastructure.database.models import (
    Base,
    LifecyclePlanModel,
)
from cleveragents.infrastructure.database.repositories import (
    ActionInUseError,
    ActionRepository,
    DuplicatePlanError,
    LifecyclePlanRepository,
)

# Valid ULIDs for deterministic tests (Crockford base32, 26 chars)
_CB32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
_ULID_COUNTER = 0


def _next_ulid() -> str:
    """Return a unique, valid ULID string for each call."""
    global _ULID_COUNTER
    _ULID_COUNTER += 1
    n = _ULID_COUNTER
    suffix = ""
    for _ in range(8):
        suffix = _CB32[n % 32] + suffix
        n //= 32
    return f"01HGZ6FE0AQDYTR4BX{suffix}"


def _make_action(
    name: str = "local/test-action",
    state: str = "available",
) -> Action:
    """Create a minimal valid Action domain object."""
    parts = name.split("/", 1)
    namespace = parts[0] if len(parts) == 2 else "local"
    short_name = parts[1] if len(parts) == 2 else parts[0]

    return Action(
        namespaced_name=NamespacedName(namespace=namespace, name=short_name),
        description=f"Test action {short_name}",
        long_description=None,
        definition_of_done=f"Verify {short_name} completes",
        strategy_actor="local/strategist",
        execution_actor="local/executor",
        estimation_actor=None,
        review_actor=None,
        arguments=[],
        reusable=True,
        read_only=False,
        state=ActionState(state),
        created_at=datetime.now(),
        updated_at=datetime.now(),
        created_by=None,
        tags=[],
    )


def _make_plan(
    action_name: str,
    plan_name: str = "local/test-plan",
) -> Plan:
    """Create a minimal valid Plan domain object."""
    parts = plan_name.split("/", 1)
    namespace = parts[0] if len(parts) == 2 else "local"
    short_name = parts[1] if len(parts) == 2 else parts[0]
    now = datetime.now()

    return Plan(
        identity=PlanIdentity(plan_id=_next_ulid(), attempt=1),
        namespaced_name=NamespacedName(namespace=namespace, name=short_name),
        action_name=action_name,
        description=f"Test plan {short_name}",
        definition_of_done="Verify plan completes",
        phase=PlanPhase.STRATEGIZE,
        processing_state=ProcessingState.QUEUED,
        strategy_actor="local/strategist",
        execution_actor="local/executor",
        timestamps=PlanTimestamps(created_at=now, updated_at=now),
        error_message=None,
        error_details=None,
        created_by=None,
        tags=[],
        reusable=True,
        read_only=False,
    )


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


@given("a repos-cov in-memory database with lifecycle schema")
def step_repos_cov_clean_db(context: Context) -> None:
    """Create a fresh in-memory SQLite database with all tables."""
    engine = create_engine("sqlite:///:memory:")
    Base.metadata.create_all(engine)
    context.rc_engine = engine
    session = sessionmaker(bind=engine)()
    context.rc_session = session
    context.rc_session_factory = lambda: session
    context.rc_error = None
    context.rc_result = None
    context.rc_delete_result = None
    context.rc_result_list = None


@given("a repos-cov action repository")
def step_repos_cov_action_repo(context: Context) -> None:
    """Instantiate an ActionRepository using the session factory."""
    context.rc_action_repo = ActionRepository(
        session_factory=context.rc_session_factory,
    )


@given("a repos-cov lifecycle plan repository")
def step_repos_cov_plan_repo(context: Context) -> None:
    """Instantiate a LifecyclePlanRepository using the session factory."""
    context.rc_plan_repo = LifecyclePlanRepository(
        session_factory=context.rc_session_factory,
    )


# -----------------------------------------------------------------------
# Helpers for error-producing sessions
# -----------------------------------------------------------------------


class _QueryRaisingSession:
    """Stub session that raises OperationalError on query()."""

    def query(self, *args: Any, **kwargs: Any) -> None:
        """Raise OperationalError to simulate a transient DB failure."""
        raise OperationalError("simulated", {}, Exception("db down"))

    def rollback(self) -> None:
        """No-op rollback."""

    def close(self) -> None:
        """No-op close."""


@given(
    "a repos-cov action repository with a session that raises OperationalError on query"
)
def step_repos_cov_action_repo_error(context: Context) -> None:
    """Create an ActionRepository whose session raises on query."""
    context.rc_action_repo = ActionRepository(
        session_factory=lambda: _QueryRaisingSession(),  # type: ignore[arg-type]
    )


@given(
    "a repos-cov plan repository with a session that raises OperationalError on query"
)
def step_repos_cov_plan_repo_error(context: Context) -> None:
    """Create a LifecyclePlanRepository whose session raises on query."""
    context.rc_plan_repo = LifecyclePlanRepository(
        session_factory=lambda: _QueryRaisingSession(),  # type: ignore[arg-type]
    )


# -----------------------------------------------------------------------
# ActionRepository.get_by_name
# -----------------------------------------------------------------------


@when('repos-cov get_by_name is called with "{name}"')
def step_repos_cov_get_by_name(context: Context, name: str) -> None:
    """Call ActionRepository.get_by_name and capture result or error."""
    try:
        context.rc_result = context.rc_action_repo.get_by_name(name)
    except Exception as exc:
        context.rc_error = exc


@then('repos-cov a DatabaseError should be raised containing "{text}"')
def step_repos_cov_verify_db_error(context: Context, text: str) -> None:
    """Assert that a DatabaseError was raised with expected text."""
    assert context.rc_error is not None, (
        "Expected DatabaseError but no error was raised"
    )
    assert isinstance(context.rc_error, DatabaseError), (
        f"Expected DatabaseError, got {type(context.rc_error).__name__}: {context.rc_error}"
    )
    assert text in str(context.rc_error), (
        f"Expected '{text}' in error message: {context.rc_error}"
    )


@then('repos-cov the returned action should have name "{name}"')
def step_repos_cov_verify_action_name(context: Context, name: str) -> None:
    """Assert the returned action has the expected namespaced name."""
    assert context.rc_result is not None, "Expected an action, got None"
    assert str(context.rc_result.namespaced_name) == name, (
        f"Expected '{name}', got '{context.rc_result.namespaced_name}'"
    )


# -----------------------------------------------------------------------
# ActionRepository - persisting actions
# -----------------------------------------------------------------------


@given('repos-cov an available action "{name}" exists')
def step_repos_cov_persist_action(context: Context, name: str) -> None:
    """Create and persist an action with default state 'available'."""
    action = _make_action(name=name, state="available")
    context.rc_action_repo.create(action)
    context.rc_session.commit()
    context.rc_current_action = action


@given('repos-cov an action "{name}" exists with state "{state}"')
def step_repos_cov_persist_action_state(
    context: Context, name: str, state: str
) -> None:
    """Create and persist an action with the given state."""
    action = _make_action(name=name, state=state)
    context.rc_action_repo.create(action)
    context.rc_session.commit()
    context.rc_current_action = action


# -----------------------------------------------------------------------
# ActionRepository.get_by_namespace
# -----------------------------------------------------------------------


@when('repos-cov get_by_namespace is called with namespace "{ns}" and state "{state}"')
def step_repos_cov_get_by_namespace_state(
    context: Context, ns: str, state: str
) -> None:
    """Call get_by_namespace with namespace and state filter."""
    try:
        context.rc_result_list = context.rc_action_repo.get_by_namespace(
            ns, state=state
        )
    except Exception as exc:
        context.rc_error = exc


@when('repos-cov get_by_namespace is called with namespace "{ns}" expecting error')
def step_repos_cov_get_by_namespace_error(context: Context, ns: str) -> None:
    """Call get_by_namespace expecting a DatabaseError."""
    try:
        context.rc_action_repo.get_by_namespace(ns)
    except Exception as exc:
        context.rc_error = exc


@then("repos-cov exactly {count:d} action should be in the result list")
def step_repos_cov_verify_count_singular(context: Context, count: int) -> None:
    """Assert the result list has exactly count items (singular)."""
    assert context.rc_result_list is not None, "Result list is None"
    assert len(context.rc_result_list) == count, (
        f"Expected {count} action(s), got {len(context.rc_result_list)}"
    )


@then("repos-cov exactly {count:d} actions should be in the result list")
def step_repos_cov_verify_count_plural(context: Context, count: int) -> None:
    """Assert the result list has exactly count items (plural)."""
    assert context.rc_result_list is not None, "Result list is None"
    assert len(context.rc_result_list) == count, (
        f"Expected {count} action(s), got {len(context.rc_result_list)}"
    )


@then('repos-cov the result list should contain action named "{name}"')
def step_repos_cov_verify_list_contains(context: Context, name: str) -> None:
    """Assert the result list contains an action with the given name."""
    names = [str(a.namespaced_name) for a in context.rc_result_list]
    assert name in names, f"Expected '{name}' in {names}"


# -----------------------------------------------------------------------
# ActionRepository.list_available
# -----------------------------------------------------------------------


@when("repos-cov list_available is called without a namespace filter")
def step_repos_cov_list_available_all(context: Context) -> None:
    """Call list_available with no namespace."""
    try:
        context.rc_result_list = context.rc_action_repo.list_available()
    except Exception as exc:
        context.rc_error = exc


@when('repos-cov list_available is called with namespace "{ns}"')
def step_repos_cov_list_available_ns(context: Context, ns: str) -> None:
    """Call list_available with a namespace filter."""
    try:
        context.rc_result_list = context.rc_action_repo.list_available(namespace=ns)
    except Exception as exc:
        context.rc_error = exc


@when("repos-cov list_available is called and an error is expected")
def step_repos_cov_list_available_error(context: Context) -> None:
    """Call list_available expecting a DatabaseError."""
    try:
        context.rc_action_repo.list_available()
    except Exception as exc:
        context.rc_error = exc


# -----------------------------------------------------------------------
# ActionRepository.update - with min_value/max_value arguments
# -----------------------------------------------------------------------


@given("repos-cov the action has arguments with min_value and max_value set")
def step_repos_cov_add_minmax_args(context: Context) -> None:
    """Add arguments with min_value and max_value to the current action."""
    context.rc_current_action.arguments = [
        ActionArgument(
            name="count",
            arg_type=ArgumentType.INTEGER,
            requirement=ArgumentRequirement.REQUIRED,
            description="Number of items",
            default_value=5,
            min_value=1.0,
            max_value=100.0,
            validation_pattern=None,
        ),
    ]


@when("repos-cov the action is updated via the repository")
def step_repos_cov_update_action(context: Context) -> None:
    """Update the current action via the repository."""
    try:
        context.rc_result = context.rc_action_repo.update(context.rc_current_action)
        context.rc_session.commit()
    except Exception as exc:
        context.rc_error = exc


@then("repos-cov the update should succeed")
def step_repos_cov_update_success(context: Context) -> None:
    """Assert no error occurred during update."""
    assert context.rc_error is None, f"Unexpected error: {context.rc_error}"


@then("repos-cov the retrieved action arguments should have min_value and max_value")
def step_repos_cov_verify_minmax_args(context: Context) -> None:
    """Retrieve the action and verify min_value/max_value on arguments."""
    name = str(context.rc_current_action.namespaced_name)
    retrieved = context.rc_action_repo.get_by_id(name)
    assert retrieved is not None, f"Action '{name}' not found after update"
    assert len(retrieved.arguments) >= 1, "Expected at least 1 argument"
    arg = retrieved.arguments[0]
    assert arg.min_value == 1.0, f"Expected min_value=1.0, got {arg.min_value}"
    assert arg.max_value == 100.0, f"Expected max_value=100.0, got {arg.max_value}"


# -----------------------------------------------------------------------
# ActionRepository.delete
# -----------------------------------------------------------------------


@given('a repos-cov lifecycle plan references action "{action_name}"')
def step_repos_cov_create_referencing_plan(context: Context, action_name: str) -> None:
    """Insert a LifecyclePlanModel row referencing the specified action."""
    session: Session = context.rc_session
    now_iso = datetime.now().isoformat()
    pid = _next_ulid()
    plan_model = LifecyclePlanModel(
        plan_id=pid,
        root_plan_id=pid,
        action_name=action_name,
        phase="strategize",
        processing_state="queued",
        attempt=1,
        namespaced_name="local/ref-plan",
        namespace="local",
        description="Plan referencing action under test",
        effective_profile_snapshot="{}",
        created_at=now_iso,
        updated_at=now_iso,
        tags_json="[]",
    )
    session.add(plan_model)
    session.flush()
    session.commit()


@when('repos-cov delete is called for action "{name}"')
def step_repos_cov_delete_action(context: Context, name: str) -> None:
    """Call ActionRepository.delete and capture result or error."""
    try:
        context.rc_delete_result = context.rc_action_repo.delete(name)
        context.rc_session.commit()
    except Exception as exc:
        context.rc_error = exc


@then('repos-cov an ActionInUseError should be raised for "{name}"')
def step_repos_cov_verify_action_in_use(context: Context, name: str) -> None:
    """Assert that an ActionInUseError was raised."""
    assert context.rc_error is not None, (
        "Expected ActionInUseError but no error was raised"
    )
    assert isinstance(context.rc_error, ActionInUseError), (
        f"Expected ActionInUseError, got {type(context.rc_error).__name__}: {context.rc_error}"
    )
    assert name in str(context.rc_error), (
        f"Expected '{name}' in error message: {context.rc_error}"
    )


@then("repos-cov the delete result should be True")
def step_repos_cov_delete_true(context: Context) -> None:
    """Assert delete returned True."""
    assert context.rc_delete_result is True, (
        f"Expected True, got {context.rc_delete_result}"
    )


@then("repos-cov the delete result should be False")
def step_repos_cov_delete_false(context: Context) -> None:
    """Assert delete returned False."""
    assert context.rc_delete_result is False, (
        f"Expected False, got {context.rc_delete_result}"
    )


@then('repos-cov get_by_id for "{name}" should return None')
def step_repos_cov_verify_deleted(context: Context, name: str) -> None:
    """Assert the action no longer exists."""
    found = context.rc_action_repo.get_by_id(name)
    assert found is None, f"Expected None, got {found}"


# -----------------------------------------------------------------------
# DuplicatePlanError
# -----------------------------------------------------------------------


@when('repos-cov a DuplicatePlanError is created for plan_id "{plan_id}"')
def step_repos_cov_create_dup_plan_error(context: Context, plan_id: str) -> None:
    """Instantiate a DuplicatePlanError."""
    context.rc_dup_plan_error = DuplicatePlanError(plan_id)


@then('repos-cov the error message should contain "{text}"')
def step_repos_cov_verify_error_contains(context: Context, text: str) -> None:
    """Assert the error message contains expected text."""
    assert text in str(context.rc_dup_plan_error), (
        f"Expected '{text}' in '{context.rc_dup_plan_error}'"
    )


@then('repos-cov the error plan_id attribute should be "{plan_id}"')
def step_repos_cov_verify_plan_id_attr(context: Context, plan_id: str) -> None:
    """Assert the plan_id attribute is correct."""
    assert context.rc_dup_plan_error.plan_id == plan_id, (
        f"Expected plan_id='{plan_id}', got '{context.rc_dup_plan_error.plan_id}'"
    )


# -----------------------------------------------------------------------
# LifecyclePlanRepository.get_by_name
# -----------------------------------------------------------------------


@given(
    'a repos-cov lifecycle plan linked to "{action_name}" with name "{plan_name}" has been persisted'
)
def step_repos_cov_persist_plan(
    context: Context, action_name: str, plan_name: str
) -> None:
    """Create and persist a lifecycle plan."""
    plan = _make_plan(action_name=action_name, plan_name=plan_name)
    context.rc_plan_repo.create(plan)
    context.rc_session.commit()
    context.rc_current_plan = plan


@when('repos-cov plan get_by_name is called with "{name}"')
def step_repos_cov_plan_get_by_name(context: Context, name: str) -> None:
    """Call LifecyclePlanRepository.get_by_name and capture result or error."""
    try:
        context.rc_result = context.rc_plan_repo.get_by_name(name)
    except Exception as exc:
        context.rc_error = exc


@when('repos-cov plan get_by_name is called with "{name}" expecting error')
def step_repos_cov_plan_get_by_name_error(context: Context, name: str) -> None:
    """Call LifecyclePlanRepository.get_by_name expecting error."""
    try:
        context.rc_plan_repo.get_by_name(name)
    except Exception as exc:
        context.rc_error = exc


@then("repos-cov the returned plan should not be None")
def step_repos_cov_plan_not_none(context: Context) -> None:
    """Assert the returned plan is not None."""
    assert context.rc_result is not None, "Expected a plan, got None"


@then("repos-cov the returned plan should be None")
def step_repos_cov_plan_none(context: Context) -> None:
    """Assert the returned plan is None."""
    assert context.rc_result is None, f"Expected None, got {context.rc_result}"


# -----------------------------------------------------------------------
# LifecyclePlanRepository.update - with project_links, arguments, invariants
# -----------------------------------------------------------------------


class _StubInvariantWithText:
    """Stub invariant with .text and .source attributes (source has .value)."""

    def __init__(self, text: str, source: InvariantSource) -> None:
        """Initialise with text and InvariantSource enum."""
        self.text = text
        self.source = source


class _StubInvariantPlainString:
    """Stub invariant that is a plain string (no .text, no .source)."""

    def __init__(self, text: str) -> None:
        """Initialise with just a string value."""
        self._text = text

    def __str__(self) -> str:
        """Return the text representation."""
        return self._text


@given(
    "repos-cov the plan has project_links, arguments, and invariants with text attribute"
)
def step_repos_cov_plan_add_children(context: Context) -> None:
    """Add project_links, arguments, and invariants (with .text) to the plan."""
    plan = context.rc_current_plan
    plan.project_links = [
        ProjectLink(project_name="local/my-project", alias=None, read_only=False),
    ]
    plan.arguments = {"greeting": "hello", "count": 42}
    plan.arguments_order = ["greeting", "count"]
    plan.invariants = [
        PlanInvariant(text="Must not break prod", source=InvariantSource.PLAN),
    ]
    plan.error_message = None
    plan.error_details = None
    plan.changeset_id = None


@when("repos-cov the plan is updated via the repository")
def step_repos_cov_update_plan(context: Context) -> None:
    """Update the current plan via the repository."""
    try:
        context.rc_result = context.rc_plan_repo.update(context.rc_current_plan)
        context.rc_session.commit()
    except Exception as exc:
        context.rc_error = exc


@then("repos-cov the plan update should succeed")
def step_repos_cov_plan_update_success(context: Context) -> None:
    """Assert no error occurred during plan update."""
    assert context.rc_error is None, f"Unexpected error: {context.rc_error}"


@then("repos-cov the retrieved plan should have project links")
def step_repos_cov_verify_project_links(context: Context) -> None:
    """Retrieve the plan and verify project links."""
    plan_id = context.rc_current_plan.identity.plan_id
    retrieved = context.rc_plan_repo.get(plan_id)
    assert retrieved is not None, f"Plan '{plan_id}' not found after update"
    assert len(retrieved.project_links) >= 1, (
        f"Expected at least 1 project link, got {len(retrieved.project_links)}"
    )
    assert retrieved.project_links[0].project_name == "local/my-project", (
        f"Expected project_name='local/my-project', got '{retrieved.project_links[0].project_name}'"
    )


@then("repos-cov the retrieved plan should have arguments")
def step_repos_cov_verify_plan_args(context: Context) -> None:
    """Retrieve the plan and verify arguments."""
    plan_id = context.rc_current_plan.identity.plan_id
    retrieved = context.rc_plan_repo.get(plan_id)
    assert retrieved is not None, f"Plan '{plan_id}' not found after update"
    assert len(retrieved.arguments) >= 1, (
        f"Expected at least 1 argument, got {len(retrieved.arguments)}"
    )


@then("repos-cov the retrieved plan should have invariants")
def step_repos_cov_verify_plan_invariants(context: Context) -> None:
    """Retrieve the plan and verify invariants."""
    plan_id = context.rc_current_plan.identity.plan_id
    retrieved = context.rc_plan_repo.get(plan_id)
    assert retrieved is not None, f"Plan '{plan_id}' not found after update"
    assert len(retrieved.invariants) >= 1, (
        f"Expected at least 1 invariant, got {len(retrieved.invariants)}"
    )


# -----------------------------------------------------------------------
# LifecyclePlanRepository.update - invariants as plain strings
# -----------------------------------------------------------------------


@given("repos-cov the plan has invariants as plain strings without text attribute")
def step_repos_cov_plan_plain_string_invariants(context: Context) -> None:
    """Set invariants on the plan as objects without .text attribute (falls back to str)."""
    plan = context.rc_current_plan
    plan.project_links = []
    plan.arguments = {}
    plan.arguments_order = []
    # Use object.__setattr__ to bypass pydantic validation — the code path does:
    #   inv_text = inv.text if hasattr(inv, "text") else str(inv)
    object.__setattr__(
        plan,
        "invariants",
        [_StubInvariantPlainString("No side effects allowed")],
    )


# -----------------------------------------------------------------------
# LifecyclePlanRepository.update - invariant source with .value enum
# -----------------------------------------------------------------------


@given("repos-cov the plan has invariants with InvariantSource enum values")
def step_repos_cov_plan_enum_invariants(context: Context) -> None:
    """Set invariants using stub objects with InvariantSource enum source."""
    plan = context.rc_current_plan
    plan.project_links = []
    plan.arguments = {}
    plan.arguments_order = []
    # Use object.__setattr__ to bypass pydantic validation
    # _StubInvariantWithText has .text and .source (an InvariantSource enum with .value)
    object.__setattr__(
        plan,
        "invariants",
        [
            _StubInvariantWithText(
                "Keep API backward compatible", InvariantSource.ACTION
            )
        ],
    )


# -----------------------------------------------------------------------
# LifecyclePlanRepository.update - processing_state as plain string
# -----------------------------------------------------------------------


@given('repos-cov the plan processing_state is set to a plain string "{state}"')
def step_repos_cov_plan_plain_state(context: Context, state: str) -> None:
    """Set the processing_state to a plain string (no .value attribute)."""
    plan = context.rc_current_plan
    plan.project_links = []
    plan.arguments = {}
    plan.arguments_order = []
    plan.invariants = []
    # Use object.__setattr__ to bypass pydantic validation and set a raw string
    # that doesn't have a .value attribute — exercises the else branch at line 1317
    object.__setattr__(plan, "processing_state", state)


# -----------------------------------------------------------------------
# LifecyclePlanRepository.update - error_details and changeset_id
# -----------------------------------------------------------------------


@given("repos-cov the plan has error_message, error_details, and changeset_id set")
def step_repos_cov_plan_error_details(context: Context) -> None:
    """Set error_message, error_details, and changeset_id on the plan."""
    plan = context.rc_current_plan
    plan.project_links = []
    plan.arguments = {}
    plan.arguments_order = []
    plan.invariants = []
    plan.error_message = "Something went wrong"
    plan.error_details = {"step": "execute", "reason": "timeout"}
    plan.changeset_id = "01HGZ6FE0AQDYTR4BX00000001"


@then("repos-cov the retrieved plan should have the error_message set")
def step_repos_cov_verify_error_message(context: Context) -> None:
    """Retrieve the plan and verify error_message is set."""
    plan_id = context.rc_current_plan.identity.plan_id
    retrieved = context.rc_plan_repo.get(plan_id)
    assert retrieved is not None, f"Plan '{plan_id}' not found after update"
    assert retrieved.error_message == "Something went wrong", (
        f"Expected error_message='Something went wrong', got '{retrieved.error_message}'"
    )
