use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde_json::Value;

pub fn action_response(action: &str, result: &Value) -> Response {
    let result_xml = value_to_xml(result);
    let body = format!(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
         <{action}Response xmlns=\"http://ec2.amazonaws.com/doc/2016-11-15/\">\
         <{action}Result>{result_xml}</{action}Result>\
         <RequestId>00000000-0000-0000-0000-000000000000</RequestId>\
         </{action}Response>"
    );
    ([("content-type", "application/xml")], body).into_response()
}

pub fn list_response(
    action: &str,
    _service: &str,
    _resource_type: &str,
    items: &[Value],
) -> Response {
    let items_xml: String = items
        .iter()
        .map(|v| format!("<item>{}</item>", value_to_xml(v)))
        .collect();
    let body = format!(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
         <{action}Response>\
         <{action}Result><items>{items_xml}</items></{action}Result>\
         <RequestId>00000000-0000-0000-0000-000000000000</RequestId>\
         </{action}Response>"
    );
    ([("content-type", "application/xml")], body).into_response()
}

pub fn error_response(status: StatusCode, code: &str, message: &str) -> Response {
    let body = format!(
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
         <ErrorResponse><Error><Code>{code}</Code><Message>{}</Message></Error>\
         <RequestId>00000000-0000-0000-0000-000000000000</RequestId></ErrorResponse>",
        xml_escape(message),
    );
    (status, [("content-type", "application/xml")], body).into_response()
}

fn value_to_xml(v: &Value) -> String {
    match v {
        Value::Object(map) => map
            .iter()
            .map(|(k, v)| format!("<{k}>{}</{k}>", value_to_xml(v)))
            .collect(),
        Value::Array(arr) => arr
            .iter()
            .map(|v| format!("<member>{}</member>", value_to_xml(v)))
            .collect(),
        Value::String(s) => xml_escape(s),
        Value::Number(n) => n.to_string(),
        Value::Bool(b) => b.to_string(),
        Value::Null => String::new(),
    }
}

fn xml_escape(s: &str) -> String {
    s.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
}
