"""Background-task management tool — list/check/wait/cancel detached jobs."""
from __future__ import annotations

import asyncio

from navi.tools._internal.base import (
    Tool,
    ToolContext,
    ToolResult,
    current_session_id,
)

_MAX_CHECK_OUTPUT = 2000
_MAX_WAIT_TIMEOUT = 120.0


def _sid(ctx: ToolContext | None) -> str | None:
    return (ctx.session_id if ctx else None) or current_session_id.get()


class TasksTool(Tool):
    name = "tasks"
    description = (
        "Manage background tasks started with background=true on long-running "
        "tool calls (terminal, ssh_exec, peer ask, spawn_agent, code_exec). "
        "list — show this session's tasks; "
        "check — status + result + recent progress of one task; "
        "wait — block until a task finishes (timeout cap 120s) and return its result; "
        "cancel — stop a running task. "
        "Prefer check + continue working over wait: wait blocks your turn."
    )
    parameters = {
        "type": "object",
        "properties": {
            "action": {
                "type": "string",
                "enum": ["list", "check", "wait", "cancel"],
                "description": "What to do with background tasks.",
            },
            "task_id": {
                "type": "string",
                "description": "Task id (bt-...) — required for check/wait/cancel.",
            },
            "timeout": {
                "type": "number",
                "description": "Max seconds to wait in the 'wait' action (default 120).",
            },
        },
        "required": ["action"],
    }

    async def execute(self, params: dict, ctx: ToolContext | None = None) -> ToolResult:
        from navi.core.tasks import get_task_manager

        action = params.get("action")
        manager = get_task_manager()
        session_id = _sid(ctx)

        if action == "list":
            jobs = manager.list(session_id or "__default__")
            if not jobs:
                return ToolResult(success=True, output="No background tasks in this session.")
            lines = [
                f"{j.task_id}  {j.tool:<12} {j.status:<10} {self._age(j)}s"
                + (f"  {j.preview(limit=120)}" if j.status != "running" else "")
                for j in jobs
            ]
            return ToolResult(success=True, output="\n".join(lines))

        task_id = params.get("task_id")
        if not task_id:
            return ToolResult(success=False, output="", error="'task_id' is required")
        job = manager.get(task_id, session_id or "__default__")
        if job is None:
            return ToolResult(
                success=False,
                output=f"Task '{task_id}' not found in this session (it may have been reaped).",
                error="task_not_found",
            )

        if action == "check":
            return ToolResult(success=True, output=self._render_check(job))

        if action == "wait":
            timeout = min(float(params.get("timeout") or _MAX_WAIT_TIMEOUT), _MAX_WAIT_TIMEOUT)
            try:
                await asyncio.wait_for(job.done.wait(), timeout=timeout)
            except asyncio.TimeoutError:
                return ToolResult(
                    success=False,
                    output=(
                        f"Task {job.task_id} is still running after {timeout:g}s "
                        f"(status: {job.status}). Continue other work and check again later."
                    ),
                    error="wait_timeout",
                )
            return ToolResult(success=True, output=self._render_check(job))

        if action == "cancel":
            if manager.cancel(job):
                return ToolResult(success=True, output=f"Task {job.task_id} cancelled.")
            return ToolResult(
                success=False,
                output=f"Task {job.task_id} is not running (status: {job.status}).",
                error="not_running",
            )

        return ToolResult(success=False, output="", error=f"Unknown action '{action}'")

    # -- rendering ----------------------------------------------------------

    @staticmethod
    def _age(job) -> int:
        import time

        end = job.finished_at if job.finished_at is not None else time.time()
        return int(end - job.created_at)

    def _render_check(self, job) -> str:
        parts = [
            f"Task {job.task_id} ({job.tool}) — status: {job.status}, age: {self._age(job)}s",
        ]
        if job.subagent_tokens is not None:
            parts.append(f"Sub-agent tokens: {job.subagent_tokens}")
        if job.status == "running":
            progress = self._recent_events(job)
            if progress:
                parts.append("Recent progress:\n" + progress)
            else:
                parts.append("No progress events yet.")
        else:
            if job.result is not None:
                out = job.result.to_message_content()
                if len(out) > _MAX_CHECK_OUTPUT:
                    out = out[:_MAX_CHECK_OUTPUT] + "\n... (truncated — full result is in the completion note)"
                parts.append(("Result:\n" if job.result.success else "Result (failed):\n") + out)
            if job.error and job.result is None:
                parts.append(f"Error: {job.error}")
        return "\n".join(parts)

    @staticmethod
    def _recent_events(job, limit: int = 8) -> str:
        """Compact summary of the last events in the task's ring buffer."""
        if job.ring is None:
            return ""
        items = list(job.ring._queue)[-limit:]
        lines = []
        for item in items:
            kind = type(item).__name__
            detail = ""
            if kind == "ToolStarted" or kind == "ToolEvent":
                detail = getattr(item, "tool_name", "")
                if kind == "ToolEvent":
                    detail += " ✓" if getattr(item, "success", False) else " ✗"
            elif kind == "TurnThinking":
                text = (getattr(item, "thinking", "") or "").strip().replace("\n", " ")
                detail = text[:100]
            elif kind == "PlanReady":
                detail = "plan ready"
            lines.append(f"  {kind}{(': ' + detail) if detail else ''}")
        return "\n".join(lines) if lines else ""