"""Step definitions for persistence_robot_alignment.feature.

Mirrors the Robot restart-simulation flow to keep unit/integration
persistence expectations aligned.  Uses file-backed SQLite so the
close-reopen cycle exercises real disk persistence.
"""

from __future__ import annotations

import tempfile
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

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

from cleveragents.domain.models.core.action import Action, ActionState
from cleveragents.domain.models.core.plan import (
    NamespacedName,
    Plan,
    PlanIdentity,
    PlanPhase,
    PlanTimestamps,
    ProcessingState,
    ProjectLink,
)
from cleveragents.infrastructure.database.models import init_database
from cleveragents.infrastructure.database.repositories import (
    ActionRepository,
    LifecyclePlanRepository,
)


def _phase_from_str(value: str) -> PlanPhase:
    """Convert a lowercase phase string to PlanPhase enum."""
    return PlanPhase(value)


def _state_from_str(value: str) -> ProcessingState:
    """Convert a lowercase state string to ProcessingState enum."""
    return ProcessingState(value)


@given("a fresh file-backed persistence alignment database")
def step_fresh_db(context: Context) -> None:
    tmp_path: str = tempfile.mktemp(suffix=".db")
    db_url: str = f"sqlite:///{tmp_path}"
    engine: Any = init_database(db_url)
    session: Session = Session(bind=engine)
    context._pra_db_path = tmp_path
    context._pra_session = session
    context._pra_engine = engine


@given('a persistence alignment action "{name}"')
def step_create_action(context: Context, name: str) -> None:
    session: Session = context._pra_session
    factory = lambda: session  # noqa: E731
    now: datetime = datetime(2026, 2, 1, tzinfo=UTC)
    action = Action(
        namespaced_name=NamespacedName.parse(name),
        description="Alignment test action",
        definition_of_done="Restart assertions pass",
        strategy_actor="local/strat",
        execution_actor="local/exec",
        state=ActionState.AVAILABLE,
        created_at=now,
        updated_at=now,
    )
    repo = ActionRepository(session_factory=factory)
    repo.create(action)
    session.commit()
    context._pra_action_name = name


@given(
    'a persistence alignment plan "{plan_id}" in phase "{phase}" with state "{state}"'
)
def step_create_plan(context: Context, plan_id: str, phase: str, state: str) -> None:
    session: Session = context._pra_session
    factory = lambda: session  # noqa: E731
    now: datetime = datetime(2026, 3, 1, tzinfo=UTC)
    plan = Plan(
        identity=PlanIdentity(plan_id=plan_id, attempt=1),
        namespaced_name=NamespacedName(namespace="local", name="aligned-plan"),
        action_name=context._pra_action_name,
        description="Alignment test plan",
        definition_of_done="Alignment pass",
        phase=_phase_from_str(phase),
        processing_state=_state_from_str(state),
        strategy_actor="local/strat",
        execution_actor="local/exec",
        project_links=[ProjectLink(project_name="local/proj-aligned")],
        timestamps=PlanTimestamps(created_at=now, updated_at=now),
        created_by="alignment-test",
        tags=["alignment"],
        reusable=True,
        read_only=False,
    )
    repo = LifecyclePlanRepository(session_factory=factory)
    repo.create(plan)
    session.commit()


@when("the persistence alignment database is closed and reopened")
def step_close_reopen(context: Context) -> None:
    context._pra_session.close()
    context._pra_engine.dispose()

    db_url: str = f"sqlite:///{context._pra_db_path}"
    engine2: Any = create_engine(db_url, echo=False)
    session2: Session = Session(bind=engine2)
    context._pra_session = session2
    context._pra_engine = engine2


@then('the persistence alignment plan "{plan_id}" should exist')
def step_plan_exists(context: Context, plan_id: str) -> None:
    session: Session = context._pra_session
    factory = lambda: session  # noqa: E731
    repo = LifecyclePlanRepository(session_factory=factory)
    plan = repo.get(plan_id)
    assert plan is not None, f"Plan {plan_id} not found after reopen"
    context._pra_plan = plan


@then('the persistence alignment plan phase should be "{phase}"')
def step_check_phase(context: Context, phase: str) -> None:
    assert context._pra_plan.phase == _phase_from_str(phase), (
        f"Expected phase {phase}, got {context._pra_plan.phase}"
    )


@then('the persistence alignment plan processing state should be "{state}"')
def step_check_state(context: Context, state: str) -> None:
    assert context._pra_plan.processing_state == _state_from_str(state), (
        f"Expected state {state}, got {context._pra_plan.processing_state}"
    )


@then('the persistence alignment plan description should be "{desc}"')
def step_check_desc(context: Context, desc: str) -> None:
    assert context._pra_plan.description == desc, (
        f"Expected '{desc}', got '{context._pra_plan.description}'"
    )


@then("the persistence alignment plan should have {count:d} project link")
def step_check_links(context: Context, count: int) -> None:
    actual: int = len(context._pra_plan.project_links)
    assert actual == count, f"Expected {count} links, got {actual}"
    # Clean up temp file
    tmp_path: str = context._pra_db_path
    context._pra_session.close()
    context._pra_engine.dispose()
    Path(tmp_path).unlink(missing_ok=True)
