"""MCP tools for operator control of the live race producer."""
from __future__ import annotations

import json
import os
import urllib.error
import urllib.request

from broadcast_control import control_enabled
from mcp.server.fastmcp import FastMCP


def _call(path: str, payload: dict | None = None) -> dict:
    base = os.environ.get("BROADCAST_PRODUCER_BASE", "http://127.0.0.1:8765").rstrip("/")
    token = (os.environ.get("VR_BROADCAST_CONTROL_TOKEN") or "").strip()
    if not token:
        raise RuntimeError("VR_BROADCAST_CONTROL_TOKEN is not configured")
    data = json.dumps(payload).encode() if payload is not None else None
    req = urllib.request.Request(base + path, data=data,
                                 method="POST" if data is not None else "GET",
                                 headers={"Authorization": "Bearer " + token,
                                          "Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=5) as response:
            return json.loads(response.read())
    except urllib.error.HTTPError as exc:
        try:
            detail = json.loads(exc.read()).get("error")
        except Exception:
            detail = None
        raise RuntimeError(detail or f"producer returned HTTP {exc.code}") from exc


def _manage_call(path: str, payload: dict) -> dict:
    base = (os.environ.get("BROADCAST_MANAGE_BASE") or "").rstrip("/")
    token = (os.environ.get("VR_MANAGE_TOKEN") or "").strip()
    if not base:
        raise RuntimeError("BROADCAST_MANAGE_BASE is not configured")
    if not token:
        raise RuntimeError("VR_MANAGE_TOKEN is not configured")
    data = json.dumps(payload).encode()
    req = urllib.request.Request(base + path, data=data, method="POST",
                                 headers={"Authorization": "Bearer " + token,
                                          "Content-Type": "application/json"})
    try:
        with urllib.request.urlopen(req, timeout=8) as response:
            result = json.loads(response.read())
    except urllib.error.HTTPError as exc:
        try:
            detail = json.loads(exc.read()).get("error")
        except Exception:
            detail = None
        raise RuntimeError(detail or f"management API returned HTTP {exc.code}") from exc
    refused = result.get("refused") or []
    if refused:
        raise RuntimeError(f"management API refused the device: {refused}")
    return result


def camera_slot_assign(slot: str, boat: str) -> dict:
    """Assign a boat to an existing OBS Boat cam slot without switching the program scene.

    Use this when the operator says, for example, "put boat 2 on Boat cam B". ``slot`` is the
    single slot letter and ``boat`` may be the current race's sail number or device ID.
    """
    slot = str(slot).strip().upper()
    if len(slot) != 1 or not ("A" <= slot <= "Z"):
        raise ValueError("slot must be one letter, for example B")
    # Resolve the operator's sail-number shorthand first. This call is intentionally made before
    # device control: only the producer knows which device currently represents that boat.
    resolved = _call("/control/camera-slot", {"boat": boat, "resolveOnly": True})
    device_id = resolved["deviceId"]
    wake = _manage_call("/v3/manage/devices/sleep",
                        {"deviceIds": [device_id], "asleep": False})
    camera = _manage_call("/v3/manage/devices/camera",
                          {"deviceIds": [device_id], "on": True, "streaming": True})
    assignment = _call("/control/camera-slot", {"slot": slot, "boat": boat})
    return {**assignment, "wake": wake, "camera": camera,
            "picture": "assigned; waiting for the device's config poll and first live clip"}


def camera_slot_status() -> dict:
    """Show which current-race boat is assigned to every Boat cam slot."""
    return _call("/control/camera-slot")


def register(mcp: FastMCP, *, control: bool | None = None) -> None:
    if control is None:
        control = control_enabled()
    if control:
        mcp.tool()(camera_slot_status)
        mcp.tool()(camera_slot_assign)


def main() -> None:
    mcp = FastMCP("race-producer")
    register(mcp)
    mcp.run()


if __name__ == "__main__":
    main()
