import json
import uuid
from r_bridge.exceptions import RProtocolError

SENTINEL = "__RBRIDGE__:"
_REQUIRED_RESPONSE_FIELDS = {"id", "status", "payload", "warnings"}


def make_request(op: str, payload: dict) -> str:
    """Return a sentinel-prefixed, newline-terminated JSON request line."""
    envelope = {"id": str(uuid.uuid4()), "op": op, "payload": payload}
    return SENTINEL + json.dumps(envelope) + "\n"


def strip_sentinel(line: str) -> str | None:
    """Return the JSON body if line is a protocol line, else None."""
    line = line.rstrip("\n")
    if not line.startswith(SENTINEL):
        return None
    return line[len(SENTINEL):]


def parse_response(line: str) -> dict:
    """Parse a sentinel-prefixed response line into a dict.

    Raises RProtocolError on any malformation.
    """
    body = strip_sentinel(line)
    if body is None:
        raise RProtocolError(f"Response line missing sentinel prefix: {line!r}")
    try:
        obj = json.loads(body)
    except json.JSONDecodeError as exc:
        raise RProtocolError(f"Response is not valid JSON: {exc}") from exc
    missing = _REQUIRED_RESPONSE_FIELDS - obj.keys()
    if missing:
        raise RProtocolError(f"Response missing fields: {missing}")
    return obj
