"""Mock LLM provider for Strategy Actor BDD tests.

Provides deterministic LLM responses for testing the StrategyActor
without requiring real AI credentials.  All mocks live in ``features/``
per ADR-022 (no mocks in production code).

Forgejo: #828
"""

from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import MagicMock

# ---------------------------------------------------------------------------
# Canned LLM responses
# ---------------------------------------------------------------------------

STRATEGY_JSON_RESPONSE = """[
  {
    "step": 1,
    "description": "Set up project scaffolding and configuration",
    "resource_requirements": ["project-config", "build-system"],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": []
  },
  {
    "step": 2,
    "description": "Implement core domain models",
    "resource_requirements": ["source-code", "database-schema"],
    "estimated_complexity": "high",
    "risk_score": 0.5,
    "depends_on": [1]
  },
  {
    "step": 3,
    "description": "Add unit tests for domain models",
    "resource_requirements": ["test-framework"],
    "estimated_complexity": "medium",
    "risk_score": 0.2,
    "depends_on": [2]
  },
  {
    "step": 4,
    "description": "Integrate API endpoints",
    "resource_requirements": ["source-code", "api-framework"],
    "estimated_complexity": "medium",
    "risk_score": 0.4,
    "depends_on": [2]
  },
  {
    "step": 5,
    "description": "Run integration tests and validate",
    "resource_requirements": ["test-framework", "ci-pipeline"],
    "estimated_complexity": "medium",
    "risk_score": 0.3,
    "depends_on": [3, 4]
  }
]"""

STRATEGY_NUMBERED_RESPONSE = (
    "1. Set up project scaffolding\n"
    "2. Implement core models\n"
    "3. Add unit tests\n"
    "4. Integrate API endpoints\n"
    "5. Run integration tests"
)

STRATEGY_EMPTY_RESPONSE = ""

STRATEGY_NON_JSON_RESPONSE = (
    "Here is my strategy:\n"
    "1. First we should set up the project\n"
    "2. Then implement the features\n"
    "3. Finally run the tests"
)

STRATEGY_CYCLIC_DEPS_RESPONSE = """[
  {
    "step": 1,
    "description": "Step A depends on C",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": [3]
  },
  {
    "step": 2,
    "description": "Step B depends on A",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": [1]
  },
  {
    "step": 3,
    "description": "Step C depends on B",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": [2]
  }
]"""

STRATEGY_PARTIAL_JSON_RESPONSE = """[
  {
    "step": 1,
    "description": "Valid action with missing fields"
  },
  {
    "step": 2,
    "description": "Another action",
    "estimated_complexity": "invalid_value",
    "risk_score": "not_a_number"
  }
]"""

STRATEGY_RISK_CLAMP_RESPONSE = """[
  {
    "step": 1,
    "description": "Action with excessive risk score",
    "resource_requirements": [],
    "estimated_complexity": "high",
    "risk_score": 5.0,
    "depends_on": []
  }
]"""

STRATEGY_SELF_DEP_RESPONSE = """[
  {
    "step": 1,
    "description": "Step that depends on itself",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": [1]
  },
  {
    "step": 2,
    "description": "Normal step",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": []
  }
]"""

STRATEGY_DUPLICATE_STEP_RESPONSE = """[
  {
    "step": 3,
    "description": "Step with explicit step number 3",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": []
  },
  {
    "step": 2,
    "description": "Normal step 2",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": []
  },
  {
    "description": "Step without step field at index 2 (fallback 3)",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": []
  }
]"""

STRATEGY_NEGATIVE_RISK_RESPONSE = """[
  {
    "step": 1,
    "description": "Action with negative risk score",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": -0.5,
    "depends_on": []
  }
]"""

STRATEGY_NON_SEQUENTIAL_STEPS_RESPONSE = """[
  {
    "step": 10,
    "description": "First action with step 10",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": []
  },
  {
    "step": 20,
    "description": "Second action with step 20",
    "resource_requirements": [],
    "estimated_complexity": "medium",
    "risk_score": 0.3,
    "depends_on": [10]
  },
  {
    "step": 30,
    "description": "Third action with step 30",
    "resource_requirements": [],
    "estimated_complexity": "high",
    "risk_score": 0.5,
    "depends_on": [20]
  }
]"""

STRATEGY_NULL_DESCRIPTION_RESPONSE = """[
  {
    "step": 1,
    "description": null,
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.1,
    "depends_on": []
  },
  {
    "step": 2,
    "description": "Valid action",
    "resource_requirements": [],
    "estimated_complexity": "medium",
    "risk_score": 0.3,
    "depends_on": []
  }
]"""

STRATEGY_TRAILING_BRACKETS_RESPONSE = (
    '[{"step": 1, "description": "Setup project", "resource_requirements": [],'
    ' "estimated_complexity": "low", "risk_score": 0.1, "depends_on": []},'
    ' {"step": 2, "description": "Implement feature", "resource_requirements": [],'
    ' "estimated_complexity": "medium", "risk_score": 0.3, "depends_on": [1]}]'
    " See [this guide] for details."
)

STRATEGY_NAN_RISK_RESPONSE = """[
  {
    "step": 1,
    "description": "Action with NaN risk score",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": "NaN",
    "depends_on": []
  }
]"""

STRATEGY_INF_RISK_RESPONSE = """[
  {
    "step": 1,
    "description": "Action with Inf risk score",
    "resource_requirements": [],
    "estimated_complexity": "medium",
    "risk_score": "Infinity",
    "depends_on": []
  }
]"""

STRATEGY_NON_DICT_ITEMS_RESPONSE = """[
  "this is a plain string",
  42,
  {
    "step": 1,
    "description": "Valid action among non-dict items",
    "resource_requirements": [],
    "estimated_complexity": "low",
    "risk_score": 0.2,
    "depends_on": []
  },
  null
]"""

STRATEGY_BULLET_MARKERS_RESPONSE = (
    "* Set up project structure\n"
    "\u2022 Configure build system\n"
    "- Implement core feature\n"
    "Regular text that should be skipped\n"
    "* Write tests"
)


# ---------------------------------------------------------------------------
# Mock factories
# ---------------------------------------------------------------------------


def make_mock_llm_response(content: str) -> SimpleNamespace:
    """Create a mock LLM response with ``.content``."""
    return SimpleNamespace(content=content)


def make_mock_llm(response_content: str) -> MagicMock:
    """Create a mock LLM that returns a canned response."""
    mock_llm = MagicMock()
    mock_llm.invoke.return_value = make_mock_llm_response(response_content)
    return mock_llm


def make_mock_registry(response_content: str) -> SimpleNamespace:
    """Create a mock ProviderRegistry returning a mock LLM."""
    mock_llm = make_mock_llm(response_content)
    return SimpleNamespace(
        create_llm=MagicMock(return_value=mock_llm),
    )


def make_mock_lifecycle(
    strategy_actor: str | None = "openai/gpt-4",
) -> SimpleNamespace:
    """Create a mock lifecycle service."""
    plan = SimpleNamespace(action_name="test-action")
    action = SimpleNamespace(strategy_actor=strategy_actor)
    return SimpleNamespace(
        get_plan=MagicMock(return_value=plan),
        get_action=MagicMock(return_value=action),
    )


def make_mock_acms_pipeline(
    context_summary: str = "Project uses Python 3.12, FastAPI, SQLAlchemy",
) -> SimpleNamespace:
    """Create a mock ACMS pipeline."""
    return SimpleNamespace(
        get_context_summary=MagicMock(return_value=context_summary),
    )


def make_failing_acms_pipeline() -> SimpleNamespace:
    """Create a mock ACMS pipeline that raises on get_context_summary."""
    return SimpleNamespace(
        get_context_summary=MagicMock(side_effect=RuntimeError("ACMS unavailable")),
    )


def make_oversized_actions_response(count: int) -> str:
    """Generate a JSON array with *count* strategy actions."""
    import json as _json

    actions = [
        {
            "step": i + 1,
            "description": f"Action number {i + 1}",
            "resource_requirements": [],
            "estimated_complexity": "low",
            "risk_score": 0.1,
            "depends_on": [],
        }
        for i in range(count)
    ]
    return _json.dumps(actions)


def make_failing_registry() -> SimpleNamespace:
    """Create a mock registry whose LLM raises on invoke."""
    mock_llm = MagicMock()
    mock_llm.invoke.side_effect = RuntimeError("LLM provider error")
    return SimpleNamespace(
        create_llm=MagicMock(return_value=mock_llm),
    )
