"""Shared mock fixtures for TDD plan-correct JSON output envelope tests.

Provides constants, mock builders, and CLI argument helpers used by the
Behave step definitions
(``features/steps/tdd_plan_correct_json_output_steps.py``).

Centralising the mock builders ensures the test suite exercises the
``plan correct`` CLI JSON output path with identically-shaped mock objects.

Bug: https://git.cleverthis.com/cleveragents/cleveragents-core/issues/8584
"""

from __future__ import annotations

from datetime import datetime
from types import SimpleNamespace
from unittest.mock import MagicMock

from cleveragents.core.exceptions import ResourceNotFoundError
from cleveragents.domain.models.core.plan import (
    NamespacedName,
    Plan,
    PlanIdentity,
    PlanPhase,
    PlanTimestamps,
    ProcessingState,
    ProjectLink,
)

# ---------------------------------------------------------------------------
# Patch targets
# ---------------------------------------------------------------------------

PATCH_CONTAINER: str = "cleveragents.application.container.get_container"

# ---------------------------------------------------------------------------
# Fixed identifiers for deterministic assertions
# ---------------------------------------------------------------------------

DECISION_ID: str = "DEC-8584-TARGET"
ROOT_DECISION_ID: str = "DEC-8584-ROOT"
CORRECTION_ID: str = "CORR-TDD-8584"


# ---------------------------------------------------------------------------
# Mock builders
# ---------------------------------------------------------------------------


def make_decision_ns(
    decision_id: str,
    parent_decision_id: str | None,
) -> SimpleNamespace:
    """Create a minimal decision-like namespace for list_decisions."""
    return SimpleNamespace(
        decision_id=decision_id,
        parent_decision_id=parent_decision_id,
    )


def _make_plan() -> Plan:
    """Build a real ``Plan`` in Execute/COMPLETE state."""
    from ulid import ULID

    return Plan(
        identity=PlanIdentity(plan_id=str(ULID())),
        namespaced_name=NamespacedName(
            server=None, namespace="local", name="tdd-8584-plan"
        ),
        action_name="local/tdd-8584-action",
        description="TDD plan for bug #8584 — JSON output envelope",
        definition_of_done=None,
        phase=PlanPhase.EXECUTE,
        processing_state=ProcessingState.COMPLETE,
        project_links=[ProjectLink(project_name="proj-1")],
        strategy_actor="openai/gpt-4",
        execution_actor="openai/gpt-4",
        created_by=None,
        reusable=False,
        read_only=False,
        timestamps=PlanTimestamps(created_at=datetime.now(), updated_at=datetime.now()),
    )


def make_container(mode: str = "revert") -> MagicMock:
    """Build a mock DI container for the JSON output envelope test.

    Args:
        mode: The correction mode (``"revert"`` or ``"append"``).
    """
    plan = _make_plan()

    mock_plan_svc = MagicMock()
    # get_plan raises RNF for the decision_id (it is not a plan_id),
    # ensuring the code falls through to the decision_id path.
    mock_plan_svc.get_plan.side_effect = ResourceNotFoundError(
        resource_type="Plan",
        resource_id=DECISION_ID,
    )
    mock_plan_svc.list_plans.return_value = [plan]

    decisions = [
        make_decision_ns(ROOT_DECISION_ID, None),
        make_decision_ns(DECISION_ID, ROOT_DECISION_ID),
    ]

    mock_decision_svc = MagicMock()
    mock_decision_svc.list_decisions.return_value = decisions
    mock_decision_svc.get_influence_edges.return_value = {}

    mock_correction_svc = MagicMock()
    mock_correction_svc.request_correction.return_value = SimpleNamespace(
        correction_id=CORRECTION_ID,
        mode=SimpleNamespace(value=mode),
        target_decision_id=DECISION_ID,
        guidance="Recompute decision subtree",
    )
    mock_correction_svc.execute_correction.return_value = SimpleNamespace(
        correction_id=CORRECTION_ID,
        status=SimpleNamespace(value="applied"),
        reverted_decisions=[DECISION_ID],
        new_decisions=[],
    )

    mock_container = MagicMock()
    mock_container.plan_lifecycle_service.return_value = mock_plan_svc
    mock_container.decision_service.return_value = mock_decision_svc
    mock_container.correction_service.return_value = mock_correction_svc
    return mock_container


def build_cli_args(mode: str = "revert") -> list[str]:
    """Build the CLI argument list for ``plan correct <decision_id> --format json``.

    Args:
        mode: The correction mode (``"revert"`` or ``"append"``).
    """
    return [
        "correct",
        DECISION_ID,
        "--mode",
        mode,
        "--guidance",
        "Recompute decision subtree",
        "--yes",
        "--format",
        "json",
    ]


__all__: list[str] = [
    "CORRECTION_ID",
    "DECISION_ID",
    "PATCH_CONTAINER",
    "ROOT_DECISION_ID",
    "build_cli_args",
    "make_container",
    "make_decision_ns",
]
