"""Helper script for Strategy Actor Robot Framework integration tests.

Usage:
    python helper_strategy_actor.py stub-mode
    python helper_strategy_actor.py llm-json
    python helper_strategy_actor.py llm-fallback
    python helper_strategy_actor.py cycle-detection
    python helper_strategy_actor.py resolve-actor
    python helper_strategy_actor.py decision-conversion
    python helper_strategy_actor.py prompt-construction

Forgejo: #828
"""

from __future__ import annotations

import sys
from pathlib import Path

# Ensure source tree is importable
_SRC = str(Path(__file__).resolve().parents[1] / "src")
sys.path.insert(0, _SRC)

# Also ensure features/ is importable for mocks
_FEATURES = str(Path(__file__).resolve().parents[1])
sys.path.insert(0, _FEATURES)

from features.mocks.mock_strategy_llm import (  # noqa: E402
    STRATEGY_JSON_RESPONSE,
    make_failing_registry,
    make_mock_lifecycle,
    make_mock_registry,
)

from cleveragents.application.services.strategy_actor import (  # noqa: E402
    StrategyAction,
    StrategyActor,
    StrategyTree,
    build_strategy_prompt,
    resolve_strategy_actor,
    validate_no_cycles,
)
from cleveragents.core.exceptions import PlanError  # noqa: E402
from cleveragents.domain.models.core.decision import DecisionType  # noqa: E402

VALID_PLAN_ID = "01HX0000000000STRATEGY0001"


def test_stub_mode() -> None:
    """Test strategy actor in stub mode (no LLM)."""
    actor = StrategyActor()
    assert not actor.has_llm, "Expected no LLM capability"

    result = actor.execute(
        plan_id=VALID_PLAN_ID,
        definition_of_done="- Set up project\n- Implement feature\n- Write tests",
    )
    assert len(result.decisions) == 3, (
        f"Expected 3 decisions, got {len(result.decisions)}"
    )
    assert result.decision_root_id is not None
    assert "Set up project" in result.decisions[0].step_text
    print("strategy-actor-stub-ok")


def test_llm_json() -> None:
    """Test strategy actor with mock LLM returning JSON strategy."""
    registry = make_mock_registry(STRATEGY_JSON_RESPONSE)
    lifecycle = make_mock_lifecycle()
    actor = StrategyActor(
        provider_registry=registry,
        lifecycle_service=lifecycle,
    )
    assert actor.has_llm, "Expected LLM capability"

    result = actor.execute(
        plan_id=VALID_PLAN_ID,
        definition_of_done="Build a REST API with authentication",
    )
    assert len(result.decisions) == 5, (
        f"Expected 5 decisions, got {len(result.decisions)}"
    )
    assert "scaffolding" in result.decisions[0].step_text.lower()
    print("strategy-actor-llm-json-ok")


def test_llm_fallback() -> None:
    """Test strategy actor falls back to stub on LLM error."""
    registry = make_failing_registry()
    lifecycle = make_mock_lifecycle()
    actor = StrategyActor(
        provider_registry=registry,
        lifecycle_service=lifecycle,
    )

    result = actor.execute(
        plan_id=VALID_PLAN_ID,
        definition_of_done="- Step A\n- Step B",
    )
    assert len(result.decisions) == 2, (
        f"Expected 2 decisions (fallback), got {len(result.decisions)}"
    )
    print("strategy-actor-llm-fallback-ok")


def test_cycle_detection() -> None:
    """Test dependency graph cycle detection."""
    # Acyclic should pass
    assert validate_no_cycles([("A", "B"), ("B", "C")]) is True

    # Empty should pass
    assert validate_no_cycles([]) is True

    # Cyclic should raise PlanError
    try:
        validate_no_cycles([("A", "B"), ("B", "C"), ("C", "A")])
        print("FAIL: Expected PlanError for cyclic graph")
        sys.exit(1)
    except PlanError as exc:
        assert "Circular dependency" in str(exc)

    print("strategy-actor-cycle-detection-ok")


def test_resolve_actor() -> None:
    """Test resolve_strategy_actor integration function."""
    # With config=llm
    actor = resolve_strategy_actor(config_value="llm")
    assert isinstance(actor, StrategyActor), "Expected StrategyActor for llm config"

    # With config=stub
    actor = resolve_strategy_actor(config_value="stub")
    assert actor is None, "Expected None for stub config"

    # With registry
    registry = make_mock_registry(STRATEGY_JSON_RESPONSE)
    actor = resolve_strategy_actor(provider_registry=registry)
    assert isinstance(actor, StrategyActor), "Expected StrategyActor with registry"

    # No config, no registry
    actor = resolve_strategy_actor()
    assert actor is None, "Expected None with no config"

    print("strategy-actor-resolve-ok")


def test_decision_conversion() -> None:
    """Test conversion of strategy tree to Decision objects."""
    actor = StrategyActor()
    result = actor.execute(
        plan_id=VALID_PLAN_ID,
        definition_of_done="- Step 1\n- Step 2",
    )
    tree = StrategyTree(
        root_id=result.decision_root_id,
        actions=[
            StrategyAction(
                action_id=d.decision_id,
                description=d.step_text,
                sequence=d.sequence,
                parent_id=d.parent_id,
            )
            for d in result.decisions
        ],
        dependency_edges=[],
    )
    decisions = actor.build_decisions(plan_id=VALID_PLAN_ID, strategy_tree=tree)
    assert len(decisions) == 2
    assert decisions[0].decision_type == DecisionType.PROMPT_DEFINITION
    assert decisions[1].decision_type == DecisionType.STRATEGY_CHOICE
    print("strategy-actor-decision-conversion-ok")


def test_prompt_construction() -> None:
    """Test strategy prompt construction."""
    prompt = build_strategy_prompt(
        definition_of_done="Build API",
        resources=["source-code"],
        project_context="FastAPI",
        acms_context="Python 3.12",
    )
    assert "Build API" in prompt
    assert "source-code" in prompt
    assert "FastAPI" in prompt
    assert "Python 3.12" in prompt

    # Minimal prompt
    prompt_min = build_strategy_prompt(definition_of_done="Build API")
    assert "Build API" in prompt_min
    assert "<available_resources>" not in prompt_min

    print("strategy-actor-prompt-ok")


COMMANDS = {
    "stub-mode": test_stub_mode,
    "llm-json": test_llm_json,
    "llm-fallback": test_llm_fallback,
    "cycle-detection": test_cycle_detection,
    "resolve-actor": test_resolve_actor,
    "decision-conversion": test_decision_conversion,
    "prompt-construction": test_prompt_construction,
}


def main() -> None:
    """Entry point."""
    if len(sys.argv) < 2 or sys.argv[1] not in COMMANDS:
        print(f"Usage: {sys.argv[0]} <{'|'.join(COMMANDS.keys())}>")
        sys.exit(1)
    COMMANDS[sys.argv[1]]()


if __name__ == "__main__":
    main()
