"""MCP steering tools — poll for pending steering messages."""

from __future__ import annotations

import logging

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_steering_check(job_id: str, vm_name: str) -> dict:
    """Check for a pending steering message for the current pilot's job.

    Called by the PreToolUse hook on every pilot tool invocation to receive
    course-correction instructions from the orchestrator or QA Engineer.
    Returns the oldest unacknowledged message (FIFO). Does NOT acknowledge
    the message — call vm_steering_ack separately after acting on it.

    Args:
        job_id: Active job identifier.
        vm_name: Target VM name.

    Returns:
        Dict with ``found`` (bool). When True, includes all SteeringMessage
        fields (message_id, content, sender, priority, etc.). When False,
        includes only ``job_id`` and ``vm_name`` for context.

    Raises:
        ToolError: On unexpected database failure.
    """
    db = await get_db()
    try:
        row = await db.get_pending_steering_message(job_id, vm_name)
    except Exception as exc:
        raise ToolError(f"Failed to check steering for job {job_id!r} on VM {vm_name!r}: {exc}") from exc
    if row is None:
        return {"found": False, "job_id": job_id, "vm_name": vm_name}
    return {"found": True, "message": row}


@tool
async def vm_steering_send(
    job_id: str,
    vm_name: str,
    sender: str,
    content: str,
    priority: str,
    delivery: str,
    target_pilot: str | None = None,
    channel_meta: str | None = None,
) -> dict:
    """Send a steering message to a running pilot's job.

    Enqueues a steering instruction in the message queue. The pilot receives
    it on the next vm_steering_check call (via PreToolUse hook) or via
    channel push, depending on the delivery mechanism.

    Args:
        job_id: Target job identifier.
        vm_name: Target VM name.
        sender: Source actor — "orchestrator", "user", or "scheduled-task".
        content: Steering instruction text.
        priority: Message priority — "normal" or "urgent".
        delivery: Delivery mechanism — "channel" or "hook".
        target_pilot: Target pilot agent ID (optional — omit to broadcast
            to all pilots on the job).
        channel_meta: Delivery-specific metadata as JSON string (optional —
            only relevant for channel-based delivery).

    Returns:
        Dict with message_id, created_at, job_id, and vm_name confirming
        the message was enqueued.

    Raises:
        ToolError: If the job does not exist or the database operation fails.
    """
    db = await get_db()
    try:
        message_id, created_at = await db.insert_steering_message(
            job_id, vm_name, sender, content, priority, delivery, target_pilot, channel_meta
        )
    except ValueError as exc:
        raise ToolError(f"Job {job_id!r} not found on VM {vm_name!r}") from exc
    except Exception as exc:
        raise ToolError(f"Failed to send steering message: {exc}") from exc
    return {"message_id": message_id, "created_at": created_at, "job_id": job_id, "vm_name": vm_name}


@tool
async def vm_steering_ack(message_id: str) -> dict:
    """Acknowledge a steering message after acting on it.

    Marks the message as consumed by setting acknowledged_at. After
    acknowledgment, vm_steering_check will no longer return this message.

    Args:
        message_id: UUID of the steering message to acknowledge (from
            the message_id field returned by vm_steering_check).

    Returns:
        Dict with acknowledged_at timestamp and message_id confirming
        the acknowledgment.

    Raises:
        ToolError: If the message does not exist or the database
            operation fails.
    """
    db = await get_db()
    try:
        acknowledged_at = await db.acknowledge_steering_message(message_id)
    except ValueError as exc:
        raise ToolError(f"Steering message {message_id!r} not found") from exc
    except Exception as exc:
        raise ToolError(f"Failed to acknowledge steering message: {exc}") from exc
    return {"acknowledged_at": acknowledged_at, "message_id": message_id}
