"""
Broadcast director MCP server  —  ONE agent, the whole broadcast (pixels + audience).

Merges the two seams into a single tool set so an LLM director can run the show end to end:
  * OBS side (obs_mcp_server): scenes, the broadcast Browser source, RTMP + local recording, and OBS's
    own upload health (bitrate / dropped frames / congestion).
  * YouTube side (youtube_mcp_server): concurrent viewers, YouTube's ingest-health verdict, the
    broadcast title/description/privacy, and the lifecycle transition (testing → live → complete).

The two are complementary, and holding both lets the agent reason across them — e.g. "OBS says the
upload is healthy but YouTube ingest is 'noData' → the RTMP binding is wrong", or the correct go-live
order: OBS start_stream (pixels flowing) → confirm ingest good → youtube_go_live (public).

Adds ONE fused tool of its own — `broadcast_health`, a single glanceable OBS+YouTube status with alert
flags for a solo operator — and otherwise registers both modules' tools on one FastMCP. Each module still
runs standalone (obs_mcp_server / youtube_mcp_server) for a narrower agent. All config + one-time YouTube
OAuth are as documented in those modules (OBS_WS_* and YOUTUBE_* env; run the YouTube `auth` step via
`python -m v3.broadcast.live.youtube_mcp_server auth`).

**Human-in-control (TRWC workflow).** Per docs/plans/trwc-obs-production-workflow.md the operator (Johan)
owns the switch box, so this server is **read-only by default**: the monitoring tools (obs_status,
stream_health, youtube_stream_stats, broadcast_health, …) are always on, but the ACTUATING tools
(switch_scene, start/stop_stream, youtube_go_live, …) are only registered when `BROADCAST_ALLOW_CONTROL`
is set — i.e. on a VR-owned / unmanned box (the 2 Aug rehearsal, walk-test, GSYS demo), never silently on
the operator's switcher. So an agent can safely sit beside Johan as a monitor/co-pilot and only take the
controls when explicitly handed them.

Run (stdio):
    .venv-demo/Scripts/python -m v3.broadcast.live.broadcast_mcp_server           # monitor only
    BROADCAST_ALLOW_CONTROL=1 .venv-demo/Scripts/python -m v3...broadcast_mcp_server  # + controls
Deps: `pip install obsws-python google-api-python-client google-auth-oauthlib "mcp[cli]"`.

Register with an MCP client (Claude Code):
    claude mcp add trwc-broadcast -- <python> -m v3.broadcast.live.broadcast_mcp_server
"""
from __future__ import annotations

import sys
from pathlib import Path

from mcp.server.fastmcp import FastMCP

sys.path.insert(0, str(Path(__file__).resolve().parent))   # so the sibling servers resolve either way
import obs_mcp_server  # noqa: E402
import youtube_mcp_server  # noqa: E402


def broadcast_health() -> dict:
    """ONE glanceable broadcast status for a solo operator — fuses OBS's upload health with YouTube's
    ingest/audience so you don't watch three dashboards at once, and raises `alerts` for anything that
    needs a human's eyes. Read-only, always available. Degrades gracefully: if YouTube isn't authorised
    or there's no live broadcast, it just reports that and still returns OBS health.

    Cross-check it encodes: OBS upload healthy BUT YouTube ingest 'noData'/'bad' → the RTMP binding is
    wrong (pixels leaving OBS aren't reaching this YouTube broadcast), not a network problem."""
    out: dict = {"alerts": []}
    try:
        oh = obs_mcp_server.stream_health()
        out["obs"] = oh
        if oh.get("streaming"):
            if oh.get("droppedFramesPct", 0) > 1:
                out["alerts"].append(f"OBS dropping {oh['droppedFramesPct']}% frames (network to ingest)")
            if oh.get("congestion", 0) > 0.3:
                out["alerts"].append(f"OBS network congestion {oh['congestion']}")
        if oh.get("renderSkippedFrames", 0) > 0 or oh.get("cpuUsagePct", 0) > 85:
            out["alerts"].append(
                f"encode strain (cpu {oh.get('cpuUsagePct')}%, skipped {oh.get('renderSkippedFrames')})")
    except Exception as e:
        out["obs"] = {"error": str(e)}
        out["alerts"].append(f"OBS unreachable: {e}")
    try:
        ys = youtube_mcp_server.youtube_stream_stats()
        out["youtube"] = ys
        if ys.get("ingestHealth") in ("bad", "noData"):
            out["alerts"].append(f"YouTube ingest {ys['ingestHealth']}"
                                 + (" while OBS upload looks fine -> check the RTMP binding"
                                    if out.get("obs", {}).get("streaming") else ""))
    except Exception as e:
        out["youtube"] = {"status": "unavailable (not authorised / no live broadcast)",
                          "detail": str(e)[:140]}
    out["ok"] = not out["alerts"]
    return out


def build() -> FastMCP:
    """One FastMCP carrying the OBS + YouTube tools plus the fused broadcast_health monitor. Read tools
    (and broadcast_health) are always registered; the actuating write tools only when the modules' control
    gate (BROADCAST_ALLOW_CONTROL) is on. Tool names are distinct across modules, so no collision."""
    mcp = FastMCP("trwc-broadcast")
    obs_mcp_server.register(mcp)                 # each honours BROADCAST_ALLOW_CONTROL for its write tools
    youtube_mcp_server.register(mcp)
    mcp.tool()(broadcast_health)                 # the co-pilot's single status glance — always on
    return mcp


def main() -> None:
    build().run()  # stdio transport (the MCP client owns the process lifecycle)


if __name__ == "__main__":
    main()
