"""Targeted tests for MCP approve/reject/resume/logs handlers."""

from __future__ import annotations

import asyncio
import json
from pathlib import Path
from types import SimpleNamespace

import pytest
from returns.result import Failure, Success

from anima.shell.mcp_agent import AgentMCPServer


def _state_with(value: str) -> SimpleNamespace:
    return SimpleNamespace(state=SimpleNamespace(value=value))


@pytest.mark.asyncio
async def test_approve_returns_acknowledged_and_instance_id(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """approve acknowledges and signals approval event on success."""
    import anima.shell.state as state_module

    calls: list[tuple[str, object]] = []

    class _Runner:
        def __init__(self) -> None:
            self._approval_event = asyncio.Event()

        def resolve_interactive_approval_future(self, decision: object) -> None:
            calls.append(("resolve", decision))

    runner = _Runner()
    server = AgentMCPServer("inst_approve_ok", runner)

    monkeypatch.setattr(
        state_module,
        "read_state",
        lambda _instance_id: Success(_state_with("awaiting_approval")),
    )
    monkeypatch.setattr(
        state_module,
        "write_approval_response",
        lambda instance_id, approved, note: (
            calls.append(("write", (instance_id, approved, note))) or Success(None)
        ),
    )
    monkeypatch.setattr(
        state_module,
        "transition_state",
        lambda instance_id, event, error=None, result_ref=None: (
            calls.append(("transition", (instance_id, event, error, result_ref)))
            or Success(_state_with("running"))
        ),
    )

    payload = await server._handle_approve(note="lgtm")

    assert payload == {"instance_id": "inst_approve_ok", "acknowledged": True}
    assert runner._approval_event.is_set() is True
    assert [name for name, _ in calls] == ["write", "transition", "resolve"]


@pytest.mark.asyncio
async def test_approve_surfaces_transition_failure(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """approve returns STATE_TRANSITION_FAILED when transition_state fails."""
    import anima.shell.state as state_module

    runner = SimpleNamespace(_approval_event=asyncio.Event())
    server = AgentMCPServer("inst_approve_fail", runner)

    monkeypatch.setattr(
        state_module,
        "read_state",
        lambda _instance_id: Success(_state_with("awaiting_approval")),
    )
    monkeypatch.setattr(
        state_module,
        "write_approval_response",
        lambda _instance_id, approved, note: Success(None),
    )
    monkeypatch.setattr(
        state_module,
        "transition_state",
        lambda _instance_id, event, error=None, result_ref=None: Failure("boom"),
    )

    payload = await server._handle_approve(note="ok")

    assert payload["error"]["code"] == "STATE_TRANSITION_FAILED"
    assert payload["error"]["numeric_code"] == 500
    assert runner._approval_event.is_set() is False


@pytest.mark.asyncio
async def test_reject_reason_optional_and_acknowledged(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """reject accepts no reason and returns acknowledged payload."""
    import anima.shell.state as state_module

    calls: list[tuple[str, object]] = []

    class _Runner:
        def __init__(self) -> None:
            self._approval_event = asyncio.Event()

        def resolve_interactive_approval_future(self, decision: object) -> None:
            calls.append(("resolve", decision))

    runner = _Runner()
    server = AgentMCPServer("inst_reject_ok", runner)

    captured: dict[str, object] = {}

    monkeypatch.setattr(
        state_module,
        "read_state",
        lambda _instance_id: Success(_state_with("awaiting_approval")),
    )
    monkeypatch.setattr(
        state_module,
        "write_approval_response",
        lambda instance_id, approved, note: (
            captured.update(
                {"instance_id": instance_id, "approved": approved, "note": note}
            )
            or Success(None)
        ),
    )
    monkeypatch.setattr(
        state_module,
        "transition_state",
        lambda _instance_id, event, error=None, result_ref=None: Success(
            _state_with("running")
        ),
    )

    payload = await server._handle_reject()

    assert payload == {"instance_id": "inst_reject_ok", "acknowledged": True}
    assert captured["approved"] is False
    assert captured["note"] is None
    assert runner._approval_event.is_set() is True
    assert [name for name, _ in calls] == ["resolve"]


@pytest.mark.asyncio
async def test_resume_returns_acknowledged_and_instance_id(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """resume acknowledges suspended->running transition."""
    import anima.shell.state as state_module

    runner = SimpleNamespace(_resume_event=asyncio.Event())
    server = AgentMCPServer("inst_resume_ok", runner)

    monkeypatch.setattr(
        state_module,
        "read_state",
        lambda _instance_id: Success(_state_with("suspended")),
    )
    monkeypatch.setattr(
        state_module,
        "transition_state",
        lambda _instance_id, event, error=None, result_ref=None: Success(
            _state_with("running")
        ),
    )

    payload = await server._handle_resume()

    assert payload == {"instance_id": "inst_resume_ok", "acknowledged": True}
    assert runner._resume_event.is_set() is True


@pytest.mark.asyncio
async def test_logs_returns_entries_and_count(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """logs returns entries key and count matching parsed lines."""
    import anima.shell.state as state_module

    instance_id = "inst_logs_shape"
    agent_dir = tmp_path / "agents" / instance_id
    agent_dir.mkdir(parents=True, exist_ok=True)
    (agent_dir / "log.jsonl").write_text(
        "\n".join(
            [
                json.dumps({"event": "start"}),
                json.dumps({"event": "done"}),
            ]
        )
    )

    monkeypatch.setattr(state_module, "get_agent_dir", lambda _instance_id: agent_dir)
    server = AgentMCPServer(instance_id, SimpleNamespace())

    payload = await server._handle_logs()

    assert payload["instance_id"] == instance_id
    assert payload["count"] == 2
    assert [entry["event"] for entry in payload["entries"]] == ["start", "done"]
