"""
Agent execution engine supporting multiple invocation modes.

Modes:
1. CLI: Interactive execution with approval prompt
2. Programmatic: Direct execution for skills and scripts
3. Server: Future - HTTP/WebSocket server for agent requests
"""

import asyncio
import time
import uuid
from typing import TYPE_CHECKING, Any

from rich.console import Console
from rich.panel import Panel
from rich.prompt import Confirm

from persuade.agents.base import AgentPhaseResult, TwoPhaseAgent
from persuade.agents.observer import NullObserver

if TYPE_CHECKING:
    from persuade.agents.observer import AgentObserver


class AgentRunner:
    """Execute agents via CLI, skill, or server.

    This class handles the orchestration of two-phase agents, including:
    - Running the plan phase
    - Displaying results to the user
    - Getting approval (for interactive mode)
    - Running the execute phase

    Supports observability via the AgentObserver protocol for structured
    logging and metrics collection.
    """

    def __init__(
        self,
        console: Console | None = None,
        observer: "AgentObserver | None" = None,
    ):
        """Initialize the runner.

        Args:
            console: Rich console for output. Creates new one if not provided.
            observer: Optional observer for structured logging/metrics.
                     Defaults to NullObserver (no-op).
        """
        self.console = console or Console()
        self.observer = observer or NullObserver()

    async def run_interactive(
        self,
        agent: TwoPhaseAgent,
        auto_approve: bool = False,
        **kwargs: Any,
    ) -> AgentPhaseResult:
        """Run agent with interactive approval.

        Args:
            agent: The agent to execute
            auto_approve: If True, skip approval prompt
            **kwargs: Additional arguments for the agent

        Returns:
            AgentPhaseResult from execution (or plan if aborted)
        """
        run_id = str(uuid.uuid4())[:8]
        agent_type = agent.__class__.__name__

        # Phase 1: Plan
        self.console.print("\n[bold blue]Phase 1: Planning...[/bold blue]\n")
        self.observer.on_phase_start(agent_type, run_id, "plan")
        start_time = time.perf_counter()

        plan_result = await agent.plan(**kwargs)

        duration_ms = (time.perf_counter() - start_time) * 1000
        self.observer.on_phase_complete(
            agent_type, run_id, "plan", duration_ms, plan_result.success, plan_result.error
        )

        if not plan_result.success:
            self.console.print(
                Panel(
                    f"[red]Plan failed:[/red] {plan_result.error}",
                    title="Error",
                    border_style="red",
                )
            )
            return plan_result

        # Display plan summary
        self._display_plan(plan_result)

        # Get approval
        if not auto_approve:
            approved = Confirm.ask("\n[yellow]Approve and execute?[/yellow]", default=False)
            if not approved:
                self.console.print("[dim]Aborted by user.[/dim]")
                return AgentPhaseResult(
                    success=False,
                    error="Aborted by user",
                    data=plan_result.data,
                    messages=["Plan was not approved"],
                )

        # Phase 2: Execute
        self.console.print("\n[bold green]Phase 2: Executing...[/bold green]\n")
        self.observer.on_phase_start(agent_type, run_id, "execute")
        start_time = time.perf_counter()

        exec_result = await agent.execute(plan_result, **kwargs)

        duration_ms = (time.perf_counter() - start_time) * 1000
        self.observer.on_phase_complete(
            agent_type, run_id, "execute", duration_ms, exec_result.success, exec_result.error
        )

        if exec_result.success:
            self._display_success(exec_result)
        else:
            self.console.print(
                Panel(
                    f"[red]Execution failed:[/red] {exec_result.error}",
                    title="Error",
                    border_style="red",
                )
            )

        return exec_result

    def _display_plan(self, result: AgentPhaseResult) -> None:
        """Display the plan summary to the user."""
        content = "\n".join(f"  {msg}" for msg in result.messages)
        self.console.print(
            Panel(
                content or "[dim]No messages[/dim]",
                title="[bold]Plan Summary[/bold]",
                border_style="blue",
            )
        )

    def _display_success(self, result: AgentPhaseResult) -> None:
        """Display execution success summary."""
        content = "\n".join(f"  {msg}" for msg in result.messages)
        self.console.print(
            Panel(
                content or "[dim]Completed[/dim]",
                title="[bold green]Success[/bold green]",
                border_style="green",
            )
        )

    @staticmethod
    def run_sync(
        agent: TwoPhaseAgent,
        auto_approve: bool = False,
        **kwargs: Any,
    ) -> AgentPhaseResult:
        """Synchronous wrapper for CLI execution.

        Args:
            agent: The agent to execute
            auto_approve: If True, skip approval prompt
            **kwargs: Additional arguments for the agent

        Returns:
            AgentPhaseResult from execution
        """
        runner = AgentRunner()
        return asyncio.run(runner.run_interactive(agent, auto_approve, **kwargs))

    @staticmethod
    def run_headless(
        agent: TwoPhaseAgent,
        **kwargs: Any,
    ) -> AgentPhaseResult:
        """Run agent without interactive prompts (for programmatic use).

        Always auto-approves. Use for CI/CD or when approval is pre-determined.

        Args:
            agent: The agent to execute
            **kwargs: Additional arguments for the agent

        Returns:
            AgentPhaseResult from execution
        """
        return asyncio.run(agent.run(auto_approve=True, **kwargs))


def cli_main(agent: TwoPhaseAgent, auto_approve: bool = False, **kwargs: Any) -> int:
    """Main entry point for CLI commands.

    Args:
        agent: The agent to execute
        auto_approve: If True, skip approval prompt
        **kwargs: Additional arguments for the agent

    Returns:
        Exit code (0 for success, 1 for failure)
    """
    result = AgentRunner.run_sync(agent, auto_approve, **kwargs)
    return 0 if result.success else 1
