"""Step definitions for plan phase/state constraint rebaseline tests."""

from __future__ import annotations

from datetime import UTC, datetime
from typing import Any

from behave import given, then, when
from sqlalchemy import create_engine, text
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session, sessionmaker

from cleveragents.infrastructure.database.models import (
    Base,
    LifecycleActionModel,
    LifecyclePlanModel,
)

_VALID_ULID_PLAN = "01HV000000000000000000PH01"
_VALID_ULID_COUNTER = 0


def _next_ulid() -> str:
    global _VALID_ULID_COUNTER
    _VALID_ULID_COUNTER += 1
    suffix = str(_VALID_ULID_COUNTER).zfill(4)
    return f"01HV0000000000000000PH{suffix}"


def _now_iso() -> str:
    return datetime.now(tz=UTC).isoformat()


def _ensure_action(session: Session) -> None:
    """Ensure a parent action exists for FK references."""
    existing = (
        session.query(LifecycleActionModel)
        .filter_by(namespaced_name="local/phase-test-action")
        .first()
    )
    if existing is not None:
        return

    action = LifecycleActionModel()
    action.namespaced_name = "local/phase-test-action"
    action.namespace = "local"
    action.name = "phase-test-action"
    action.description = "Test action for phase migration"
    action.definition_of_done = "Done"
    action.strategy_actor = "local/s"
    action.execution_actor = "local/e"
    action.state = "available"
    action.tags_json = "[]"
    action.created_at = _now_iso()
    action.updated_at = _now_iso()
    session.add(action)
    session.commit()


@given("the plan phase rebaseline database is initialized")
def step_plan_phase_rebaseline_db_init(context: Any) -> None:
    """Set up an in-memory database with the schema including new constraints."""
    engine = create_engine("sqlite:///:memory:")

    # Enable FK enforcement
    from sqlalchemy import event

    @event.listens_for(engine, "connect")
    def set_sqlite_pragma(dbapi_connection: Any, _: Any) -> None:
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON")
        cursor.close()

    Base.metadata.create_all(engine)
    factory = sessionmaker(bind=engine)
    session = factory()
    _ensure_action(session)
    context.phase_rebaseline_engine = engine
    context.phase_rebaseline_session = session


@when('I insert a plan with phase "{phase}" and state "{state}"')
def step_insert_plan_with_phase_and_state(context: Any, phase: str, state: str) -> None:
    """Insert a plan with the specified phase and state."""
    session: Session = context.phase_rebaseline_session
    ulid = _next_ulid()
    now = _now_iso()
    plan = LifecyclePlanModel()
    plan.plan_id = ulid
    plan.root_plan_id = ulid
    plan.action_name = "local/phase-test-action"
    plan.namespaced_name = "local/phase-test-plan"
    plan.namespace = "local"
    plan.phase = phase
    plan.processing_state = state
    plan.description = "Test plan"
    plan.tags_json = "[]"
    plan.effective_profile_snapshot = "{}"
    plan.created_at = now
    plan.updated_at = now
    try:
        session.add(plan)
        session.commit()
        context.phase_rebaseline_result = "ok"
        context.phase_rebaseline_plan_id = ulid
    except (IntegrityError, Exception) as exc:
        session.rollback()
        context.phase_rebaseline_result = "error"
        context.phase_rebaseline_error = str(exc)


@when('I try to insert a plan with phase "{phase}" and state "{state}"')
def step_try_insert_plan_with_phase_and_state(
    context: Any, phase: str, state: str
) -> None:
    """Try to insert a plan with the specified phase and state, expecting failure."""
    session: Session = context.phase_rebaseline_session
    ulid = _next_ulid()
    now = _now_iso()
    try:
        session.execute(
            text(
                "INSERT INTO v3_plans "
                "(plan_id, action_name, namespaced_name, namespace, "
                "phase, processing_state, description, tags_json, "
                "created_at, updated_at) "
                "VALUES (:pid, :aname, :nname, :ns, :phase, :state, "
                ":desc, :tags, :cat, :uat)"
            ),
            {
                "pid": ulid,
                "aname": "local/phase-test-action",
                "nname": "local/try-plan",
                "ns": "local",
                "phase": phase,
                "state": state,
                "desc": "Test plan",
                "tags": "[]",
                "cat": now,
                "uat": now,
            },
        )
        session.commit()
        context.phase_rebaseline_result = "ok"
    except (IntegrityError, Exception) as exc:
        session.rollback()
        context.phase_rebaseline_result = "error"
        context.phase_rebaseline_error = str(exc)


@when("I insert a plan using default phase")
def step_insert_plan_default_phase(context: Any) -> None:
    """Insert a plan without specifying a phase to test the ORM default."""
    session: Session = context.phase_rebaseline_session
    ulid = _next_ulid()
    now = _now_iso()
    # Use ORM to create plan without specifying phase — should default to 'action'
    plan = LifecyclePlanModel()
    plan.plan_id = ulid
    plan.root_plan_id = ulid
    plan.action_name = "local/phase-test-action"
    plan.namespaced_name = "local/default-phase-plan"
    plan.namespace = "local"
    plan.description = "Default phase plan"
    plan.tags_json = "[]"
    plan.effective_profile_snapshot = "{}"
    plan.created_at = now
    plan.updated_at = now
    session.add(plan)
    session.commit()
    context.phase_rebaseline_plan_id = ulid


@then('the plan should be persisted successfully with phase "{phase}"')
def step_verify_plan_persisted_phase(context: Any, phase: str) -> None:
    """Verify the plan was persisted with the expected phase."""
    assert context.phase_rebaseline_result == "ok", (
        f"Expected ok, got {context.phase_rebaseline_result}"
    )
    session: Session = context.phase_rebaseline_session
    plan = (
        session.query(LifecyclePlanModel)
        .filter_by(plan_id=context.phase_rebaseline_plan_id)
        .one()
    )
    assert plan.phase == phase


@then('the plan should be persisted successfully with state "{state}"')
def step_verify_plan_persisted_state(context: Any, state: str) -> None:
    """Verify the plan was persisted with the expected state."""
    assert context.phase_rebaseline_result == "ok", (
        f"Expected ok, got {context.phase_rebaseline_result}"
    )
    session: Session = context.phase_rebaseline_session
    plan = (
        session.query(LifecyclePlanModel)
        .filter_by(plan_id=context.phase_rebaseline_plan_id)
        .one()
    )
    assert plan.processing_state == state


@then("the insert should fail with a constraint violation")
def step_verify_constraint_violation(context: Any) -> None:
    """Verify the insert failed."""
    assert context.phase_rebaseline_result == "error", (
        f"Expected error, got {context.phase_rebaseline_result}"
    )


@then('the rebaselined plan phase should be "{phase}"')
def step_verify_rebaselined_plan_phase_default(context: Any, phase: str) -> None:
    """Verify the plan's phase matches the expected value."""
    session: Session = context.phase_rebaseline_session
    plan = (
        session.query(LifecyclePlanModel)
        .filter_by(plan_id=context.phase_rebaseline_plan_id)
        .one()
    )
    assert plan.phase == phase, f"Expected phase '{phase}', got '{plan.phase}'"
