"""Shell adapters for app.control_service ports.

Spec source:
- docs/proposals/execution-architecture-refactor.md#repository-target-architecture/app
- docs/proposals/execution-architecture-refactor.md#refactor-program/wave-2-launch-and-control-unification
"""

from __future__ import annotations

from returns.result import Failure, Result, Success

from anima.app.control_service import (
    ApprovalPort,
    ContractStatePort,
    ControlTransportPort,
    DefaultControlService,
    LifecycleControlPort,
)
from anima.core.execution import ExecutionContractResolver, resolve_execution_contract
from anima.core.execution import AgentMutableStateView, ResolvedExecutionContract
from anima.core.schemas import AnimaError
from anima.shell.contract_compat import load_persisted_or_legacy_contract
from anima.shell.mcp_bridge import call_agent_mcp_tool_async
from anima.shell.state import read_state


class ShellContractStatePort(ContractStatePort):
    """Load mutable state + resolved contract for control-service calls."""

    def read_state(self, instance_id: str) -> Result[AgentMutableStateView, AnimaError]:
        return read_state(instance_id)

    def load_contract(
        self, instance_id: str
    ) -> Result[ResolvedExecutionContract, AnimaError]:
        state_result = read_state(instance_id)
        if isinstance(state_result, Failure):
            return state_result
        return Success(load_persisted_or_legacy_contract(state_result.unwrap()))


class ShellApprovalPort(ApprovalPort):
    """Persist approval response payloads in shell state channel."""

    def write_approval_response(
        self, instance_id: str, approved: bool, note: str | None = None
    ) -> Result[None, AnimaError]:
        from anima.shell.state import write_approval_response

        return write_approval_response(instance_id, approved=approved, note=note)


class ShellLifecyclePort(LifecycleControlPort):
    """Apply lifecycle transitions via shell state adapters."""

    def approve(self, instance_id: str) -> Result[None, AnimaError]:
        from anima.core.lifecycle import LifecycleEvent
        from anima.shell.state import transition_state

        result = transition_state(instance_id, LifecycleEvent.approve)
        if isinstance(result, Failure):
            return result
        return Success(None)

    def reject(
        self, instance_id: str, reason: str | None = None
    ) -> Result[None, AnimaError]:
        from anima.core.lifecycle import LifecycleEvent
        from anima.shell.state import transition_state

        result = transition_state(
            instance_id,
            LifecycleEvent.reject,
        )
        if isinstance(result, Failure):
            return result
        return Success(None)

    def resume(self, instance_id: str) -> Result[None, AnimaError]:
        from anima.core.lifecycle import LifecycleEvent
        from anima.shell.state import transition_state

        result = transition_state(instance_id, LifecycleEvent.resume)
        if isinstance(result, Failure):
            return result
        return Success(None)

    def kill(
        self, instance_id: str, reason: str | None = None
    ) -> Result[None, AnimaError]:
        from anima.shell.agent_kill import kill_agent_tree

        result = kill_agent_tree(instance_id, error=reason or "Killed by user")
        if isinstance(result, Failure):
            return result
        return Success(None)


class MCPToolTransportPort(ControlTransportPort):
    """Route control-service tool calls through per-agent MCP bridge."""

    async def call_agent_tool(
        self,
        *,
        instance_id: str,
        tool_name: str,
        arguments: dict[str, object] | None = None,
        timeout: float = 30.0,
    ):
        return await call_agent_mcp_tool_async(
            instance_id,
            tool_name,
            arguments=arguments,
            timeout=timeout,
        )


# @invar:allow shell_result: Shell wiring factory returns service object, not I/O Result
def build_shell_control_service(
    *,
    transport_port: ControlTransportPort | None = None,
    contract_resolver: ExecutionContractResolver | None = None,
) -> DefaultControlService:
    """Create default control service wired to shell state adapters."""
    return DefaultControlService(
        contract_state_port=ShellContractStatePort(),
        approval_port=ShellApprovalPort(),
        lifecycle_port=ShellLifecyclePort(),
        transport_port=transport_port,
        contract_resolver=contract_resolver or resolve_execution_contract,
    )


__all__ = [
    "MCPToolTransportPort",
    "ShellApprovalPort",
    "ShellContractStatePort",
    "ShellLifecyclePort",
    "build_shell_control_service",
]
