//! Pure helpers the writer task uses to shape outbound requests.
//!
//! Pulled out of the main streamable-HTTP module so the parent file
//! stays under the module-size cap. None of these helpers touch the
//! HTTP client or the shared session-id table; they operate on a
//! borrowed message + transport mode + optional stateless `_meta`
//! and return either the wire bytes or a header-friendly string.

use serde_json::Value;

use super::{SharedState, TransportMode};
use crate::protocol::JsonRpcMessage;

/// Pull the target name out of a JSON-RPC request's params for the
/// SEP-2243 `Mcp-Name` header. Returns `Some` for the three
/// name-bearing methods (`tools/call`, `resources/read`,
/// `prompts/get`) and `None` for everything else, including
/// notifications and server-initiated requests we never originate.
pub(super) fn name_from_params<'a>(method: &str, params: Option<&'a Value>) -> Option<&'a str> {
    let params = params?;
    match method {
        "tools/call" => params.get("name").and_then(Value::as_str),
        "resources/read" => params.get("uri").and_then(Value::as_str),
        "prompts/get" => params.get("name").and_then(Value::as_str),
        _ => None,
    }
}

/// Serialize the outbound message, splicing `_meta` into `params` when
/// the transport is in stateless mode. Returns the byte buffer the
/// signer + reqwest sees, so a future regression in the splice cannot
/// quietly desync the signature from the wire shape.
pub(super) fn build_outbound_body(msg: &JsonRpcMessage, shared: &SharedState) -> Vec<u8> {
    match (&shared.mode, &shared.stateless_meta) {
        (TransportMode::Stateless, Some(meta)) => {
            let value = match serde_json::to_value(msg) {
                Ok(v) => splice_meta(v, meta),
                Err(_) => return Vec::new(),
            };
            serde_json::to_vec(&value).unwrap_or_default()
        }
        _ => serde_json::to_vec(msg).unwrap_or_default(),
    }
}

/// Splice the `_meta` value into the request's `params` object. When
/// `params` is missing or not an object, the outbound shape is the
/// original envelope unchanged so we never lie about the wire form;
/// SEP-2575 says `_meta` rides on a `params` object, not a scalar.
pub(super) fn splice_meta(mut envelope: Value, meta: &Value) -> Value {
    let Some(params) = envelope.get_mut("params") else {
        return envelope;
    };
    let Some(map) = params.as_object_mut() else {
        return envelope;
    };
    map.insert("_meta".to_string(), meta.clone());
    envelope
}
