"""Step definitions for TDD issue #7501 — PlanResult.success derivation.

Validates that PlanRepository._to_domain derives PlanResult.success from
the dedicated result_success column rather than from error_message is None.

Targets ``PlanRepository`` in
``src/cleveragents/infrastructure/database/repositories.py``.
"""

from __future__ import annotations

import warnings
from datetime import datetime

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

from cleveragents.infrastructure.database.models import Base, PlanModel
from cleveragents.infrastructure.database.repositories import PlanRepository


def _make_project(session: object) -> int:
    """Insert a minimal project row and return its id."""
    from cleveragents.infrastructure.database.models import ProjectModel

    project = ProjectModel(
        name="test-project-7501",
        path="/tmp/test-project-7501",
        settings={},
    )
    session.add(project)  # type: ignore[attr-defined]
    session.flush()  # type: ignore[attr-defined]
    return int(project.id)  # type: ignore[attr-defined]


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


@given("a clean in-memory database for plan result success tests")
def step_clean_db_result_success(context: Context) -> None:
    """Create a fresh in-memory SQLite database with all tables."""
    engine = create_engine("sqlite:///:memory:")
    Base.metadata.create_all(engine)
    context.rs_db_engine = engine
    session = sessionmaker(bind=engine)()
    context.rs_db_session = session


@given("a legacy plan repository backed by the database")
def step_legacy_plan_repo(context: Context) -> None:
    """Instantiate a PlanRepository using the in-memory session."""
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", DeprecationWarning)
        context.rs_plan_repo = PlanRepository(session=context.rs_db_session)
    context.rs_retrieved_plan = None
    context.rs_project_id = _make_project(context.rs_db_session)
    context.rs_db_session.commit()


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


def _insert_plan_model(
    session: object,
    project_id: int,
    applied_at: datetime | None = None,
    error_message: str | None = None,
    result_success: bool | None = None,
) -> int:
    """Insert a PlanModel row directly and return its id."""
    db_plan = PlanModel(
        project_id=project_id,
        name="test-plan-7501",
        prompt="Test prompt for issue 7501",
        status="pending",
        current=False,
        applied_at=applied_at,
        error_message=error_message,
        result_success=result_success,
    )
    session.add(db_plan)  # type: ignore[attr-defined]
    session.flush()  # type: ignore[attr-defined]
    session.commit()  # type: ignore[attr-defined]
    return int(db_plan.id)  # type: ignore[attr-defined]


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


@given("a legacy plan with applied_at set and result_success column True")
def step_plan_result_success_true(context: Context) -> None:
    """Insert a plan row with result_success=True."""
    context.rs_plan_id = _insert_plan_model(
        session=context.rs_db_session,
        project_id=context.rs_project_id,
        applied_at=datetime.now(),
        error_message=None,
        result_success=True,
    )


@given("a legacy plan with applied_at set and result_success column False")
def step_plan_result_success_false(context: Context) -> None:
    """Insert a plan row with result_success=False."""
    context.rs_plan_id = _insert_plan_model(
        session=context.rs_db_session,
        project_id=context.rs_project_id,
        applied_at=datetime.now(),
        error_message=None,
        result_success=False,
    )


@given("a legacy plan with a build error_message and result_success column True")
def step_plan_build_error_result_success_true(context: Context) -> None:
    """Insert a plan row with a build error but result_success=True.

    This is the core bug scenario: a plan that had a build error but later
    succeeded in the apply phase.  Without the fix, success would be derived
    as False (because error_message is not None).  With the fix, success is
    correctly derived as True from result_success.
    """
    context.rs_plan_id = _insert_plan_model(
        session=context.rs_db_session,
        project_id=context.rs_project_id,
        applied_at=datetime.now(),
        error_message="Build phase error: compilation failed",
        result_success=True,
    )


@given(
    "a legacy plan with applied_at set and result_success column NULL and no error_message"
)
def step_plan_null_result_success_no_error(context: Context) -> None:
    """Insert a plan row with result_success=NULL and no error_message.

    Simulates a pre-migration record.  The legacy heuristic should mark it
    as success=True because error_message is None.
    """
    context.rs_plan_id = _insert_plan_model(
        session=context.rs_db_session,
        project_id=context.rs_project_id,
        applied_at=datetime.now(),
        error_message=None,
        result_success=None,
    )


@given(
    "a legacy plan with applied_at set and result_success column NULL and an error_message"
)
def step_plan_null_result_success_with_error(context: Context) -> None:
    """Insert a plan row with result_success=NULL and an error_message.

    Simulates a pre-migration record that failed.  The legacy heuristic
    should mark it as success=False because error_message is not None.
    """
    context.rs_plan_id = _insert_plan_model(
        session=context.rs_db_session,
        project_id=context.rs_project_id,
        applied_at=datetime.now(),
        error_message="Apply phase error: deployment failed",
        result_success=None,
    )


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


@when("the legacy plan is retrieved from the repository")
def step_retrieve_legacy_plan(context: Context) -> None:
    """Retrieve the plan via PlanRepository.get_by_id."""
    context.rs_retrieved_plan = context.rs_plan_repo.get_by_id(context.rs_plan_id)


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


@then("the retrieved plan result success should be True")
def step_check_result_success_true(context: Context) -> None:
    """Assert that the retrieved plan's result.success is True."""
    plan = context.rs_retrieved_plan
    assert plan is not None, "Expected a plan but got None"
    assert plan.result is not None, "Expected plan.result to be set but it was None"
    assert plan.result.success is True, (
        f"Expected plan.result.success=True but got {plan.result.success!r}"
    )


@then("the retrieved plan result success should be False")
def step_check_result_success_false(context: Context) -> None:
    """Assert that the retrieved plan's result.success is False."""
    plan = context.rs_retrieved_plan
    assert plan is not None, "Expected a plan but got None"
    assert plan.result is not None, "Expected plan.result to be set but it was None"
    assert plan.result.success is False, (
        f"Expected plan.result.success=False but got {plan.result.success!r}"
    )
