"""Query-style CLI command implementations.

Spec source: plan step ``cli-refactor.split-cli-package`` (initial split)
requires moving command logic out of ``src/anima/shell/cli.py`` without
behavior changes.
"""

from __future__ import annotations

import sys
from datetime import datetime, timezone

from rich.table import Table
from rich.tree import Tree
from returns.result import Failure

from anima.app.control_service import StatusRequest
from anima.core.schemas import AgentState, AgentStateData
from anima.shell.control_service_adapter import build_shell_control_service
from anima.shell.cli_commands._shared import (
    resolve_agent,
    shortest_unique_agent_id_prefixes,
)
from anima.shell.state import (
    check_and_reap_stale_agents,
    list_agents,
)


STATE_COLORS: dict[AgentState, str] = {
    AgentState.running: "green",
    AgentState.completed: "blue",
    AgentState.failed: "red",
    AgentState.killed: "yellow",
    AgentState.pending: "dim",
    AgentState.suspended: "cyan",
    AgentState.awaiting_approval: "magenta",
}


# @invar:allow shell_result: formatting helper used only for CLI presentation
# @shell_orchestration: helper remains in shell to avoid cross-layer churn in initial split
# @shell_complexity: bounded formatting branches mirror prior CLI behavior
def _uptime_from(created_at: str | None) -> str:
    """Return human-readable uptime from an ISO timestamp."""
    if not created_at:
        return "N/A"

    try:
        created = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
        delta = datetime.now(timezone.utc) - created
        total_seconds = int(delta.total_seconds())
        if total_seconds >= 3600:
            hours = total_seconds // 3600
            minutes = (total_seconds % 3600) // 60
            return f"{hours}h{minutes}m"
        if total_seconds >= 60:
            minutes = total_seconds // 60
            seconds = total_seconds % 60
            return f"{minutes}m{seconds}s"
        return f"{total_seconds}s"
    except (ValueError, TypeError):
        return "N/A"


# Extracted to state.py as check_and_reap_stale_agents (public).
# Local alias preserves call sites in this file.
_check_and_reap_stale_agents = check_and_reap_stale_agents


# @shell_complexity: CLI output with table formatting and JSON mode branches
def list_command(state_filter: str | None, json_output: bool, console) -> None:
    """Implementation for ``anima list``."""
    filter_state = None
    if state_filter:
        try:
            filter_state = AgentState(state_filter)
        except ValueError:
            console.print(f"[red]Error: Invalid state '{state_filter}'[/red]")
            sys.exit(1)

    result = list_agents(filter_state)

    from returns.result import Failure

    if isinstance(result, Failure):
        console.print(f"[red]Error: {result.failure()}[/red]")
        sys.exit(1)

    agents = result.unwrap()

    # Stale detection: check running agents and auto-reap dead PIDs
    agents = _check_and_reap_stale_agents(agents)

    if json_output:
        output = [a.model_dump() for a in agents]
        console.print_json(data=output)
        return

    if not agents:
        console.print("No agents found.")
        return

    table = Table(title="Agents")
    table.add_column("INSTANCE_ID", style="cyan")
    table.add_column("STATE", style="white")
    table.add_column("PERSONA", style="white")
    table.add_column("LIFECYCLE", style="white")
    table.add_column("UPTIME", style="white")
    table.add_column("TOKENS_USED", style="white")

    display_id_map = shortest_unique_agent_id_prefixes([a.instance_id for a in agents])

    for agent in agents:
        instance_id_short = display_id_map.get(agent.instance_id, agent.instance_id)
        state_color = STATE_COLORS.get(agent.state, "white")
        state_str = f"[{state_color}]{agent.state.value}[/{state_color}]"
        persona = agent.persona_name or "N/A"
        lifecycle = agent.lifecycle.value if agent.lifecycle else "N/A"
        uptime_str = _uptime_from(agent.created_at)
        tokens_used = (
            str(agent.token_usage.total_tokens)
            if agent.token_usage and agent.token_usage.total_tokens > 0
            else "0"
        )

        table.add_row(
            instance_id_short,
            state_str,
            persona,
            lifecycle,
            uptime_str,
            tokens_used,
        )

    console.print(table)


# @shell_complexity: 16 branches for tree display logic (state filter, hierarchy detection, json output, multiple output modes)
def tree_command(state_filter: str | None, json_output: bool, console) -> None:
    """Implementation for ``anima tree``."""
    filter_state = None
    if state_filter:
        try:
            filter_state = AgentState(state_filter)
        except ValueError:
            console.print(f"[red]Error: Invalid state '{state_filter}'[/red]")
            sys.exit(1)

    result = list_agents(filter_state)

    from returns.result import Failure

    if isinstance(result, Failure):
        console.print(f"[red]Error: {result.failure()}[/red]")
        sys.exit(1)

    agents = result.unwrap()

    # Stale detection: check running agents and auto-reap dead PIDs
    agents = _check_and_reap_stale_agents(agents)

    if not agents:
        if json_output:
            console.print_json(data=[])
        else:
            console.print("[dim]No agents found.[/dim]")
        sys.exit(0)

    agent_by_id: dict[str, AgentStateData] = {a.instance_id: a for a in agents}

    root_agents: list[AgentStateData] = []
    has_hierarchy = False
    for agent in agents:
        if agent.parent_id:
            has_hierarchy = True
        else:
            root_agents.append(agent)

    if json_output:
        output: list[dict] = []
        if has_hierarchy:

            def build_tree_nodes(agent: AgentStateData) -> dict:
                node = agent.model_dump()
                node["children"] = [
                    build_tree_nodes(agent_by_id[cid])
                    for cid in agent.children
                    if cid in agent_by_id
                ]
                return node

            output = [build_tree_nodes(root) for root in root_agents]
        else:
            output = [a.model_dump() for a in agents]
        console.print_json(data=output)
        sys.exit(0)

    if not has_hierarchy:
        tree = Tree("[bold]Agents (flat)[/bold]")
        display_id_map = shortest_unique_agent_id_prefixes(
            [a.instance_id for a in agents]
        )
        for agent in agents:
            instance_id_short = display_id_map.get(agent.instance_id, agent.instance_id)
            state_color = STATE_COLORS.get(agent.state, "white")
            persona = agent.persona_name or "N/A"
            tree.add(
                f"[cyan]{instance_id_short}[/cyan] "
                f"[{state_color}]{agent.state.value}[/{state_color}] "
                f"[dim]{persona}[/dim]"
            )
        console.print(tree)
        sys.exit(0)

    display_id_map = shortest_unique_agent_id_prefixes([a.instance_id for a in agents])

    def add_agent_to_tree(agent: AgentStateData, parent_node) -> None:
        instance_id_short = display_id_map.get(agent.instance_id, agent.instance_id)
        state_color = STATE_COLORS.get(agent.state, "white")
        persona = agent.persona_name or "N/A"
        label = (
            f"[cyan]{instance_id_short}[/cyan] "
            f"[{state_color}]{agent.state.value}[/{state_color}] "
            f"[dim]{persona}[/dim]"
        )

        node = parent_node.add(label)
        for child_id in agent.children:
            if child_id in agent_by_id:
                add_agent_to_tree(agent_by_id[child_id], node)

    tree = Tree("[bold]Agent Hierarchy[/bold]")
    for agent in root_agents:
        add_agent_to_tree(agent, tree)

    console.print(tree)
    sys.exit(0)


# @shell_complexity: 4 branches for status display logic (parent check, children, result, error details)
def status_command(
    agent_id: str,
    json_output: bool,
    console,
    *,
    exit_agent_not_found: int,
) -> None:
    """Implementation for ``anima status``."""
    from rich.panel import Panel

    state = resolve_agent(
        agent_id,
        json_output=json_output,
        console=console,
        exit_agent_not_found=exit_agent_not_found,
    )
    control_result = build_shell_control_service().status(
        StatusRequest(instance_id=state.instance_id)
    )
    if not isinstance(control_result, Failure):
        state = control_result.unwrap()[0]

    if json_output:
        console.print_json(data=state.model_dump())
        return

    state_color = STATE_COLORS.get(state.state, "white")
    uptime_str = _uptime_from(state.created_at)
    tools_str = ", ".join(f"{t.name}({t.count})" for t in state.tools_called) or "none"
    children_str = ", ".join(state.children) if state.children else "none"

    panel_content = [
        f"[bold]Instance ID:[/] {state.instance_id}",
        f"[bold]State:[/] [{state_color}]{state.state.value}[/{state_color}]",
        f"[bold]Persona:[/] {state.persona_name}",
        f"[bold]Lifecycle:[/] {state.lifecycle.value}",
        f"[bold]Tokens:[/] {state.token_usage.prompt_tokens} prompt / {state.token_usage.completion_tokens} completion / {state.token_usage.total_tokens} total",
        f"[bold]Tools Called:[/] {tools_str}",
        f"[bold]Uptime:[/] {uptime_str}",
    ]

    if state.parent_id:
        panel_content.append(f"[bold]Parent ID:[/] {state.parent_id}")

    panel_content.append(f"[bold]Children:[/] {children_str}")

    if state.error:
        panel_content.append(f"[bold]Error:[/] [red]{state.error}[/red]")

    panel = Panel(
        "\n".join(panel_content),
        title="Agent Status",
        border_style="blue",
    )
    console.print(panel)
