use runx_contracts::sha256_prefixed;
use runx_contracts::tools::{JsonPayload, JsonPayloadObject};

pub(crate) fn sha256_stable(value: &JsonPayload) -> String {
    sha256_prefixed(stable_stringify(value).as_bytes())
}

pub(crate) fn stable_stringify(value: &JsonPayload) -> String {
    match value {
        JsonPayload::Null => "null".to_owned(),
        JsonPayload::Bool(value) => value.to_string(),
        JsonPayload::Number(value) => value.to_string(),
        JsonPayload::String(value) => json_string(value),
        JsonPayload::Array(values) => format!(
            "[{}]",
            values
                .iter()
                .map(stable_stringify)
                .collect::<Vec<_>>()
                .join(",")
        ),
        JsonPayload::Object(values) => stable_object(values),
    }
}

fn stable_object(values: &JsonPayloadObject) -> String {
    let entries = values
        .iter()
        .map(|(key, value)| format!("{}:{}", json_string(key), stable_stringify(value)))
        .collect::<Vec<_>>()
        .join(",");
    format!("{{{entries}}}")
}

fn json_string(value: &str) -> String {
    serde_json::to_string(value).unwrap_or_else(|_| "\"\"".to_owned())
}
