"""Unit tests for the Python side of the ``report_outcome`` tool.

Under pi (SPEC §8.2) the tool itself is a TypeScript extension; the
Python contract is (a) normalizing the raw outcome string and (b)
reading the declared outcome back off pi's ``tool_execution_end``
event. We exercise both without booting pi.
"""

from __future__ import annotations

from typing import Any

from attractor.agent import (
    REPORT_OUTCOME_EXTENSION_PATH,
    REPORT_OUTCOME_TOOL_NAME,
    OutcomeStatus,
    ReportOutcomeState,
    normalize_outcome,
)
from attractor.agent.runner import (
    _record_report_outcome,  # pyright: ignore[reportPrivateUsage]
)


# [unit->REQ-AGENT-TOOLS-V01]
def test_tool_name_and_extension_path() -> None:
    """The runner loads the extension by path; the tool name is stable."""
    assert REPORT_OUTCOME_TOOL_NAME == "report_outcome"
    assert REPORT_OUTCOME_EXTENSION_PATH.name == "report_outcome.ts"
    assert REPORT_OUTCOME_EXTENSION_PATH.is_file(), (
        "report_outcome.ts must ship inside the package so the runner can "
        "resolve it via --extension"
    )


# [unit->REQ-AGENT-TOOLS-V01]
def test_normalize_outcome_accepts_canonical_and_variants() -> None:
    assert normalize_outcome("SUCCESS") == OutcomeStatus.SUCCESS
    assert normalize_outcome("success") == OutcomeStatus.SUCCESS
    assert normalize_outcome("FAILURE") == OutcomeStatus.FAILURE
    assert normalize_outcome("failure") == OutcomeStatus.FAILURE


# [unit->REQ-EXEC-OUTCOME-NO-OP]
def test_normalize_outcome_handles_no_op_variants() -> None:
    assert normalize_outcome("NO_OP") == OutcomeStatus.NO_OP
    assert normalize_outcome("NO-OP") == OutcomeStatus.NO_OP
    assert normalize_outcome("noop") == OutcomeStatus.NO_OP


# [unit->REQ-AGENT-TOOLS-V01]
def test_normalize_outcome_rejects_unknown() -> None:
    assert normalize_outcome("MAYBE") is None
    assert normalize_outcome("") is None


def _end_event(**details: Any) -> dict[str, Any]:
    """Build a pi ``tool_execution_end`` event for report_outcome."""
    return {
        "type": "tool_execution_end",
        "toolName": REPORT_OUTCOME_TOOL_NAME,
        "result": {"content": [{"type": "text", "text": "ok"}], "details": details},
        "isError": False,
    }


# [unit->REQ-AGENT-TOOLS-V01]
def test_record_report_outcome_reads_normalized_details() -> None:
    """The runner trusts the extension's normalized details.status."""
    state = ReportOutcomeState()
    _record_report_outcome(
        _end_event(status="FAILURE", reason="3 tests failed"), state
    )
    assert state.invoked is True
    assert state.status == OutcomeStatus.FAILURE
    assert state.success is False
    assert state.reason == "3 tests failed"


# [unit->REQ-EXEC-OUTCOME-NO-OP]
def test_record_report_outcome_success_and_no_op() -> None:
    success = ReportOutcomeState()
    _record_report_outcome(_end_event(status="SUCCESS", reason="all green"), success)
    assert success.status == OutcomeStatus.SUCCESS
    assert success.success is True

    no_op = ReportOutcomeState()
    _record_report_outcome(_end_event(status="NO_OP", reason="already fixed"), no_op)
    assert no_op.status == OutcomeStatus.NO_OP
    assert no_op.success is False
    assert no_op.reason == "already fixed"


# [unit->REQ-AGENT-TOOLS-V01]
def test_record_report_outcome_ignores_missing_or_bad_details() -> None:
    """No details.status → state stays uninvoked (runner falls through)."""
    state = ReportOutcomeState()
    _record_report_outcome(
        {"type": "tool_execution_end", "toolName": REPORT_OUTCOME_TOOL_NAME, "result": {}},
        state,
    )
    assert state.invoked is False
