"""Reproduction: spec-compliance.verify-meta-and-interact.

Expected: MCP responses include `_meta` with `trace_id`, `instance_id`, and
`agent_id`; `interact()` round-trips promptly and timeout paths return errors.
Actual:   Some error responses omit `_meta`; unresolved `interact()` calls can
hang instead of returning a timeout error.
"""

from __future__ import annotations

import asyncio
import os
import tempfile
from dataclasses import dataclass

from returns.result import Failure

from anima.core.schemas import JobSpec
from anima.shell.mcp_agent import AgentMCPServer
from anima.shell.mcp_serve import ServeMCPServer
from anima.shell.state import create_agent_state, delete_agent_state


REQUIRED_META_KEYS = ("trace_id", "instance_id", "agent_id")


@dataclass
class FakeAgentRunner:
    """Minimal AgentRunnerProtocol implementation for integration tests.

    Seam-pure integration path: no MagicMock, real protocol compliance.
    """

    instance_id: str


def _assert_required_meta(payload: dict, label: str) -> list[str]:
    failures: list[str] = []
    meta = payload.get("_meta")
    if not isinstance(meta, dict):
        return [f"{label}: missing _meta"]

    for key in REQUIRED_META_KEYS:
        if key not in meta:
            failures.append(f"{label}: _meta missing {key}")
    return failures


async def _run_repro() -> None:
    failures: list[str] = []
    state_agent_id: str | None = None

    with tempfile.TemporaryDirectory(prefix="anima_repro_meta_") as state_dir:
        previous_anima_state = os.environ.get("ANIMA_STATE")
        os.environ["ANIMA_STATE"] = state_dir

        create_result = create_agent_state(
            "inst_spec_compliance_verify",
            "tester",
            JobSpec(persona_ref="test@v1", instance_id="inst_spec_compliance_verify"),
        )
        if isinstance(create_result, Failure):
            raise AssertionError(
                f"Failed to create agent state: {create_result.failure()}"
            )

        state = create_result.unwrap()
        state_agent_id = state.agent_id
        # Seam-pure integration: FakeAgentRunner implements AgentRunnerProtocol.
        agent_server = AgentMCPServer(
            state.agent_id,
            FakeAgentRunner(instance_id=state.instance_id),
            # Use short timeout for timeout test (< 2s)
            interact_timeout_seconds=1.0,
        )
        serve_server = ServeMCPServer(host="127.0.0.1", port=8450)

        try:
            # Happy path: status response includes required _meta fields.
            status_payload = await agent_server._handle_status()
            failures.extend(
                _assert_required_meta(status_payload, "agent.status(success)")
            )

            # Error path: response should still include required _meta fields.
            missing_agent_status = await serve_server._handle_status(
                "ag_nonexistent_meta"
            )
            failures.extend(
                _assert_required_meta(missing_agent_status, "serve.status(error)")
            )

            # Edge path: interact round-trip should return quickly.
            interact_task = asyncio.create_task(agent_server._handle_interact("hello"))
            await asyncio.sleep(0.05)
            pending = agent_server.get_pending_message()
            if pending is None:
                failures.append(
                    "agent.interact(roundtrip): pending queue was not created"
                )
            else:
                queued = await asyncio.wait_for(pending.get(), timeout=1.0)
                if queued != "hello":
                    failures.append(
                        f"agent.interact(roundtrip): queued message mismatch {queued!r}"
                    )

            agent_server.set_response("world", turn_number=1)
            interact_payload = await asyncio.wait_for(interact_task, timeout=2.0)
            if interact_payload.get("response") != "world":
                failures.append(
                    "agent.interact(roundtrip): response payload mismatch "
                    f"{interact_payload!r}"
                )
            failures.extend(
                _assert_required_meta(interact_payload, "agent.interact(success)")
            )

            # Error path: unresolved interact should return timeout error, not hang.
            unresolved_task = asyncio.create_task(
                agent_server._handle_interact("timeout-check")
            )
            try:
                unresolved_payload = await asyncio.wait_for(
                    unresolved_task, timeout=2.0
                )
                if "error" not in unresolved_payload:
                    failures.append(
                        "agent.interact(timeout): expected timeout error payload, got "
                        f"{unresolved_payload!r}"
                    )
            except asyncio.TimeoutError:
                failures.append(
                    "agent.interact(timeout): call hangs >2s instead of returning timeout error"
                )
                unresolved_task.cancel()
                try:
                    await unresolved_task
                except BaseException:
                    pass
        finally:
            if state_agent_id is not None:
                delete_agent_state(state_agent_id)
            if previous_anima_state is None:
                os.environ.pop("ANIMA_STATE", None)
            else:
                os.environ["ANIMA_STATE"] = previous_anima_state

    assert not failures, "\n".join(failures)


def repro_issue_spec_compliance_verify_meta_and_interact() -> None:
    """MCP metadata and interact semantics match spec expectations."""
    asyncio.run(_run_repro())


if __name__ == "__main__":
    repro_issue_spec_compliance_verify_meta_and_interact()
    print("PASS: spec-compliance.verify-meta-and-interact is fixed")
