"""MCP TODO tools — manage the per-job TODO list for pilot task tracking."""

from __future__ import annotations

import logging
from datetime import UTC, datetime
from uuid import uuid4

from fastmcp.exceptions import ToolError
from fastmcp.tools import tool

from mcp_vm_blackbox.coordination_db import get_db

_LOG = logging.getLogger(__name__)


@tool
async def vm_todo_list(job_id: str, vm_name: str, status: str | None = None) -> dict:
    """List TODO items for a job, optionally filtered by status.

    Returns all TODO items for the specified job ordered by priority then
    creation time. Use the optional ``status`` filter to narrow results to
    a specific lifecycle state.

    Args:
        job_id: Job identifier.
        vm_name: Target VM name.
        status: Optional status filter — one of: "queued", "in_progress",
            "done", "blocked", "skipped". Omit to return all items.

    Returns:
        Dict with ``job_id``, ``vm_name``, ``items`` (list of TODO dicts),
        and ``total`` (int count after filtering).

    Raises:
        ToolError: On unexpected database failure.
    """
    db = await get_db()
    try:
        items = await db.list_todos(job_id, vm_name)
    except Exception as exc:
        raise ToolError(f"Failed to list todos for job {job_id}: {exc}") from exc
    if status is not None:
        items = [item for item in items if item.get("status") == status]
    return {"job_id": job_id, "vm_name": vm_name, "items": items, "total": len(items)}


@tool
async def vm_todo_add(
    job_id: str,
    vm_name: str,
    title: str,
    added_by: str,
    description: str | None = None,
    priority: int = 100,
    assigned_pilot: str | None = None,
) -> dict:
    """Add a new TODO item to the per-job task list.

    Generates a UUID item_id and ISO-8601 created_at timestamp. The new item
    is always created with status ``"queued"``.

    Args:
        job_id: Job identifier.
        vm_name: Target VM name.
        title: Short title for the TODO item.
        added_by: Actor creating the item — e.g. "pilot", "orchestrator",
            "user".
        description: Optional longer description of the task.
        priority: Sort priority — lower numbers sort first. Defaults to 100.
        assigned_pilot: Optional pilot agent ID to assign the item to.

    Returns:
        Dict with ``job_id``, ``item_id``, ``title``, ``status``,
        ``priority``, ``created_at``, and ``timestamp``.

    Raises:
        ToolError: If the job does not exist or the database operation fails.
    """
    item_id = str(uuid4())
    created_at = datetime.now(UTC).isoformat()
    record: dict[str, object] = {
        "item_id": item_id,
        "job_id": job_id,
        "vm_name": vm_name,
        "title": title,
        "description": description,
        "status": "queued",
        "priority": priority,
        "added_by": added_by,
        "assigned_pilot": assigned_pilot,
        "created_at": created_at,
    }
    db = await get_db()
    try:
        await db.insert_todo(record)
    except Exception as exc:
        raise ToolError(f"Failed to add todo for job {job_id}: {exc}") from exc
    return {
        "job_id": job_id,
        "item_id": item_id,
        "title": title,
        "status": "queued",
        "priority": priority,
        "created_at": created_at,
        "timestamp": created_at,
    }


@tool
async def vm_todo_update(
    job_id: str,
    vm_name: str,
    item_id: str,
    status: str | None = None,
    title: str | None = None,
    blocked_by: str | None = None,
    assigned_pilot: str | None = None,
) -> dict:
    """Update a TODO item's fields.

    Applies a partial update to the specified TODO item. Only the fields
    provided are changed; omitted fields are left unchanged. Auto-timestamps
    (``started_at``, ``completed_at``) are handled by the database layer.

    Args:
        job_id: Job identifier.
        vm_name: Target VM name (used for context in error messages).
        item_id: UUID of the TODO item to update.
        status: New status — one of: "queued", "in_progress", "done",
            "blocked", "skipped". Optional.
        title: Replacement title text. Optional.
        blocked_by: Description of what is blocking this item. Optional.
        assigned_pilot: Pilot agent ID to assign or reassign. Optional.

    Returns:
        Dict with all updated item fields plus a ``timestamp`` field.

    Raises:
        ToolError: If the item does not exist, or on database failure.
    """
    patch: dict[str, object] = {}
    if status is not None:
        patch["status"] = status
    if title is not None:
        patch["title"] = title
    if blocked_by is not None:
        patch["blocked_by"] = blocked_by
    if assigned_pilot is not None:
        patch["assigned_pilot"] = assigned_pilot

    db = await get_db()
    try:
        row = await db.update_todo(item_id, job_id, patch)
    except Exception as exc:
        raise ToolError(f"Failed to update todo {item_id} for job {job_id}: {exc}") from exc
    if row is None:
        raise ToolError(f"TODO item {item_id!r} not found for job {job_id!r}")
    result = dict(row)
    result["timestamp"] = datetime.now(UTC).isoformat()
    return result


@tool
async def vm_todo_reorder(job_id: str, vm_name: str, ordered_ids: list[str]) -> dict:
    """Reorder TODO items by reassigning priority values.

    Items in ``ordered_ids`` receive ``priority = index * 10`` (0-based).
    Items not present in the list retain their existing priority. This
    allows callers to specify a partial reordering.

    Args:
        job_id: Job identifier.
        vm_name: Target VM name.
        ordered_ids: Ordered list of ``item_id`` values. The first element
            receives the lowest priority number (highest precedence).

    Returns:
        Dict with ``job_id``, ``reordered`` (count of IDs supplied), and
        ``timestamp``.

    Raises:
        ToolError: On unexpected database failure.
    """
    db = await get_db()
    try:
        await db.reorder_todos(job_id, vm_name, ordered_ids)
    except Exception as exc:
        raise ToolError(f"Failed to reorder todos for job {job_id}: {exc}") from exc
    return {"job_id": job_id, "reordered": len(ordered_ids), "timestamp": datetime.now(UTC).isoformat()}
