"""Helper script for Robot Framework phase reversion tests."""

from __future__ import annotations

import sys
from pathlib import Path

# Ensure src is importable when run from workspace root
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))

from cleveragents.application.services.plan_lifecycle_service import (
    PlanLifecycleService,
)
from cleveragents.config.settings import Settings
from cleveragents.core.exceptions import PlanError
from cleveragents.domain.models.core.action import (
    ActionArgument,
    ArgumentRequirement,
    ArgumentType,
)
from cleveragents.domain.models.core.plan import (
    AutomationProfileProvenance,
    AutomationProfileRef,
    InvariantSource,
    PlanInvariant,
    PlanPhase,
    ProcessingState,
    ProjectLink,
)


def _make_service() -> PlanLifecycleService:
    """Create a fresh PlanLifecycleService."""
    return PlanLifecycleService(settings=Settings())


def _create_plan(
    service: PlanLifecycleService,
    action_name: str = "local/test-action",
    profile: str = "manual",
) -> str:
    """Create an action and plan, return plan_id."""
    service.create_action(
        name=action_name,
        description=f"Test {action_name}",
        definition_of_done="Tests pass",
        strategy_actor="local/strategist",
        execution_actor="local/executor",
    )
    plan = service.use_action(action_name)
    plan.automation_profile = AutomationProfileRef(
        profile_name=profile,
        provenance=AutomationProfileProvenance.PLAN,
    )
    return plan.identity.plan_id


def _advance_to_execute(service: PlanLifecycleService, plan_id: str) -> None:
    """Advance plan to Execute phase."""
    service.start_strategize(plan_id)
    service.complete_strategize(plan_id)
    plan = service.get_plan(plan_id)
    if plan.phase != PlanPhase.EXECUTE:
        service.execute_plan(plan_id)


def _advance_to_apply_constrained(
    service: PlanLifecycleService,
    plan_id: str,
) -> None:
    """Advance plan to Apply/constrained."""
    _advance_to_execute(service, plan_id)
    service.start_execute(plan_id)
    service.complete_execute(plan_id)
    plan = service.get_plan(plan_id)
    if plan.phase != PlanPhase.APPLY:
        service.apply_plan(plan_id)
    plan = service.get_plan(plan_id)
    if plan.processing_state == ProcessingState.QUEUED:
        service.start_apply(plan_id)
    plan = service.get_plan(plan_id)
    plan.processing_state = ProcessingState.CONSTRAINED
    plan.error_message = "Constraints too restrictive"


def _advance_to_applied(service: PlanLifecycleService, plan_id: str) -> None:
    """Advance plan to Applied terminal state."""
    _advance_to_execute(service, plan_id)
    service.start_execute(plan_id)
    service.complete_execute(plan_id)
    plan = service.get_plan(plan_id)
    if plan.phase != PlanPhase.APPLY:
        service.apply_plan(plan_id)
    plan = service.get_plan(plan_id)
    if plan.processing_state == ProcessingState.QUEUED:
        service.start_apply(plan_id)
    service.complete_apply(plan_id)


def test_manual_revert() -> None:
    """Test manual reversion from Execute to Strategize."""
    service = _make_service()
    plan_id = _create_plan(service)
    _advance_to_execute(service, plan_id)

    plan = service.revert_plan(plan_id, PlanPhase.STRATEGIZE, reason="test revert")
    assert plan.phase == PlanPhase.STRATEGIZE
    assert plan.processing_state == ProcessingState.QUEUED
    assert plan.reversion_count == 1
    assert len(plan.decisions) == 1
    assert plan.decisions[0]["type"] == "reversion"
    print("reversion-manual-ok")


def test_auto_revert_apply() -> None:
    """Test auto-reversion from constrained Apply."""
    service = _make_service()
    plan_id = _create_plan(service, profile="ci")
    _advance_to_apply_constrained(service, plan_id)

    plan = service.try_auto_revert_from_apply(plan_id, "constraints too strict")
    assert plan.phase == PlanPhase.STRATEGIZE
    assert plan.reversion_count == 1
    print("reversion-auto-apply-ok")


def test_loop_guard() -> None:
    """Test loop guard prevents more than 3 reversions."""
    service = _make_service()
    plan_id = _create_plan(service)
    _advance_to_execute(service, plan_id)

    plan = service.get_plan(plan_id)
    plan.reversion_count = 3

    try:
        service.revert_plan(plan_id, PlanPhase.STRATEGIZE, reason="should fail")
        print("ERROR: should have raised PlanError")
        sys.exit(1)
    except PlanError as e:
        assert "maximum reversion count" in str(e).lower() or "max" in str(e).lower()
        print("loop-guard-ok")


def test_profile_gate() -> None:
    """Test that manual profile blocks auto-reversion."""
    service = _make_service()
    plan_id = _create_plan(service, profile="manual")
    _advance_to_apply_constrained(service, plan_id)

    plan = service.try_auto_revert_from_apply(plan_id, "should stay")
    assert plan.phase == PlanPhase.APPLY
    assert plan.processing_state == ProcessingState.CONSTRAINED
    print("profile-gate-ok")


def test_terminal_block() -> None:
    """Test that applied terminal plan cannot be reverted."""
    service = _make_service()
    plan_id = _create_plan(service)
    _advance_to_applied(service, plan_id)

    try:
        service.revert_plan(plan_id, PlanPhase.STRATEGIZE, reason="should fail")
        print("ERROR: should have raised PlanError")
        sys.exit(1)
    except PlanError as e:
        assert "terminal" in str(e).lower()
        print("terminal-block-ok")


def test_context_preserved() -> None:
    """Test that plan context is preserved through reversion."""
    service = _make_service()
    service.create_action(
        name="local/ctx-test",
        description="Context test",
        definition_of_done="Tests pass",
        strategy_actor="local/strategist",
        execution_actor="local/executor",
        arguments=[
            ActionArgument(
                name="coverage",
                arg_type=ArgumentType.INTEGER,
                requirement=ArgumentRequirement.OPTIONAL,
                description="Coverage threshold",
            ),
        ],
    )
    plan = service.use_action(
        "local/ctx-test",
        project_links=[ProjectLink(project_name="local/my-project")],
        arguments={"coverage": 95},
        invariants=[PlanInvariant(text="No regressions", source=InvariantSource.PLAN)],
    )
    plan.automation_profile = AutomationProfileRef(
        profile_name="manual",
        provenance=AutomationProfileProvenance.PLAN,
    )
    plan_id = plan.identity.plan_id

    service.start_strategize(plan_id)
    service.complete_strategize(plan_id)
    service.execute_plan(plan_id)
    plan = service.revert_plan(plan_id, PlanPhase.STRATEGIZE, reason="context check")

    assert plan.arguments == {"coverage": 95}
    assert len(plan.project_links) == 1
    assert plan.project_links[0].project_name == "local/my-project"
    inv_texts = [inv.text for inv in plan.invariants]
    assert "No regressions" in inv_texts
    print("context-preserved-ok")


def test_decision_recording() -> None:
    """Test that each reversion is recorded as a decision."""
    service = _make_service()
    plan_id = _create_plan(service)
    _advance_to_execute(service, plan_id)

    plan = service.revert_plan(plan_id, PlanPhase.STRATEGIZE, reason="first")
    assert len(plan.decisions) == 1
    assert plan.decisions[0]["type"] == "reversion"
    assert plan.decisions[0]["source_phase"] == "execute"
    assert plan.decisions[0]["target_phase"] == "strategize"
    assert plan.decisions[0]["reversion_number"] == 1
    assert "timestamp" in plan.decisions[0]
    print("decision-recording-ok")


def test_can_revert_to() -> None:
    """Test can_revert_to validation."""
    service = _make_service()
    plan_id = _create_plan(service)
    plan = service.get_plan(plan_id)

    # Strategize cannot revert to Apply or Action
    plan.phase = PlanPhase.STRATEGIZE
    plan.processing_state = ProcessingState.QUEUED
    assert not plan.can_revert_to(PlanPhase.APPLY)
    assert not plan.can_revert_to(PlanPhase.ACTION)

    # Execute can revert to Strategize
    plan.phase = PlanPhase.EXECUTE
    assert plan.can_revert_to(PlanPhase.STRATEGIZE)

    # Apply can revert to Strategize
    plan.phase = PlanPhase.APPLY
    plan.processing_state = ProcessingState.CONSTRAINED
    assert plan.can_revert_to(PlanPhase.STRATEGIZE)

    # Applied cannot revert
    plan.processing_state = ProcessingState.APPLIED
    assert not plan.can_revert_to(PlanPhase.STRATEGIZE)

    # Max reversions reached blocks reversion — set processing_state
    # first so the phase-state validator sees QUEUED (valid in any phase).
    plan.processing_state = ProcessingState.QUEUED
    plan.phase = PlanPhase.EXECUTE
    plan.reversion_count = 3
    assert not plan.can_revert_to(PlanPhase.STRATEGIZE)

    print("can-revert-to-ok")


if __name__ == "__main__":
    cmd = sys.argv[1] if len(sys.argv) > 1 else "manual_revert"
    dispatch = {
        "manual_revert": test_manual_revert,
        "auto_revert_apply": test_auto_revert_apply,
        "loop_guard": test_loop_guard,
        "profile_gate": test_profile_gate,
        "terminal_block": test_terminal_block,
        "context_preserved": test_context_preserved,
        "decision_recording": test_decision_recording,
        "can_revert_to": test_can_revert_to,
    }
    fn = dispatch.get(cmd)
    if fn:
        fn()
    else:
        print(f"Unknown command: {cmd}", file=sys.stderr)
        sys.exit(1)
