"""Tests for spawn result persistence (anima collect).

Spec source: vectl step spawn-result-persistence.auto-write-result.
Purpose: Ensure run_agent_sync persists AgentResult for all terminal states
(completed, killed) so anima collect works for spawn-completed agents.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any

import pytest
from returns.result import Success

from anima.core.lifecycle import LifecycleEvent
from anima.core.schemas import AgentResult, AgentState, JobSpec, LifecycleType
from anima.shell.agent_sync import run_agent_sync
from anima.shell.state import (
    create_agent_state,
    read_result,
    read_state,
    transition_state,
)


def test_run_agent_sync_success_path_writes_result_json(
    tmp_state_dir: Any, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Success path (completed or killed) writes result.json for anima collect."""
    _ = tmp_state_dir
    instance_id = "inst_sync_result_write"
    job = JobSpec(
        persona_ref="test@test",
        instance_id=instance_id,
        lifecycle=LifecycleType.ephemeral,
    )
    created = create_agent_state(instance_id, "test-persona", job)
    assert isinstance(created, Success)

    started = transition_state(instance_id, LifecycleEvent.start)
    assert isinstance(started, Success)

    # Mock AgentRunner.run() to return a Success result
    async def _mock_run_completed(self: Any) -> Success[AgentResult]:
        return Success(AgentResult(final_state=AgentState.completed, token_usage=None))

    from anima.shell.agent import AgentRunner

    monkeypatch.setattr(AgentRunner, "run", _mock_run_completed)

    exit_code = run_agent_sync(instance_id, "test task")

    assert exit_code == 0

    # Verify result.json exists and contains correct data
    result_file = Path(tmp_state_dir) / "agents" / instance_id / "result.json"
    assert result_file.exists(), "result.json must exist for spawn-completed agents"

    read_result_outcome = read_result(instance_id)
    assert isinstance(read_result_outcome, Success)
    agent_result = read_result_outcome.unwrap()
    assert agent_result.final_state == AgentState.completed


def test_run_agent_sync_killed_path_writes_result_json(
    tmp_state_dir: Any, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Killed state (interrupt) writes result.json for anima collect."""
    _ = tmp_state_dir
    instance_id = "inst_sync_killed_result"
    job = JobSpec(
        persona_ref="test@test",
        instance_id=instance_id,
        lifecycle=LifecycleType.ephemeral,
    )
    created = create_agent_state(instance_id, "test-persona", job)
    assert isinstance(created, Success)

    started = transition_state(instance_id, LifecycleEvent.start)
    assert isinstance(started, Success)

    # Mock AgentRunner.run() to return killed result
    async def _mock_run_killed(self: Any) -> Success[AgentResult]:
        return Success(AgentResult(final_state=AgentState.killed, token_usage=None))

    from anima.shell.agent import AgentRunner

    monkeypatch.setattr(AgentRunner, "run", _mock_run_killed)

    exit_code = run_agent_sync(instance_id, "test task")

    assert exit_code == 130  # SIGINT exit code

    # Verify result.json exists
    result_file = Path(tmp_state_dir) / "agents" / instance_id / "result.json"
    assert result_file.exists(), "result.json must exist for killed agents"

    read_result_outcome = read_result(instance_id)
    assert isinstance(read_result_outcome, Success)
    agent_result = read_result_outcome.unwrap()
    assert agent_result.final_state == AgentState.killed


def test_run_agent_sync_failure_path_does_not_write_result_json(
    tmp_state_dir: Any, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Failure path transitions state but does NOT write result.json.

    Result persistence only happens for successful agent completion.
    Failed agents write result via ctl_finish path (not run_agent_sync).
    """
    _ = tmp_state_dir
    instance_id = "inst_sync_fail_result"
    job = JobSpec(
        persona_ref="test@test",
        instance_id=instance_id,
        lifecycle=LifecycleType.ephemeral,
    )
    created = create_agent_state(instance_id, "test-persona", job)
    assert isinstance(created, Success)

    started = transition_state(instance_id, LifecycleEvent.start)
    assert isinstance(started, Success)

    # Mock AgentRunner.run() to return Failure
    from returns.result import Failure
    from anima.core.schemas import InternalError

    async def _mock_run_failure(self: Any) -> Failure[Any]:
        return Failure(InternalError("test failure"))

    from anima.shell.agent import AgentRunner

    monkeypatch.setattr(AgentRunner, "run", _mock_run_failure)

    exit_code = run_agent_sync(instance_id, "test task")

    assert exit_code == 1

    # Verify result.json does NOT exist for failure path
    result_file = Path(tmp_state_dir) / "agents" / instance_id / "result.json"
    assert not result_file.exists(), "result.json must not exist for failed agents"

    # Verify state transitioned to failed
    state_result = read_state(instance_id)
    assert isinstance(state_result, Success)
    assert state_result.unwrap().state == AgentState.failed
