#[cfg(any(test, feature = "async", feature = "blocking"))]
use http::header::HeaderName;

#[cfg(any(test, feature = "async", feature = "blocking"))]
use crate::error::Error;

pub(crate) fn redact_value(value: &str) -> String {
    let value = value.trim();
    if value.is_empty() {
        return "<redacted>".to_string();
    }

    let chars = value.chars().collect::<Vec<_>>();
    if chars.len() <= 12 {
        return "<redacted>".to_string();
    }

    let head = chars.iter().take(2).collect::<String>();
    let tail = chars
        .iter()
        .rev()
        .take(2)
        .copied()
        .collect::<Vec<_>>()
        .into_iter()
        .rev()
        .collect::<String>();

    format!("{head}...{tail}")
}

#[cfg(any(test, feature = "async", feature = "blocking"))]
pub(crate) fn metadata_header_name(value: &str) -> Result<HeaderName, Error> {
    let value = value.trim();
    if value.is_empty() {
        return Err(Error::invalid_config("metadata key must not be empty"));
    }

    let mut name = String::with_capacity("x-amz-meta-".len() + value.len());
    name.push_str("x-amz-meta-");
    name.push_str(&value.to_ascii_lowercase());

    HeaderName::from_bytes(name.as_bytes())
        .map_err(|_| Error::invalid_config("invalid metadata key for x-amz-meta-* header"))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn redacts_values_consistently() {
        assert_eq!(redact_value(""), "<redacted>");
        assert_eq!(redact_value("   "), "<redacted>");
        assert_eq!(redact_value("12345678"), "<redacted>");
        assert_eq!(redact_value("123456789"), "<redacted>");
        assert_eq!(redact_value("123456789012"), "<redacted>");
        assert_eq!(redact_value("1234567890123"), "12...23");
    }

    #[test]
    fn builds_metadata_header_name() {
        let name = metadata_header_name(" Foo ").unwrap();
        assert_eq!(name.as_str(), "x-amz-meta-foo");

        assert!(metadata_header_name("").is_err());
        assert!(metadata_header_name("a b").is_err());
    }
}
