"""
Persistent Shell Tools

Provides tools for agents to manage long-running shell sessions:
- SHELL_EXEC: Send commands to a persistent shell
- SHELL_VIEW: Read buffered output from a shell
- SHELL_MANAGE: List, kill, restart shells
"""

from typing import Dict, Any, List
from pathlib import Path
import logging

from ..base import (
    BaseTool, ToolMetadata, ToolParameter, ToolExecutionContext,
    ToolResult, ToolPermission
)
from .shell_manager import ShellManager

logger = logging.getLogger(__name__)


class ShellExecTool(BaseTool):
    """Send a command to a persistent shell session"""

    def get_metadata(self) -> ToolMetadata:
        return ToolMetadata(
            name="SHELL_EXEC",
            category="shell",
            description=(
                "Execute a command in a persistent shell session. Unlike RUN_COMMAND, "
                "this keeps the shell alive between calls — perfect for servers, build watchers, "
                "REPLs, and multi-step workflows. The shell persists across tool calls."
            ),
            parameters=[
                ToolParameter(
                    name="command",
                    type="string",
                    required=True,
                    description="Command to execute in the shell"
                ),
                ToolParameter(
                    name="shell_id",
                    type="string",
                    required=False,
                    default=None,
                    description="Target shell ID (e.g. 'shell-1'). If omitted, uses or creates a default shell."
                ),
                ToolParameter(
                    name="wait_ms",
                    type="int",
                    required=False,
                    default=3000,
                    description="Milliseconds to wait for output after sending command (default: 3000). Use higher for slow commands."
                ),
                ToolParameter(
                    name="label",
                    type="string",
                    required=False,
                    default="",
                    description="Label for new shell (e.g. 'dev-server', 'build'). Only used when creating a new shell."
                ),
            ],
            permissions=[ToolPermission.EXECUTE],
            examples=[
                '{"name":"SHELL_EXEC","parameters":{"command":"python -m http.server 8080","wait_ms":2000,"label":"http-server"}}',
                '{"name":"SHELL_EXEC","parameters":{"command":"npm run dev","label":"frontend"}}',
                '{"name":"SHELL_EXEC","parameters":{"command":"curl localhost:8080","shell_id":"shell-2"}}',
            ]
        )

    def execute(self, params: Dict[str, Any], context: ToolExecutionContext) -> ToolResult:
        command = params.get("command", "")
        shell_id = params.get("shell_id")
        wait_ms = min(int(params.get("wait_ms", 3000)), 30000)  # Max 30s
        label = params.get("label", "")

        if not command:
            return ToolResult(success=False, output="", error="Command is required")

        manager = ShellManager.get_instance()
        workspace = str(Path(context.workspace_path))
        cwd = context.metadata.get("cwd", workspace)

        if shell_id:
            # Use specified shell
            shell = manager.get_shell(context.session_id, shell_id)
            if not shell:
                return ToolResult(
                    success=False, output="",
                    error=f"Shell '{shell_id}' not found. Use SHELL_MANAGE with action='list' to see available shells."
                )
            if not shell.is_alive:
                return ToolResult(
                    success=False, output="",
                    error=f"Shell '{shell_id}' has exited (code={shell.process.returncode}). "
                          f"Use SHELL_MANAGE with action='restart' to restart it."
                )
        else:
            if label:
                # Create a new labeled shell
                shell, msg = manager.create_shell(
                    context.session_id, cwd, label=label, workspace_path=workspace
                )
                if not shell:
                    return ToolResult(success=False, output="", error=msg)
            else:
                # Get or create default shell
                shell = manager.get_or_create_default(
                    context.session_id, cwd, workspace_path=workspace
                )

        # Send command
        output = shell.send_command(command, wait_ms=wait_ms)

        # Update workspace file
        manager._write_workspace_file(context.session_id, workspace)

        return ToolResult(
            success=True,
            output=f"[{shell.shell_id}] $ {command}\n\n{output}",
            metadata={
                "shell_id": shell.shell_id,
                "pid": shell.pid,
                "status": shell.status,
                "label": shell.label
            }
        )


class ShellViewTool(BaseTool):
    """Read recent output from a persistent shell"""

    def get_metadata(self) -> ToolMetadata:
        return ToolMetadata(
            name="SHELL_VIEW",
            category="shell",
            description="Read recent output from a persistent shell session. Use this to check on long-running processes.",
            parameters=[
                ToolParameter(
                    name="shell_id",
                    type="string",
                    required=True,
                    description="Shell ID to read output from (e.g. 'shell-1')"
                ),
                ToolParameter(
                    name="lines",
                    type="int",
                    required=False,
                    default=50,
                    description="Number of recent lines to return (default: 50, max: 500)"
                ),
            ],
            permissions=[ToolPermission.READ],
            examples=[
                '{"name":"SHELL_VIEW","parameters":{"shell_id":"shell-1"}}',
                '{"name":"SHELL_VIEW","parameters":{"shell_id":"shell-1","lines":100}}',
            ]
        )

    def execute(self, params: Dict[str, Any], context: ToolExecutionContext) -> ToolResult:
        shell_id = params.get("shell_id", "")
        lines = min(int(params.get("lines", 50)), 500)

        if not shell_id:
            return ToolResult(success=False, output="", error="shell_id is required")

        manager = ShellManager.get_instance()
        shell = manager.get_shell(context.session_id, shell_id)

        if not shell:
            return ToolResult(
                success=False, output="",
                error=f"Shell '{shell_id}' not found. Use SHELL_MANAGE with action='list' to see available shells."
            )

        output = shell.read_output(lines=lines)

        return ToolResult(
            success=True,
            output=output,
            metadata={
                "shell_id": shell.shell_id,
                "status": shell.status,
                "buffer_lines": len(shell.output_buffer)
            }
        )


class ShellManageTool(BaseTool):
    """List, kill, or restart shell sessions"""

    def get_metadata(self) -> ToolMetadata:
        return ToolMetadata(
            name="SHELL_MANAGE",
            category="shell",
            description="Manage persistent shell sessions: list running shells, kill a shell, or restart a stopped shell.",
            parameters=[
                ToolParameter(
                    name="action",
                    type="choice",
                    required=True,
                    description="Action to perform",
                    choices=["list", "kill", "restart"]
                ),
                ToolParameter(
                    name="shell_id",
                    type="string",
                    required=False,
                    default=None,
                    description="Shell ID (required for 'kill' and 'restart')"
                ),
            ],
            permissions=[ToolPermission.EXECUTE],
            examples=[
                '{"name":"SHELL_MANAGE","parameters":{"action":"list"}}',
                '{"name":"SHELL_MANAGE","parameters":{"action":"kill","shell_id":"shell-1"}}',
                '{"name":"SHELL_MANAGE","parameters":{"action":"restart","shell_id":"shell-1"}}',
            ]
        )

    def execute(self, params: Dict[str, Any], context: ToolExecutionContext) -> ToolResult:
        action = params.get("action", "")
        shell_id = params.get("shell_id")
        manager = ShellManager.get_instance()
        workspace = str(Path(context.workspace_path))

        if action == "list":
            shells = manager.list_shells(context.session_id)
            if not shells:
                return ToolResult(
                    success=True,
                    output="No active shells. Use SHELL_EXEC to start one."
                )

            lines = ["Active Shells:"]
            for s in shells:
                status_icon = "🟢" if s["status"] == "running" else "🔴"
                lines.append(
                    f"  {status_icon} {s['id']} ({s['label']}) — PID: {s['pid']} — "
                    f"Status: {s['status']} — Lines: {s['buffer_lines']} — "
                    f"Last: {s['last_output_line'][:60]}"
                )
            lines.append(f"\nTotal: {len(shells)}/{5} shells")

            return ToolResult(
                success=True,
                output='\n'.join(lines),
                metadata={"count": len(shells)}
            )

        elif action == "kill":
            if not shell_id:
                return ToolResult(success=False, output="", error="shell_id is required for 'kill' action")

            killed = manager.kill_shell(context.session_id, shell_id, workspace_path=workspace)
            if killed:
                return ToolResult(success=True, output=f"Killed shell '{shell_id}'")
            else:
                return ToolResult(success=False, output="", error=f"Shell '{shell_id}' not found")

        elif action == "restart":
            if not shell_id:
                return ToolResult(success=False, output="", error="shell_id is required for 'restart' action")

            old_shell = manager.get_shell(context.session_id, shell_id)
            old_cwd = old_shell.cwd if old_shell else context.metadata.get("cwd", workspace)
            old_label = old_shell.label if old_shell else shell_id

            # Kill old
            manager.kill_shell(context.session_id, shell_id, workspace_path=workspace)

            # Create new with same ID (manager auto-increments, so label matches)
            new_shell, msg = manager.create_shell(
                context.session_id, old_cwd, label=old_label, workspace_path=workspace
            )
            if new_shell:
                return ToolResult(
                    success=True,
                    output=f"Restarted shell as '{new_shell.shell_id}' (was '{shell_id}') — PID: {new_shell.pid}",
                    metadata={"new_shell_id": new_shell.shell_id, "pid": new_shell.pid}
                )
            else:
                return ToolResult(success=False, output="", error=f"Failed to restart: {msg}")

        else:
            return ToolResult(success=False, output="", error=f"Unknown action: {action}. Use 'list', 'kill', or 'restart'.")


def get_all_tools() -> List[type]:
    """Return all persistent shell tools"""
    return [
        ShellExecTool,
        ShellViewTool,
        ShellManageTool,
    ]
