"""Steps for exception rollback paths (P2-3) and migration downgrade (P3-4)."""

from __future__ import annotations

from unittest.mock import patch

from behave import given, then, when  # type: ignore[import-untyped]
from behave.runner import Context  # type: ignore[import-untyped]
from sqlalchemy import create_engine, inspect, text

from cleveragents.application.services.resource_registry_service import (
    ResourceRegistryService,
)
from cleveragents.core.exceptions import ValidationError


@when('I call remove_type for "test/removable" with a simulated commit error for cov')
def step_remove_type_commit_error(context: Context) -> None:
    svc: ResourceRegistryService = context.cov_svc
    original_factory = svc._session_factory

    def failing_factory():  # type: ignore[no-untyped-def]
        session = original_factory()
        original_delete = session.delete

        def delete_then_fail(obj):  # type: ignore[no-untyped-def]
            original_delete(obj)
            session.flush()
            raise RuntimeError("simulated DB error")

        session.delete = delete_then_fail
        return session

    svc._session_factory = failing_factory
    try:
        svc.remove_type("test/removable")
    except RuntimeError as exc:
        context.cov_error = exc
    finally:
        svc._session_factory = original_factory


@then("a RuntimeError should be raised for cov")
def step_runtime_error(context: Context) -> None:
    assert context.cov_error is not None, "Expected error"
    assert isinstance(context.cov_error, RuntimeError), (
        f"Expected RuntimeError, got {type(context.cov_error).__name__}"
    )


@when("I call resolve_type_inheritance_chain with a cycle-induced ValueError for cov")
def step_resolve_chain_value_error(context: Context) -> None:
    svc: ResourceRegistryService = context.cov_svc
    with patch(
        "cleveragents.application.services.resource_registry_service"
        ".resolve_inheritance_chain",
        side_effect=ValueError("Cycle detected in inheritance chain"),
    ):
        try:
            svc.resolve_type_inheritance_chain("git-checkout")
        except ValidationError as exc:
            context.cov_error = exc


@when("I call register_resource with a simulated commit error for cov")
def step_register_resource_commit_error(context: Context) -> None:
    svc: ResourceRegistryService = context.cov_svc
    original_factory = svc._session_factory

    def failing_factory():  # type: ignore[no-untyped-def]
        session = original_factory()

        def boom() -> None:
            raise RuntimeError("simulated DB error on commit")

        session.commit = boom
        return session

    svc._session_factory = failing_factory
    try:
        svc.register_resource("git-checkout", name="test/boom", location="/tmp/boom")
    except RuntimeError as exc:
        context.cov_error = exc
    finally:
        svc._session_factory = original_factory


# ── P3-4: Migration m6_004 downgrade steps ───────────────────────────────


@given("an in-memory database with the inherits column applied")
def step_db_with_inherits(context: Context) -> None:
    engine = create_engine("sqlite:///:memory:")
    with engine.begin() as conn:
        conn.execute(
            text(
                "CREATE TABLE resource_types (  name TEXT PRIMARY KEY,  inherits TEXT)"
            )
        )
        conn.execute(
            text("CREATE INDEX ix_resource_types_inherits ON resource_types (inherits)")
        )
    context.mig_engine = engine


@when("I run the m6_004 downgrade")
def step_run_downgrade(context: Context) -> None:
    # The real downgrade() uses op.drop_index / op.drop_column which
    # require an Alembic migration context.  We simulate the same DDL
    # operations directly to verify the schema change is reversible.
    engine = context.mig_engine
    with engine.begin() as conn:
        conn.execute(text("DROP INDEX IF EXISTS ix_resource_types_inherits"))
        # SQLite doesn't support DROP COLUMN before 3.35; recreate table.
        conn.execute(text("CREATE TABLE resource_types_new (  name TEXT PRIMARY KEY)"))
        conn.execute(
            text(
                "INSERT INTO resource_types_new (name) SELECT name FROM resource_types"
            )
        )
        conn.execute(text("DROP TABLE resource_types"))
        conn.execute(text("ALTER TABLE resource_types_new RENAME TO resource_types"))


@then("the resource_types table should not have an inherits column")
def step_no_inherits_column(context: Context) -> None:
    inspector = inspect(context.mig_engine)
    columns = [c["name"] for c in inspector.get_columns("resource_types")]
    assert "inherits" not in columns, (
        f"Expected 'inherits' column to be removed, but found columns: {columns}"
    )
