"""BashTool20250124 - Persistent bash session with sentinel-based output.

Anthropic's native bash_20250124 tool with a long-running session.
"""

import asyncio
import subprocess
import uuid
from typing import Any, Literal

from nanobot.agent.tools.anthropic.base import BaseAnthropicTool, ToolResult


class _BashSession:
    """Manages a persistent bash subprocess with sentinel-based output reading."""

    def __init__(self):
        self.process: subprocess.Popen | None = None
        self._start()

    def _start(self):
        """Start the bash process."""
        self.process = subprocess.Popen(
            ["bash"],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            text=True,
            bufsize=1,
        )

    def restart(self):
        """Restart the bash session."""
        if self.process:
            self.process.terminate()
            try:
                self.process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                self.process.kill()
                self.process.wait()
        self._start()

    async def run_command(self, command: str, timeout: float = 120.0) -> str:
        """Run a command in the persistent bash session.

        Uses a unique sentinel to detect command completion.

        Args:
            command: Bash command to execute
            timeout: Maximum time to wait for command completion (seconds)

        Returns:
            Command output (stdout + stderr combined)

        Raises:
            asyncio.TimeoutError: If command doesn't complete within timeout
            RuntimeError: If bash process has died
        """
        if not self.process or self.process.poll() is not None:
            raise RuntimeError("Bash process has died")

        # Generate unique sentinel
        sentinel = f"<<BASH_COMMAND_DONE_{uuid.uuid4().hex}>>"

        # Send command + sentinel
        full_command = f"{command}\necho '{sentinel}'\n"
        self.process.stdin.write(full_command)
        self.process.stdin.flush()

        # Read output until sentinel appears
        output_lines = []
        start_time = asyncio.get_event_loop().time()

        while True:
            # Check timeout
            elapsed = asyncio.get_event_loop().time() - start_time
            if elapsed > timeout:
                raise asyncio.TimeoutError(
                    f"Command timed out after {timeout}s: {command[:50]}..."
                )

            # Read line (non-blocking via asyncio)
            try:
                line = await asyncio.wait_for(
                    asyncio.to_thread(self.process.stdout.readline),
                    timeout=1.0,
                )
            except asyncio.TimeoutError:
                # No output yet, continue waiting
                continue

            if not line:
                # EOF - process died
                raise RuntimeError("Bash process terminated unexpectedly")

            # Check for sentinel
            if sentinel in line:
                break

            output_lines.append(line.rstrip("\n"))

        return "\n".join(output_lines)

    def __del__(self):
        """Clean up bash process on deletion."""
        if self.process:
            self.process.terminate()
            try:
                self.process.wait(timeout=2)
            except subprocess.TimeoutExpired:
                self.process.kill()


class BashTool20250124(BaseAnthropicTool):
    """Anthropic's native bash_20250124 tool with persistent session.

    Executes bash commands in a long-running shell session. Environment
    variables and working directory persist across commands.

    Parameters:
        command (str, optional): Bash command to execute
        restart (bool, optional): Restart the bash session (clears state)
    """

    api_type: Literal["bash_20250124"] = "bash_20250124"
    name: Literal["bash"] = "bash"
    beta_flag: str = "computer-use-2025-11-24"

    def __init__(self):
        self._session = _BashSession()

    async def __call__(
        self,
        command: str | None = None,
        restart: bool = False,
        **kwargs: Any,
    ) -> ToolResult:
        """Execute bash command or restart session.

        Args:
            command: Bash command to execute (optional)
            restart: Restart the bash session (optional)
            **kwargs: Additional arguments (ignored)

        Returns:
            ToolResult with command output or error
        """
        if restart:
            self._session.restart()
            return ToolResult(output="Bash session restarted successfully.")

        if not command:
            return ToolResult(
                error="Either 'command' or 'restart=True' must be provided."
            )

        try:
            output = await self._session.run_command(command)
            return ToolResult(output=output if output else "(no output)")
        except asyncio.TimeoutError as e:
            return ToolResult(error=f"Command timed out: {e}")
        except Exception as e:
            return ToolResult(error=f"{e}")

    def to_params(self) -> dict[str, Any]:
        """Convert to Anthropic API tool parameter format.

        Returns:
            Tool definition for Anthropic API with bash_20250124 type
        """
        return {
            "type": self.api_type,
            "name": self.name,
        }
