use serde::{Deserialize, Serialize};

/// "Sort by" types for manga results pages such as the manga search or latest manga updates pages.
#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, PartialOrd, Serialize)]
#[serde(from = "u8")]
#[repr(u8)]
pub enum MangaResultsSortBy {
    LastUpdatedAscending = 0,
    LastUpdatedDescending = 1,
    TitleAscending = 2,
    TitleDescending = 3,
    CommentsAscending = 4,
    CommentsDescending = 5,
    RatingAscending = 6,
    RatingDescending = 7,
    ViewsAscending = 8,
    ViewsDescending = 9,
    FollowsAscending = 10,
    FollowsDescending = 11,
}

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

impl std::fmt::Display for MangaResultsSortBy {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str(match self {
            Self::LastUpdatedAscending => "Last updated ▲",
            Self::LastUpdatedDescending => "Last updated ▼",
            Self::TitleAscending => "Title ▲",
            Self::TitleDescending => "Title ▼",
            Self::CommentsAscending => "Comments ▲",
            Self::CommentsDescending => "Comments ▼",
            Self::RatingAscending => "Rating ▲",
            Self::RatingDescending => "Rating ▼",
            Self::ViewsAscending => "Views ▲",
            Self::ViewsDescending => "Views ▼",
            Self::FollowsAscending => "Follows ▲",
            Self::FollowsDescending => "Follows ▼",
        })
    }
}

impl From<u8> for MangaResultsSortBy {
    fn from(value: u8) -> Self {
        match value {
            0 => Self::LastUpdatedAscending,
            1 => Self::LastUpdatedDescending,
            2 => Self::TitleAscending,
            3 => Self::TitleDescending,
            4 => Self::CommentsAscending,
            5 => Self::CommentsDescending,
            6 => Self::RatingAscending,
            7 => Self::RatingDescending,
            8 => Self::ViewsAscending,
            9 => Self::ViewsDescending,
            10 => Self::FollowsAscending,
            11 => Self::FollowsDescending,
            _ => Self::LastUpdatedAscending,
        }
    }
}

impl From<u16> for MangaResultsSortBy {
    fn from(value: u16) -> Self {
        match value {
            0 => Self::LastUpdatedAscending,
            1 => Self::LastUpdatedDescending,
            2 => Self::TitleAscending,
            3 => Self::TitleDescending,
            4 => Self::CommentsAscending,
            5 => Self::CommentsDescending,
            6 => Self::RatingAscending,
            7 => Self::RatingDescending,
            8 => Self::ViewsAscending,
            9 => Self::ViewsDescending,
            10 => Self::FollowsAscending,
            11 => Self::FollowsDescending,
            _ => Self::LastUpdatedAscending,
        }
    }
}

impl From<u32> for MangaResultsSortBy {
    fn from(value: u32) -> Self {
        match value {
            0 => Self::LastUpdatedAscending,
            1 => Self::LastUpdatedDescending,
            2 => Self::TitleAscending,
            3 => Self::TitleDescending,
            4 => Self::CommentsAscending,
            5 => Self::CommentsDescending,
            6 => Self::RatingAscending,
            7 => Self::RatingDescending,
            8 => Self::ViewsAscending,
            9 => Self::ViewsDescending,
            10 => Self::FollowsAscending,
            11 => Self::FollowsDescending,
            _ => Self::LastUpdatedAscending,
        }
    }
}

impl From<u64> for MangaResultsSortBy {
    fn from(value: u64) -> Self {
        match value {
            0 => Self::LastUpdatedAscending,
            1 => Self::LastUpdatedDescending,
            2 => Self::TitleAscending,
            3 => Self::TitleDescending,
            4 => Self::CommentsAscending,
            5 => Self::CommentsDescending,
            6 => Self::RatingAscending,
            7 => Self::RatingDescending,
            8 => Self::ViewsAscending,
            9 => Self::ViewsDescending,
            10 => Self::FollowsAscending,
            11 => Self::FollowsDescending,
            _ => Self::LastUpdatedAscending,
        }
    }
}

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

    #[test]
    fn sort_by_produces_last_updated_ascending_from_default() {
        let sort_by = MangaResultsSortBy::default();
        assert_eq!(sort_by, MangaResultsSortBy::LastUpdatedAscending);
    }

    #[test]
    fn sort_by_produces_last_updated_descending_from_1() {
        let sort_by = MangaResultsSortBy::from(1u8);
        assert_eq!(sort_by, MangaResultsSortBy::LastUpdatedDescending);
    }

    #[test]
    fn sort_by_produces_last_updated_ascending_from_21() {
        let sort_by = MangaResultsSortBy::from(21u8);
        assert_eq!(sort_by, MangaResultsSortBy::LastUpdatedAscending);
    }
}
