/// Plural forms — handles language-specific plurality rules.
///
/// Different languages have very different plural forms. English has two
/// (one / other). Russian has four. Arabic has six. This module maps counts
/// to the correct form for a given locale.

/// Named plural categories per Unicode CLDR.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PluralForm {
    /// Exactly zero (some languages have a special zero form).
    Zero,
    /// Exactly one.
    One,
    /// Small numbers (e.g. 2–4 in Slavic languages).
    Two,
    /// Few (language-specific).
    Few,
    /// Many (language-specific).
    Many,
    /// The catch-all / default.
    Other,
}

/// Determine the plural form for a count in a given language.
///
/// Rules are a simplified implementation of CLDR plural rules for the most
/// common languages. The full CLDR spec covers hundreds of languages; these
/// cover the major cases.
pub fn plural_form(language: &str, count: i64) -> PluralForm {
    let n = count.unsigned_abs(); // absolute value for matching

    match language {
        // English and most Western European languages: one / other
        "en" | "de" | "nl" | "sv" | "da" | "no" | "nb" | "nn" | "fi"
        | "et" | "hu" | "tr" | "pt" | "it" | "es" | "ca" | "el" | "id"
        | "ms" | "th" | "zh" | "ja" | "ko" | "vi" | "ur" => {
            if n == 1 { PluralForm::One } else { PluralForm::Other }
        }

        // French: one for 0 and 1, other for rest
        "fr" => {
            if n <= 1 { PluralForm::One } else { PluralForm::Other }
        }

        // Russian, Ukrainian, Belarusian: complex Slavic rules
        "ru" | "uk" | "be" => {
            let n10 = n % 10;
            let n100 = n % 100;
            if n10 == 1 && n100 != 11 {
                PluralForm::One
            } else if (2..=4).contains(&n10) && !(12..=14).contains(&n100) {
                PluralForm::Few
            } else {
                PluralForm::Many
            }
        }

        // Polish: similar Slavic rules
        "pl" => {
            let n10 = n % 10;
            let n100 = n % 100;
            if n == 1 {
                PluralForm::One
            } else if (2..=4).contains(&n10) && !(12..=14).contains(&n100) {
                PluralForm::Few
            } else {
                PluralForm::Many
            }
        }

        // Czech, Slovak
        "cs" | "sk" => {
            if n == 1 {
                PluralForm::One
            } else if (2..=4).contains(&n) {
                PluralForm::Few
            } else {
                PluralForm::Other
            }
        }

        // Arabic: 6 forms
        "ar" => {
            let n100 = n % 100;
            if n == 0 {
                PluralForm::Zero
            } else if n == 1 {
                PluralForm::One
            } else if n == 2 {
                PluralForm::Two
            } else if (3..=10).contains(&n100) {
                PluralForm::Few
            } else if (11..=99).contains(&n100) {
                PluralForm::Many
            } else {
                PluralForm::Other
            }
        }

        // Hebrew
        "he" | "iw" => {
            if n == 1 {
                PluralForm::One
            } else if n == 2 {
                PluralForm::Two
            } else if n >= 11 && n % 10 == 0 {
                PluralForm::Many
            } else {
                PluralForm::Other
            }
        }

        // Default: one / other
        _ => {
            if n == 1 { PluralForm::One } else { PluralForm::Other }
        }
    }
}

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

    #[test]
    fn english_one() {
        assert_eq!(plural_form("en", 1), PluralForm::One);
    }

    #[test]
    fn english_other() {
        assert_eq!(plural_form("en", 0), PluralForm::Other);
        assert_eq!(plural_form("en", 2), PluralForm::Other);
        assert_eq!(plural_form("en", 100), PluralForm::Other);
    }

    #[test]
    fn french_zero_is_one() {
        assert_eq!(plural_form("fr", 0), PluralForm::One);
        assert_eq!(plural_form("fr", 1), PluralForm::One);
        assert_eq!(plural_form("fr", 2), PluralForm::Other);
    }

    #[test]
    fn russian_one() {
        assert_eq!(plural_form("ru", 1), PluralForm::One);
        assert_eq!(plural_form("ru", 21), PluralForm::One);
        assert_eq!(plural_form("ru", 101), PluralForm::One);
    }

    #[test]
    fn russian_few() {
        assert_eq!(plural_form("ru", 2), PluralForm::Few);
        assert_eq!(plural_form("ru", 3), PluralForm::Few);
        assert_eq!(plural_form("ru", 22), PluralForm::Few);
    }

    #[test]
    fn russian_many() {
        assert_eq!(plural_form("ru", 5), PluralForm::Many);
        assert_eq!(plural_form("ru", 11), PluralForm::Many);
        assert_eq!(plural_form("ru", 20), PluralForm::Many);
    }

    #[test]
    fn arabic_zero() {
        assert_eq!(plural_form("ar", 0), PluralForm::Zero);
    }

    #[test]
    fn arabic_two() {
        assert_eq!(plural_form("ar", 2), PluralForm::Two);
    }

    #[test]
    fn arabic_few() {
        assert_eq!(plural_form("ar", 5), PluralForm::Few);
        assert_eq!(plural_form("ar", 10), PluralForm::Few);
    }

    #[test]
    fn arabic_many() {
        assert_eq!(plural_form("ar", 15), PluralForm::Many);
    }

    #[test]
    fn hebrew_two() {
        assert_eq!(plural_form("he", 2), PluralForm::Two);
    }
}
