//! Region codes — `extensions/rung-price.md` §2, the WAI family's one definition of a region
//! code, its country and *holds*.
//!
//! A region code is an ISO 3166-1 alpha-2 code (two capitals `A`–`Z`) or an ISO 3166-2 code (an
//! alpha-2 code, `-`, then 1 to 3 capitals or digits), written in capitals as WAI writes ISO 3166
//! codes elsewhere. Its **country** is its first two characters. Whether ISO assigns a code is
//! not checked, and codes are compared byte for byte. A list of codes **holds** a code *r* when it
//! lists *r*, or when *r* is a subdivision code and the list names *r*'s country; it never holds a
//! country because it lists a subdivision of it.
//!
//! CDNI writes the same codes in lowercase (RFC 8006 §4.3.8 `countrycode`, RFC 9388 §2.1.1.1
//! `subdivisioncode`): [`to_cdni`] and [`from_cdni`] change the case of the letters, exactly.
//!
//! Three pure functions with no dependency, behind no feature, so that every extension that
//! states a region uses this one module rather than a copy.

/// §2: whether `s` is a region code, in capitals.
pub fn code(s: &[u8]) -> bool {
    let alpha2 = |c: &[u8]| c.len() == 2 && c.iter().all(u8::is_ascii_uppercase);
    match s.len() {
        2 => alpha2(s),
        4..=6 => alpha2(&s[..2]) && s[2] == b'-' && s[3..].iter().all(|c| c.is_ascii_uppercase() || c.is_ascii_digit()),
        _ => false,
    }
}

/// Whether `s` is a subdivision code (ISO 3166-2), given that it is a region code.
pub fn subdivision(s: &[u8]) -> bool {
    s.len() > 2
}

/// A code's country: its first two bytes (all of `s` when it is shorter).
pub fn country(s: &[u8]) -> &[u8] {
    &s[..s.len().min(2)]
}

/// §2 *Holds*: whether `listed`, in strictly ascending order of its bytes (as every footprint
/// is), lists `r`, or lists `r`'s country when `r` is a subdivision code.
pub fn holds<S: AsRef<[u8]>>(listed: &[S], r: &[u8]) -> bool {
    let has = |x: &[u8]| listed.binary_search_by(|l| l.as_ref().cmp(x)).is_ok();
    has(r) || (subdivision(r) && has(country(r)))
}

/// A code in CDNI's lowercase form.
pub fn to_cdni(s: &str) -> String {
    s.to_ascii_lowercase()
}

/// A CDNI code in WAI's capitals; `None` when the result is not a region code.
pub fn from_cdni(s: &str) -> Option<String> {
    let c = s.to_ascii_uppercase();
    (code(c.as_bytes()) && to_cdni(&c) == s).then_some(c)
}

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

    #[test]
    fn the_grammar() {
        for ok in ["XA", "XB-1", "XB-ABC", "XB-1A2", "US", "US-NY"] {
            assert!(code(ok.as_bytes()), "{ok}");
        }
        for bad in ["", "X", "xb", "XBB", "XB-", "XB-ABCD", "XB_1", "XB-a", "X1", "XB-1-", "XB1"] {
            assert!(!code(bad.as_bytes()), "{bad}");
        }
        assert_eq!(country(b"XB-1"), b"XB");
        assert_eq!(country(b"XB"), b"XB");
    }

    #[test]
    fn a_footprint_holds_a_code_or_its_country_never_upward() {
        let f = ["XA", "XB"];
        assert!(holds(&f, b"XB"));
        assert!(holds(&f, b"XB-1"), "a subdivision, held by its country");
        assert!(!holds(&f, b"XC"));
        assert!(!holds(&f, b"XC-1"));
        let sub = vec![b"XB-1".to_vec()];
        assert!(holds(&sub, b"XB-1"));
        assert!(!holds(&sub, b"XB"), "a country is never held by its subdivision");
        assert!(!holds(&sub, b"XB-2"));
    }

    #[test]
    fn the_cdni_case_mapping_is_exact() {
        assert_eq!(to_cdni("XB-1A"), "xb-1a");
        assert_eq!(from_cdni("xb-1a").as_deref(), Some("XB-1A"));
        assert_eq!(from_cdni("us-ny").as_deref(), Some("US-NY"));
        assert_eq!(from_cdni("XB"), None, "CDNI writes lowercase");
        assert_eq!(from_cdni("usa"), None);
    }
}
