use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, PartialOrd, Serialize)]
#[serde(from = "u8")]
#[repr(u8)]
pub enum UserRating {
    Appalling = 1,
    Horrible = 2,
    VeryBad = 3,
    Bad = 4,
    Average = 5,
    Fine = 6,
    Good = 7,
    VeryGood = 8,
    Great = 9,
    Masterpiece = 10,
    #[serde(other)]
    NoRating = 0,
}

impl Default for UserRating {
    fn default() -> Self {
        0u8.into()
    }
}

impl std::fmt::Display for UserRating {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str(match self {
            Self::Appalling => "Appalling",
            Self::Horrible => "Horrible",
            Self::VeryBad => "Very bad",
            Self::Bad => "Bad",
            Self::Average => "Average",
            Self::Fine => "Fine",
            Self::Good => "Good",
            Self::VeryGood => "Very good",
            Self::Great => "Great",
            Self::Masterpiece => "Masterpiece",
            Self::NoRating => "No Rating",
        })
    }
}

impl From<u8> for UserRating {
    fn from(value: u8) -> Self {
        match value {
            1 => Self::Appalling,
            2 => Self::Horrible,
            3 => Self::VeryBad,
            4 => Self::Bad,
            5 => Self::Average,
            6 => Self::Fine,
            7 => Self::Good,
            8 => Self::VeryGood,
            9 => Self::Great,
            10 => Self::Masterpiece,
            _ => Self::NoRating,
        }
    }
}

impl From<u16> for UserRating {
    fn from(value: u16) -> Self {
        match value {
            1 => Self::Appalling,
            2 => Self::Horrible,
            3 => Self::VeryBad,
            4 => Self::Bad,
            5 => Self::Average,
            6 => Self::Fine,
            7 => Self::Good,
            8 => Self::VeryGood,
            9 => Self::Great,
            10 => Self::Masterpiece,
            _ => Self::NoRating,
        }
    }
}

impl From<u32> for UserRating {
    fn from(value: u32) -> Self {
        match value {
            1 => Self::Appalling,
            2 => Self::Horrible,
            3 => Self::VeryBad,
            4 => Self::Bad,
            5 => Self::Average,
            6 => Self::Fine,
            7 => Self::Good,
            8 => Self::VeryGood,
            9 => Self::Great,
            10 => Self::Masterpiece,
            _ => Self::NoRating,
        }
    }
}

impl From<u64> for UserRating {
    fn from(value: u64) -> Self {
        match value {
            1 => Self::Appalling,
            2 => Self::Horrible,
            3 => Self::VeryBad,
            4 => Self::Bad,
            5 => Self::Average,
            6 => Self::Fine,
            7 => Self::Good,
            8 => Self::VeryGood,
            9 => Self::Great,
            10 => Self::Masterpiece,
            _ => Self::NoRating,
        }
    }
}

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

    #[test]
    fn user_rating_produces_no_rating_from_default() {
        let user_rating = UserRating::default();
        assert_eq!(user_rating, UserRating::NoRating);
    }

    #[test]
    fn user_rating_produces_no_rating_from_0() {
        let user_rating = UserRating::from(0u8);
        assert_eq!(user_rating, UserRating::NoRating);
    }

    #[test]
    fn user_rating_produces_average_from_5() {
        let user_rating = UserRating::from(5u8);
        assert_eq!(user_rating, UserRating::Average);
    }
}
