from __future__ import annotations

from typing import Optional

from .._http import HttpClient
from .._environment import Environment, resolve_rosalind_url
from ..models.chat import ChatInfo, ChatStatus
from ..models.block import Block, BashOutput
from .workspace import Workspace


class RosalindNamespace:
    def __init__(self, http: HttpClient):
        self._http = http

    # ── chat lifecycle ──────────────────────────────────────────────

    def create_chat(
        self,
        project: str,
        chat_name: str,
        description: str = "",
        environment: Environment = None,
    ) -> str:
        base_url = resolve_rosalind_url(environment)
        payload: dict = {
            "job_name": project,
            "chat_name": chat_name,
            "description": description,
        }
        if environment:
            payload["device"] = environment
        data = self._http.post("/rosalind/chat/start", json=payload, base_url=base_url)
        return data.get("chat_name", chat_name)

    def send_message(
        self,
        project: str,
        chat_name: str,
        message: str,
        mode: str = "pro",
        depth: str = "balanced",
        images: Optional[list[dict]] = None,
        force_fresh_session: bool = False,
        callback_url: Optional[str] = None,
        callback_headers: Optional[dict[str, str]] = None,
    ) -> bool:
        """Fire-and-forget: sends a message and returns True if accepted.

        The backend starts an async task. Use chat_status() to check
        whether the agent is still running, and get_blocks() to read
        output once done.

        If callback_url is provided, the backend will POST a JSON payload
        to that URL when the chat task finishes (completed/error/cancelled).
        """
        payload: dict = {
            "job_name": project,
            "chat_name": chat_name,
            "message": message,
            "model_mode": mode,
            "depth": depth,
            "images": images,
            "force_fresh_session": force_fresh_session,
        }
        if callback_url:
            payload["callback_url"] = callback_url
        if callback_headers:
            payload["callback_headers"] = callback_headers
        data = self._http.post("/rosalind/chat/message", json=payload)
        return data.get("success", False)

    def cancel_chat(self, project: str, chat_name: str) -> bool:
        """Cancel the currently-running task for this chat, if any."""
        task = self._find_task(project, chat_name)
        if not task or task.get("status") != "running":
            return False
        data = self._http.post(f"/rosalind/task/{task['task_id']}/cancel")
        return data.get("success", False)

    # ── listing / status ────────────────────────────────────────────

    def list_chats(self, project: str, limit: Optional[int] = None) -> list[ChatInfo]:
        """List chats for a project, most-recent first."""
        data = self._http.get(f"/rosalind/job/{project}/chats")
        chats = [ChatInfo.from_dict(c) for c in data.get("chats", [])]
        if limit is not None:
            chats = chats[:limit]
        return chats

    def chat_status(self, project: str, chat_name: str) -> ChatStatus:
        """Check whether a chat's agent task is still running."""
        task = self._find_task(project, chat_name)
        return ChatStatus.from_task(chat_name, task)

    # ── block access ────────────────────────────────────────────────

    def list_blocks(self, project: str, chat_name: str) -> int:
        """Return the total number of blocks in a chat's history."""
        data = self._http.get(f"/rosalind/chat/{project}/{chat_name}/history")
        return data.get("total", 0)

    def get_blocks(
        self,
        project: str,
        chat_name: str,
        start: int = 0,
        end: Optional[int] = None,
    ) -> list[Block]:
        """Return blocks[start:end] from the chat history.

        Each Block carries .content_type to distinguish its role:
          "text"        → block.text / block.assistant_message
          "thinking"    → block.text
          "tool_use"    → block.tool_name, block.tool_input
          "mcp_result"  → block.tool_name, block.tool_result, block.is_output_panel
        """
        data = self._http.get(f"/rosalind/chat/{project}/{chat_name}/history")
        raw = data.get("history", [])
        sliced = raw[start:end]
        return [Block.from_raw(start + i, entry) for i, entry in enumerate(sliced)]

    def get_bash_outputs(
        self,
        project: str,
        chat_name: str,
        start: int = 0,
        end: Optional[int] = None,
    ) -> list[BashOutput]:
        """Extract only bash command inputs/outputs from the block range."""
        blocks = self.get_blocks(project, chat_name, start, end)
        results: list[BashOutput] = []
        # Pair tool_use (command) with the next mcp_result (output)
        pending_command: Optional[str] = None
        for b in blocks:
            if b.content_type == "tool_use" and b.is_bash:
                pending_command = (b.tool_input or {}).get("command")
            elif b.content_type == "mcp_result" and b.is_bash:
                bo = BashOutput.from_block(b)
                if bo:
                    if pending_command and not bo.command:
                        bo.command = pending_command
                    results.append(bo)
                pending_command = None
        return results

    # ── workspace shortcut ──────────────────────────────────────────

    def workspace(self, project: str, chat_name: str) -> Workspace:
        """Get a Workspace helper scoped to this chat."""
        return Workspace(self._http, project, chat_name)

    # ── internals ───────────────────────────────────────────────────

    def _find_task(self, project: str, chat_name: str) -> Optional[dict]:
        """Find the most-recent task for a given chat from the user's
        active task list.  Returns None if no task found."""
        data = self._http.get("/rosalind/tasks")
        tasks = data.get("tasks", [])
        for t in tasks:
            if t.get("job_name") == project and t.get("chat_name") == chat_name:
                return t
        return None
