"""Child-agent management tools: poll, cancel, child_logs.

Non-blocking tools for inspecting and controlling spawned subagents.
All tools validate that ``child_id`` belongs to the calling parent's
``AgentStateData.children`` list before proceeding.

Spec source: ``docs/proposals/subagent-coordination.md`` (Phase 2: Core Tools).
"""

import json

from deal import pre, post
from returns.result import Failure, Result, Success

from anima.core.schemas import (
    AgentStateData,
    AnimaError,
    InvalidInputError,
)
from anima.shell.agent_kill import kill_agent_tree
from anima.shell.state import read_log, read_state


def _validate_child_id(
    instance_id: str,
    child_id: str,
) -> Result[AgentStateData, AnimaError]:
    """Validate that child_id belongs to the parent's children list.

    Reads the parent's state and checks that ``child_id`` is present in
    ``AgentStateData.children``. Returns the parent state on success for
    downstream use, or a descriptive error envelope on failure.

    Args:
        instance_id: The parent agent's instance ID.
        child_id: The child agent's instance ID to validate.

    Returns:
        Success(parent_state_data) if child_id is valid,
        Failure(AnimaError) with descriptive error if not.

    >>> # Validation rejects child_ids not in parent's children list
    >>> # (requires live agent state — see integration tests)
    """
    parent_result = read_state(instance_id)
    if isinstance(parent_result, Failure):
        return parent_result

    parent_state = parent_result.unwrap()
    if child_id not in parent_state.children:
        return Failure(
            InvalidInputError(
                f"child_id '{child_id}' was not spawned by this agent. "
                f"Your children: {parent_state.children}.",
                {"child_id": child_id, "parent_children": parent_state.children},
            )
        )

    return Success(parent_state)


# @shell_complexity: Non-blocking state read with child validation
@pre(
    lambda instance_id, child_id: (
        isinstance(instance_id, str)
        and len(instance_id.strip()) > 0
        and isinstance(child_id, str)
        and len(child_id.strip()) > 0
    )
)
@post(lambda result: isinstance(result, (Success, Failure)))
async def ctl_poll(
    instance_id: str,
    child_id: str,
) -> Result[str, AnimaError]:
    """Check a child's current status without waiting.

    Non-blocking status check. Does NOT return response_text — use
    ``ctl_await`` for that. Reads the child's ``state.json`` and returns
    current status, turn count, and error information.

    Args:
        instance_id: The parent agent's instance ID (injected by tool factory).
        child_id: The child agent's instance ID to poll.

    Returns:
        Success with JSON string envelope::

            {"child_id": "...", "status": "running", "current_turn": 3,
             "error": null, "summary": "Child ...: running, turn 3."}

        Failure with AnimaError on validation or read errors.

    >>> # ctl_poll returns status envelope for a valid child
    >>> # (requires live agent state — see integration tests)
    """
    validation = _validate_child_id(instance_id, child_id)
    if isinstance(validation, Failure):
        return validation

    child_result = read_state(child_id)
    if isinstance(child_result, Failure):
        return child_result

    child_state = child_result.unwrap()
    status = child_state.state.value
    turn = child_state.current_turn
    error = child_state.error

    envelope = {
        "child_id": child_id,
        "status": status,
        "current_turn": turn,
        "error": error,
        "summary": f"Child {child_id}: {status}, turn {turn}.",
    }
    return Success(json.dumps(envelope))


# @shell_complexity: Process kill orchestration with child validation
@pre(
    lambda instance_id, child_id: (
        isinstance(instance_id, str)
        and len(instance_id.strip()) > 0
        and isinstance(child_id, str)
        and len(child_id.strip()) > 0
    )
)
@post(lambda result: isinstance(result, (Success, Failure)))
async def ctl_cancel(
    instance_id: str,
    child_id: str,
) -> Result[str, AnimaError]:
    """Terminate a child agent by killing its process tree.

    Validates ``child_id`` belongs to the parent, then kills the child
    agent process tree using ``kill_agent_tree`` from ``agent_kill.py``.

    Args:
        instance_id: The parent agent's instance ID (injected by tool factory).
        child_id: The child agent's instance ID to cancel.

    Returns:
        Success with JSON string envelope::

            {"child_id": "...", "status": "cancelled",
             "summary": "Child ... cancelled."}

        Failure with AnimaError on validation or kill errors.

    >>> # ctl_cancel kills child process and returns confirmation
    >>> # (requires live agent state — see integration tests)
    """
    validation = _validate_child_id(instance_id, child_id)
    if isinstance(validation, Failure):
        return validation

    kill_result = kill_agent_tree(child_id, error="Cancelled by parent agent")
    if isinstance(kill_result, Failure):
        return kill_result

    envelope = {
        "child_id": child_id,
        "status": "cancelled",
        "summary": f"Child {child_id} cancelled.",
    }
    return Success(json.dumps(envelope))


# @shell_complexity: Log read with filtering and child validation
@pre(
    lambda instance_id, child_id, last_n=10: (
        isinstance(instance_id, str)
        and len(instance_id.strip()) > 0
        and isinstance(child_id, str)
        and len(child_id.strip()) > 0
        and (last_n is None or (isinstance(last_n, int) and last_n > 0))
    )
)
@post(lambda result: isinstance(result, (Success, Failure)))
async def ctl_child_logs(
    instance_id: str,
    child_id: str,
    last_n: int | None = 10,
) -> Result[str, AnimaError]:
    """Read a child agent's conversation log for debugging.

    Reads the child's ``log.jsonl``, filters to conversation events
    (``user_message``, ``response``, ``tool_call``, ``error``), and
    returns the last ``last_n`` events. Infrastructure events like
    ``created`` and ``state_transition`` are excluded.

    Args:
        instance_id: The parent agent's instance ID (injected by tool factory).
        child_id: The child agent's instance ID whose logs to read.
        last_n: Maximum number of conversation events to return.
            Defaults to 10 to limit context consumption. Pass ``None``
            to return all events (use with caution).

    Returns:
        Success with JSON string envelope::

            {"child_id": "...", "total_events": 47, "returned_events": 10,
             "events": [...], "summary": "Last 10 of 47 events. ..."}

        Failure with AnimaError on validation or read errors.

    >>> # ctl_child_logs returns filtered conversation events
    >>> # (requires live agent state — see integration tests)
    """
    validation = _validate_child_id(instance_id, child_id)
    if isinstance(validation, Failure):
        return validation

    log_result = read_log(child_id)
    if isinstance(log_result, Failure):
        return log_result

    _CONVERSATION_EVENTS = {
        "user_message", "response", "tool_call", "error", "interact_error",
    }

    all_entries = log_result.unwrap()
    conversation = [
        entry for entry in all_entries
        if entry.get("event") in _CONVERSATION_EVENTS
    ]

    total_events = len(conversation)
    if last_n is not None:
        returned = conversation[-last_n:]
    else:
        returned = conversation

    events_out: list[dict] = []
    for entry in returned:
        details = entry.get("details") or {}
        evt: dict = {
            "event": entry.get("event", "unknown"),
            "turn": details.get("turn_number") or details.get("turn"),
            "text": details.get("message_text") or details.get("response_text") or details.get("error") or details.get("text"),
        }
        if entry.get("event") == "tool_call":
            evt["tool"] = details.get("tool_name") or details.get("tool")
        events_out.append(evt)

    envelope = {
        "child_id": child_id,
        "total_events": total_events,
        "returned_events": len(returned),
        "events": events_out,
        "summary": f"Last {len(returned)} of {total_events} events.",
    }
    return Success(json.dumps(envelope))
