"""Lifecycle commands: approve, reject, resume.

Spec source: plan step ``final-integration.refactor-cli-function-size``
extracts lifecycle transition commands from cli.py without behavior changes.

MCP-first behavior (sse-bridge-cli-dedup.cli-lifecycle-mcp-prefer):
- approve/reject/resume try MCP first for alive agents
- Falls back to file/state operations if MCP not available
"""

# @invar:allow function_size: CLI command implementations with state transitions
# @invar:allow shell_result: lifecycle subcommands map transition failures to process exit status

from __future__ import annotations

import sys

from rich.console import Console
from rich.panel import Panel
from returns.result import Failure, Success

from anima.core.schemas import AgentState
from anima.app.control_service import ApprovalRequest, ResumeRequest
from anima.shell.cli_commands._shared import resolve_agent
from anima.shell.mcp_bridge import try_mcp_with_fallback
from anima.shell.control_service_adapter import build_shell_control_service

EXIT_AGENT_NOT_FOUND = 2


# @shell_complexity: CLI command with JSON and human output paths
# @invar:allow function_size: CLI command with approval handling
def approve_impl(
    agent_id: str, note: str | None, json_output: bool, console: Console
) -> None:
    """Implementation for anima approve command.

    Transitions agent from awaiting_approval -> running.

    MCP-first behavior (sse-bridge-cli-dedup.cli-lifecycle-mcp-prefer):
    1. Try MCP tool call for alive agents first
    2. Fall back to file/state operations if MCP not available

    Exit codes: 0 success, 1 wrong state, 2 agent not found.
    """
    control_service = build_shell_control_service()

    # 1. Resolve ID (prefix match)
    state = resolve_agent(
        agent_id,
        json_output=json_output,
        console=console,
        exit_agent_not_found=EXIT_AGENT_NOT_FOUND,
    )
    instance_id = state.instance_id

    # 2. Validate awaiting_approval state
    if state.state != AgentState.awaiting_approval:
        if json_output:
            console.print_json(
                data={
                    "error": {
                        "code": "INVALID_STATE",
                        "numeric_code": 1,
                        "message": f"Agent not awaiting approval (state: {state.state.value})",
                        "details": {
                            "agent_id": instance_id,
                            "state": state.state.value,
                        },
                    }
                }
            )
        else:
            console.print(
                f"[red]Error: Agent not awaiting approval (state: {state.state.value})[/red]"
            )
        sys.exit(1)

    # 3. Try MCP first for alive agents (MCP-first pattern)
    mcp_result = try_mcp_with_fallback(
        instance_id,
        "anima.approve",
        arguments={"note": note} if note else {},
    )

    if isinstance(mcp_result, Success):
        # MCP call succeeded - agent handled it directly
        if json_output:
            console.print_json(
                data={
                    "instance_id": instance_id,
                    "state": AgentState.running.value,
                    "approved": True,
                    "note": note,
                }
            )
        else:
            panel = Panel(
                f"[bold]Instance:[/] {instance_id}\n"
                f"[bold]State:[/] [green]running[/green]"
                + (f"\n[bold]Note:[/] {note}" if note else ""),
                title="anima approve",
                border_style="green",
            )
            console.print(panel)
        sys.exit(0)

    # 4. Fallback: MCP not available, use shared control-service path.
    control_result = control_service.approve(
        ApprovalRequest(instance_id=state.instance_id, approved=True, note=note)
    )
    if isinstance(control_result, Failure):
        if json_output:
            console.print_json(
                data={
                    "error": {
                        "code": "INTERNAL",
                        "numeric_code": 10,
                        "message": f"Failed to approve: {control_result.failure()}",
                        "details": {
                            "agent_id": instance_id,
                            "state": state.state.value,
                        },
                    }
                }
            )
        else:
            console.print(
                f"[red]Error: Failed to approve: {control_result.failure()}[/red]"
            )
        sys.exit(1)

    # 6. Output
    if json_output:
        console.print_json(
            data={
                "instance_id": instance_id,
                "state": AgentState.running.value,
                "approved": True,
                "note": note,
            }
        )
    else:
        panel = Panel(
            f"[bold]Instance:[/] {instance_id}\n"
            f"[bold]State:[/] [green]running[/green]"
            + (f"\n[bold]Note:[/] {note}" if note else ""),
            title="anima approve",
            border_style="green",
        )
        console.print(panel)

    sys.exit(0)


# @shell_complexity: CLI command with JSON and human output paths
def reject_impl(
    agent_id: str, reason: str | None, json_output: bool, console: Console
) -> None:
    """Implementation for anima reject command.

    Transitions agent from awaiting_approval -> running (agent can re-plan).

    MCP-first behavior (sse-bridge-cli-dedup.cli-lifecycle-mcp-prefer):
    1. Try MCP tool call for alive agents first
    2. Fall back to file/state operations if MCP not available

    Exit codes: 0 success, 1 wrong state, 2 agent not found.
    """
    control_service = build_shell_control_service()

    # 1. Resolve ID (prefix match)
    state = resolve_agent(
        agent_id,
        json_output=json_output,
        console=console,
        exit_agent_not_found=EXIT_AGENT_NOT_FOUND,
    )
    instance_id = state.instance_id

    # 2. Validate awaiting_approval state
    if state.state != AgentState.awaiting_approval:
        if json_output:
            console.print_json(
                data={
                    "error": {
                        "code": "INVALID_STATE",
                        "numeric_code": 1,
                        "message": f"Agent not awaiting approval (state: {state.state.value})",
                        "details": {
                            "agent_id": instance_id,
                            "state": state.state.value,
                        },
                    }
                }
            )
        else:
            console.print(
                f"[red]Error: Agent not awaiting approval (state: {state.state.value})[/red]"
            )
        sys.exit(1)

    # 3. Try MCP first for alive agents (MCP-first pattern)
    mcp_result = try_mcp_with_fallback(
        instance_id,
        "anima.reject",
        arguments={"note": reason} if reason else {},
    )

    if isinstance(mcp_result, Success):
        # MCP call succeeded - agent handled it directly
        if json_output:
            console.print_json(
                data={
                    "instance_id": instance_id,
                    "state": AgentState.running.value,
                    "rejected": True,
                    "reason": reason,
                }
            )
        else:
            panel = Panel(
                f"[bold]Instance:[/] {instance_id}\n"
                f"[bold]State:[/] [yellow]running[/yellow]"
                + (f"\n[bold]Reason:[/] {reason}" if reason else ""),
                title="anima reject",
                border_style="red",
            )
            console.print(panel)
        sys.exit(0)

    # 4. Fallback: MCP not available, use shared control-service path.
    control_result = control_service.reject(
        ApprovalRequest(instance_id=state.instance_id, approved=False, note=reason)
    )
    if isinstance(control_result, Failure):
        if json_output:
            console.print_json(
                data={
                    "error": {
                        "code": "INTERNAL",
                        "numeric_code": 10,
                        "message": f"Failed to reject: {control_result.failure()}",
                        "details": {
                            "agent_id": instance_id,
                            "state": state.state.value,
                        },
                    }
                }
            )
        else:
            console.print(
                f"[red]Error: Failed to reject: {control_result.failure()}[/red]"
            )
        sys.exit(1)

    # 5. Output
    if json_output:
        console.print_json(
            data={
                "instance_id": instance_id,
                "state": AgentState.running.value,
                "rejected": True,
                "reason": reason,
            }
        )
    else:
        panel = Panel(
            f"[bold]Instance:[/] {instance_id}\n"
            f"[bold]State:[/] [yellow]running[/yellow]"
            + (f"\n[bold]Reason:[/] {reason}" if reason else ""),
            title="anima reject",
            border_style="red",
        )
        console.print(panel)

    sys.exit(0)


# @shell_complexity: 5 branches for resume flow (state validation, json_output, transition result branches)
def resume_impl(agent_id: str, json_output: bool, console: Console) -> None:
    """Implementation for anima resume command.

    Transitions agent from suspended -> running.

    MCP-first behavior (sse-bridge-cli-dedup.cli-lifecycle-mcp_prefer):
    1. Try MCP tool call for alive agents first
    2. Fall back to file/state operations if MCP not available

    Exit codes: 0 success, 1 wrong state, 2 agent not found.
    """
    control_service = build_shell_control_service()

    # 1. Resolve ID (prefix match)
    state = resolve_agent(
        agent_id,
        json_output=json_output,
        console=console,
        exit_agent_not_found=EXIT_AGENT_NOT_FOUND,
    )
    instance_id = state.instance_id

    # 2. Validate suspended state
    if state.state != AgentState.suspended:
        if json_output:
            console.print_json(
                data={
                    "error": {
                        "code": "INVALID_STATE",
                        "numeric_code": 1,
                        "message": f"Agent not suspended (state: {state.state.value})",
                        "details": {
                            "agent_id": instance_id,
                            "state": state.state.value,
                        },
                    }
                }
            )
        else:
            console.print(
                f"[red]Error: Agent not suspended (state: {state.state.value})[/red]"
            )
        sys.exit(1)

    # 3. Try MCP first for alive agents (MCP-first pattern)
    mcp_result = try_mcp_with_fallback(
        instance_id,
        "anima.resume",
    )

    if isinstance(mcp_result, Success):
        # MCP call succeeded - agent handled it directly
        if json_output:
            console.print_json(
                data={
                    "instance_id": instance_id,
                    "state": AgentState.running.value,
                }
            )
        else:
            panel = Panel(
                f"[bold]Instance:[/] {instance_id}\n[bold]State:[/] [green]running[/green]",
                title="anima resume",
                border_style="green",
            )
            console.print(panel)
            sys.exit(0)

    # 4. Fallback: MCP not available, use shared control-service path.
    control_result = control_service.resume(
        ResumeRequest(instance_id=state.instance_id)
    )
    if isinstance(control_result, Failure):
        if json_output:
            console.print_json(
                data={
                    "error": {
                        "code": "INTERNAL",
                        "numeric_code": 10,
                        "message": f"Failed to resume: {control_result.failure()}",
                        "details": {
                            "agent_id": instance_id,
                            "state": state.state.value,
                        },
                    }
                }
            )
        else:
            console.print(
                f"[red]Error: Failed to resume: {control_result.failure()}[/red]"
            )
        sys.exit(1)

    # 5. Output
    if json_output:
        console.print_json(
            data={
                "instance_id": instance_id,
                "state": AgentState.running.value,
            }
        )
    else:
        panel = Panel(
            f"[bold]Instance:[/] {instance_id}\n[bold]State:[/] [green]running[/green]",
            title="anima resume",
            border_style="green",
        )
        console.print(panel)

    sys.exit(0)
