mod impls;

#[cfg(test)]
mod tests;

use core::num::NonZeroU8;

use crate::error::{ParseCardError, ParseRankError, ParseSuitError};

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Suit {
    Club = 0b0000_0000,
    Diamond = 0b0001_0000,
    Heart = 0b0010_0000,
    Spade = 0b0011_0000,
}

impl Suit {
    pub const MASK: u8 = 0b0011_0000;

    pub const MIN: Suit = Suit::Club;
    pub const MAX: Suit = Suit::Spade;

    pub const COUNT: usize = 4;

    pub const ALL: [Suit; 4] = [Suit::Club, Suit::Diamond, Suit::Heart, Suit::Spade];
    pub const RED: [Suit; 2] = [Suit::Diamond, Suit::Heart];
    pub const BLACK: [Suit; 2] = [Suit::Club, Suit::Spade];

    pub const SYMBOL_UTF8_LEN: usize = 3;

    pub const fn to_u8(self) -> u8 {
        self as u8
    }

    pub const fn from_value(value: u8) -> Option<Self> {
        match value {
            0b0000_0000 => Some(Self::Club),
            0b0001_0000 => Some(Self::Diamond),
            0b0010_0000 => Some(Self::Heart),
            0b0011_0000 => Some(Self::Spade),
            _ => None,
        }
    }

    pub const fn as_char(self) -> char {
        match self {
            Suit::Club => '♣',
            Suit::Diamond => '♦',
            Suit::Heart => '♥',
            Suit::Spade => '♠',
        }
    }

    pub const fn as_str(self) -> &'static str {
        match self {
            Suit::Club => "♣",
            Suit::Diamond => "♦",
            Suit::Heart => "♥",
            Suit::Spade => "♠",
        }
    }

    pub fn parse_bytes(bytes: &[u8]) -> Result<Self, ParseSuitError> {
        if bytes.len() != Self::SYMBOL_UTF8_LEN {
            return Err(ParseSuitError);
        }
        if bytes == Suit::Club.as_str().as_bytes() {
            return Ok(Suit::Club);
        }
        if bytes == Suit::Diamond.as_str().as_bytes() {
            return Ok(Suit::Diamond);
        }
        if bytes == Suit::Heart.as_str().as_bytes() {
            return Ok(Suit::Heart);
        }
        if bytes == Suit::Spade.as_str().as_bytes() {
            return Ok(Suit::Spade);
        }
        Err(ParseSuitError)
    }

    pub const fn parse_char(c: char) -> Result<Self, ParseSuitError> {
        match c {
            '♣' => Ok(Suit::Club),
            '♦' => Ok(Suit::Diamond),
            '♥' => Ok(Suit::Heart),
            '♠' => Ok(Suit::Spade),
            _ => Err(ParseSuitError),
        }
    }

    pub fn parse_str(s: &str) -> Result<Self, ParseSuitError> {
        Self::parse_bytes(s.as_bytes())
    }
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(u8)]
pub enum Rank {
    #[default]
    Ace = 1,
    Two = 2,
    Three = 3,
    Four = 4,
    Five = 5,
    Six = 6,
    Seven = 7,
    Eight = 8,
    Nine = 9,
    Ten = 10,
    Jack = 11,
    Queen = 12,
    King = 13,
    Jocker = 14,
}

impl Rank {
    pub const MASK: u8 = 0b0000_1111;

    pub const MIN: Rank = Rank::Ace;
    pub const MAX: Rank = Rank::Jocker;

    pub const STANDARD_COUNT: usize = 13;
    pub const ALL_COUNT: usize = 14;

    pub const SYMBOL_UTF8_LEN: usize = 1;

    pub const STANDARD: [Rank; 13] = [
        Rank::Ace,
        Rank::Two,
        Rank::Three,
        Rank::Four,
        Rank::Five,
        Rank::Six,
        Rank::Seven,
        Rank::Eight,
        Rank::Nine,
        Rank::Ten,
        Rank::Jack,
        Rank::Queen,
        Rank::King,
    ];
    pub const STANDARD_REVERSE: [Rank; 13] = const {
        let mut stack = Self::STANDARD;
        stack.reverse();
        stack
    };

    pub const ALL: [Rank; 14] = [
        Rank::Ace,
        Rank::Two,
        Rank::Three,
        Rank::Four,
        Rank::Five,
        Rank::Six,
        Rank::Seven,
        Rank::Eight,
        Rank::Nine,
        Rank::Ten,
        Rank::Jack,
        Rank::Queen,
        Rank::King,
        Rank::Jocker,
    ];
    pub const ALL_REVERSE: [Rank; 14] = const {
        let mut stack = Self::ALL;
        stack.reverse();
        stack
    };

    pub const fn to_u8(self) -> u8 {
        self as u8
    }

    pub const fn from_value(value: u8) -> Option<Self> {
        if value >= Rank::Ace.to_u8() && value <= Rank::Jocker.to_u8() {
            // SAFETY: value is a variant of Rank
            Some(unsafe { core::mem::transmute::<u8, Rank>(value) })
        } else {
            None
        }
    }

    pub const fn is_royalty(self) -> bool {
        matches!(self, Rank::Jack | Rank::Queen | Rank::King)
    }

    pub const fn is_numbered(self) -> bool {
        matches!(
            self,
            Rank::Two
                | Rank::Three
                | Rank::Four
                | Rank::Five
                | Rank::Six
                | Rank::Seven
                | Rank::Eight
                | Rank::Nine
                | Rank::Ten
        )
    }

    pub const fn pip_count(self) -> u8 {
        match self {
            Rank::Ace => 1,
            Rank::Two => 2,
            Rank::Three => 3,
            Rank::Four => 4,
            Rank::Five => 5,
            Rank::Six => 6,
            Rank::Seven => 7,
            Rank::Eight => 8,
            Rank::Nine => 9,
            Rank::Ten => 10,
            Rank::Jack | Rank::Queen | Rank::King | Rank::Jocker => 0,
        }
    }

    pub const fn has_pip(self) -> bool {
        self.pip_count() != 0
    }

    pub const fn as_char(self) -> char {
        match self {
            Rank::Ace => 'A',
            Rank::Two => '2',
            Rank::Three => '3',
            Rank::Four => '4',
            Rank::Five => '5',
            Rank::Six => '6',
            Rank::Seven => '7',
            Rank::Eight => '8',
            Rank::Nine => '9',
            Rank::Ten => 'T',
            Rank::Jack => 'J',
            Rank::Queen => 'Q',
            Rank::King => 'K',
            Rank::Jocker => 'U',
        }
    }

    pub const fn as_str(self) -> &'static str {
        match self {
            Rank::Ace => "A",
            Rank::Two => "2",
            Rank::Three => "3",
            Rank::Four => "4",
            Rank::Five => "5",
            Rank::Six => "6",
            Rank::Seven => "7",
            Rank::Eight => "8",
            Rank::Nine => "9",
            Rank::Ten => "T",
            Rank::Jack => "J",
            Rank::Queen => "Q",
            Rank::King => "K",
            Rank::Jocker => "U",
        }
    }

    pub const fn parse_byte(b: u8) -> Result<Self, ParseRankError> {
        match b {
            b'A' => Ok(Rank::Ace),
            b'2' => Ok(Rank::Two),
            b'3' => Ok(Rank::Three),
            b'4' => Ok(Rank::Four),
            b'5' => Ok(Rank::Five),
            b'6' => Ok(Rank::Six),
            b'7' => Ok(Rank::Seven),
            b'8' => Ok(Rank::Eight),
            b'9' => Ok(Rank::Nine),
            b'T' => Ok(Rank::Ten),
            b'J' => Ok(Rank::Jack),
            b'Q' => Ok(Rank::Queen),
            b'K' => Ok(Rank::King),
            b'U' => Ok(Rank::Jocker),
            _ => Err(ParseRankError),
        }
    }

    pub const fn parse_char(c: char) -> Result<Self, ParseRankError> {
        if c.len_utf8() != Self::SYMBOL_UTF8_LEN {
            return Err(ParseRankError);
        }
        Self::parse_byte(c as u8)
    }

    pub const fn parse_str(s: &str) -> Result<Self, ParseRankError> {
        let bytes = s.as_bytes();
        if bytes.len() != Self::SYMBOL_UTF8_LEN {
            return Err(ParseRankError);
        }
        Self::parse_byte(bytes[0])
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(u8)]
pub enum CardKind {
    #[default]
    Normal = 0,
    Wild = Card::WILD_MASK,
    Custom = Card::CUSTOM_MASK,
}

impl CardKind {
    pub const fn to_u8(self) -> u8 {
        self as u8
    }
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Card(NonZeroU8);

impl Card {
    pub const WILD_MASK: u8 = 0b0100_0000;
    pub const CUSTOM_MASK: u8 = 0b1000_0000;

    pub const SPECIAL_MASK: u8 = Self::WILD_MASK | Self::CUSTOM_MASK;

    pub const NORMAL_MIN: Card = Card::new(Rank::MIN, Suit::MIN);
    pub const NORMAL_MAX: Card = Card::new(Rank::MAX, Suit::MAX);

    pub const WILD_MIN: Card = Card::wild(Rank::MAX, Suit::MAX);
    pub const WILD_MAX: Card = Card::wild(Rank::MAX, Suit::MAX);

    pub const STANDARD_COUNT: usize = 52;

    pub const WILD: Card = Card::WILD_MAX;

    pub const WILD_SYMBOL: u8 = b'*';
    pub const WILD_SYMBOL_CHAR: char = '*';
    pub const WILD_SYMBOL_STR: &str = "*";

    pub const UTF8_MIN_LEN: usize = 1;
    pub const UTF8_MAX_LEN: usize = Rank::SYMBOL_UTF8_LEN + Suit::SYMBOL_UTF8_LEN + 1;

    pub const fn wild(rank: Rank, suit: Suit) -> Self {
        Self(
            // SAFETY: (WILD_MASK | rank | suit) always produces a non-zero u8
            unsafe { NonZeroU8::new_unchecked(Self::WILD_MASK | rank.to_u8() | suit.to_u8()) },
        )
    }

    pub const fn new(rank: Rank, suit: Suit) -> Self {
        Self(
            // SAFETY: (rank | suit) always produces a non-zero u8
            unsafe { NonZeroU8::new_unchecked(rank.to_u8() | suit.to_u8()) },
        )
    }

    pub const fn custom(value: u8) -> Self {
        Self(
            // SAFETY: (CUSTOM_MASK | value) will always produce a non-zero u8
            // since CUSTOM_MASK is non-zero
            unsafe { NonZeroU8::new_unchecked(Self::CUSTOM_MASK | value) },
        )
    }

    pub const fn new_kind(kind: CardKind, rank: Rank, suit: Suit) -> Self {
        Self(
            // SAFETY: (kind | rank | suit) always produces a non-zero u8
            unsafe { NonZeroU8::new_unchecked(kind.to_u8() | rank.to_u8() | suit.to_u8()) },
        )
    }

    pub const fn suit(self) -> Suit {
        // SAFETY: (Suit::MASK & u8) always get a valid Suit variant
        unsafe { core::mem::transmute(self.0.get() & Suit::MASK) }
    }

    pub const fn rank(self) -> Rank {
        let rank_value = self.0.get() & Rank::MASK;
        if rank_value == 0 || rank_value == 15 {
            Rank::Ace
        } else {
            // SAFETY: rank_value is a valid Rank variant
            unsafe { core::mem::transmute::<u8, Rank>(rank_value) }
        }
    }

    pub const fn kind(self) -> CardKind {
        if self.is_custom() {
            return CardKind::Custom;
        }
        // SAFETY: WILD_MASK only contains the left most bit set, so any bitand
        // operation with u8 will produce either 0 xor WILD_MASK, which is the
        // bit pattern of CardKind
        unsafe { core::mem::transmute(self.0.get() & Card::WILD_MASK) }
    }

    pub const fn is_custom(self) -> bool {
        (self.0.get() & Self::CUSTOM_MASK) != 0
    }

    pub const fn is_wild(self) -> bool {
        (self.0.get() & Self::SPECIAL_MASK) == Card::WILD_MASK
    }

    pub const fn is_normal(self) -> bool {
        (self.0.get() & Self::SPECIAL_MASK) == 0
    }

    pub const fn get_custom(self) -> Option<NonZeroU8> {
        if self.is_custom() { Some(self.0) } else { None }
    }

    pub const fn to_custom(self) -> Self {
        Self(
            // SAFETY: (CUSTOM_MASK | u8) always produces a non-zero u8
            unsafe { NonZeroU8::new_unchecked(Self::CUSTOM_MASK | self.0.get()) },
        )
    }

    /// Converts self to a will card
    pub const fn to_wild(self) -> Self {
        let normal = self.to_normal().0.get();
        // SAFETY: normal is non-zero
        Self(unsafe { NonZeroU8::new_unchecked(Self::WILD_MASK | normal) })
    }

    pub const fn to_normal(self) -> Self {
        let mut normal = self.0.get() & !Self::SPECIAL_MASK;
        if normal == 0 {
            normal = normal | Rank::Ace.to_u8();
        }
        // SAFETY: normal is non-zero
        Self(unsafe { NonZeroU8::new_unchecked(normal) })
    }

    pub const fn to_kind(self, kind: CardKind) -> Self {
        match kind {
            CardKind::Normal => self.to_normal(),
            CardKind::Wild => self.to_wild(),
            CardKind::Custom => self.to_custom(),
        }
    }

    pub const fn to_rank(self, rank: Rank) -> Self {
        // SAFETY: ((self & !RANK_MASK) | rank) always produces a non-zero u8
        Self(unsafe { NonZeroU8::new_unchecked((self.0.get() & !Rank::MASK) | rank.to_u8()) })
    }

    pub const fn to_suit(self, suit: Suit) -> Self {
        // SAFETY: ((self & !SUIT_MASK) | suit) always produces a non-zero u8
        Self(unsafe { NonZeroU8::new_unchecked((self.0.get() & !Suit::MASK) | suit.to_u8()) })
    }

    pub const fn to_u8(self) -> NonZeroU8 {
        self.0
    }

    pub fn parse_bytes(bytes: &[u8]) -> Result<Self, ParseCardError> {
        if bytes.len() < Card::UTF8_MIN_LEN || bytes.len() > Card::UTF8_MAX_LEN {
            return Err(ParseCardError::Length);
        }

        let (kind, remain) = if bytes[0] == Card::WILD_SYMBOL {
            (CardKind::Wild, &bytes[1..])
        } else {
            (CardKind::Normal, bytes)
        };

        let first = match (remain.first().copied(), kind) {
            (None, CardKind::Wild) => return Ok(Card::WILD),
            (None, _) => return Err(ParseCardError::Length),
            (Some(by), _) => by,
        };

        let rank = Rank::parse_byte(first)?;
        let suit = Suit::parse_bytes(&remain[1..])?;

        Ok(Self::new_kind(kind, rank, suit))
    }

    pub fn parse_str(s: &str) -> Result<Self, ParseCardError> {
        Self::parse_bytes(s.as_bytes())
    }
}
