use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, PartialOrd, Serialize)]
#[serde(from = "u8")]
#[repr(u8)]
pub enum UserLevel {
    Banned = 0,
    Validating = 2,
    Member = 3,
    Contributor = 4,
    GroupLeader = 5,
    PowerUploader = 6,
    VIP = 9,
    PublicRelations = 10,
    ForumModerator = 11,
    Moderator = 12,
    Developer = 15,
    Administrator = 16,
    Administrator2 = 20,
    #[serde(other)]
    Guest = 1,
}

impl Default for UserLevel {
    fn default() -> Self {
        1u8.into()
    }
}

impl std::fmt::Display for UserLevel {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        fmt.write_str(match self {
            Self::Banned => "Banned",
            Self::Guest => "Guest",
            Self::Validating => "Validating",
            Self::Member => "Member",
            Self::Contributor => "Contributor",
            Self::GroupLeader => "Group Leader",
            Self::PowerUploader => "Power Uploader",
            Self::VIP => "VIP",
            Self::PublicRelations => "Public Relations",
            Self::ForumModerator => "Forum Moderator",
            Self::Moderator => "Moderator",
            Self::Developer => "Developer",
            Self::Administrator => "Administrator",
            Self::Administrator2 => "Administrator 2",
        })
    }
}

impl From<u8> for UserLevel {
    fn from(value: u8) -> Self {
        match value {
            0 => Self::Banned,
            1 => Self::Guest,
            2 => Self::Validating,
            3 => Self::Member,
            4 => Self::Contributor,
            5 => Self::GroupLeader,
            6 => Self::PowerUploader,
            9 => Self::VIP,
            10 => Self::PublicRelations,
            11 => Self::ForumModerator,
            12 => Self::Moderator,
            15 => Self::Developer,
            16 => Self::Administrator,
            20 => Self::Administrator2,
            _ => Self::Guest,
        }
    }
}

impl From<u16> for UserLevel {
    fn from(value: u16) -> Self {
        match value {
            0 => Self::Banned,
            1 => Self::Guest,
            2 => Self::Validating,
            3 => Self::Member,
            4 => Self::Contributor,
            5 => Self::GroupLeader,
            6 => Self::PowerUploader,
            9 => Self::VIP,
            10 => Self::PublicRelations,
            11 => Self::ForumModerator,
            12 => Self::Moderator,
            15 => Self::Developer,
            16 => Self::Administrator,
            20 => Self::Administrator2,
            _ => Self::Guest,
        }
    }
}

impl From<u32> for UserLevel {
    fn from(value: u32) -> Self {
        match value {
            0 => Self::Banned,
            1 => Self::Guest,
            2 => Self::Validating,
            3 => Self::Member,
            4 => Self::Contributor,
            5 => Self::GroupLeader,
            6 => Self::PowerUploader,
            9 => Self::VIP,
            10 => Self::PublicRelations,
            11 => Self::ForumModerator,
            12 => Self::Moderator,
            15 => Self::Developer,
            16 => Self::Administrator,
            20 => Self::Administrator2,
            _ => Self::Guest,
        }
    }
}

impl From<u64> for UserLevel {
    fn from(value: u64) -> Self {
        match value {
            0 => Self::Banned,
            1 => Self::Guest,
            2 => Self::Validating,
            3 => Self::Member,
            4 => Self::Contributor,
            5 => Self::GroupLeader,
            6 => Self::PowerUploader,
            9 => Self::VIP,
            10 => Self::PublicRelations,
            11 => Self::ForumModerator,
            12 => Self::Moderator,
            15 => Self::Developer,
            16 => Self::Administrator,
            20 => Self::Administrator2,
            _ => Self::Guest,
        }
    }
}

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

    #[test]
    fn user_level_produces_guest_from_default() {
        let user_level = UserLevel::default();
        assert_eq!(user_level, UserLevel::Guest);
    }

    #[test]
    fn user_level_produces_banned_from_0() {
        let user_level = UserLevel::from(0u8);
        assert_eq!(user_level, UserLevel::Banned);
    }

    #[test]
    fn user_level_produces_guest_from_21() {
        let user_level = UserLevel::from(21u8);
        assert_eq!(user_level, UserLevel::Guest);
    }
}
