use serde_json::{Map, Value, json};

use crate::creds::Credentials;
use crate::error::Error;
use crate::protocol::json;
use crate::region::Region;

const SERVICE: &str = "ssm";

#[derive(Debug, Clone)]
pub struct StartSessionOutput {
    pub session_id: String,
    pub stream_url: String,
    pub token_value: String,
}

pub fn start_session(
    creds: &Credentials,
    region: &Region,
    target: &str,
    document_name: &str,
    parameters: &[(&str, Vec<String>)],
) -> Result<StartSessionOutput, Error> {
    let mut params = Map::new();
    for (name, values) in parameters {
        params.insert(
            (*name).to_string(),
            Value::Array(values.iter().map(|v| Value::String(v.clone())).collect()),
        );
    }

    let body = json!({
        "Target": target,
        "DocumentName": document_name,
        "Parameters": Value::Object(params),
    });
    let resp = json::call(creds, region, SERVICE, "AmazonSSM.StartSession", &body)?;

    Ok(StartSessionOutput {
        session_id: field(&resp, "SessionId")?,
        stream_url: field(&resp, "StreamUrl")?,
        token_value: field(&resp, "TokenValue")?,
    })
}

fn field(v: &Value, key: &str) -> Result<String, Error> {
    v.get(key)
        .and_then(Value::as_str)
        .map(str::to_string)
        .ok_or_else(|| Error::parse(format!("StartSession response missing `{key}`")))
}
