use serde::{Deserialize, Serialize};

/// Card suit.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Suit {
    Clubs,
    Diamonds,
    Hearts,
    Spades,
}

impl Suit {
    /// Returns `true` for red suits (Diamonds, Hearts).
    pub fn is_red(self) -> bool {
        matches!(self, Suit::Diamonds | Suit::Hearts)
    }

    /// Returns `true` for black suits (Clubs, Spades).
    pub fn is_black(self) -> bool {
        !self.is_red()
    }
}

/// Card rank, Ace through King.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Rank {
    Ace,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    Eight,
    Nine,
    Ten,
    Jack,
    Queen,
    King,
}

impl Rank {
    /// Numeric value: Ace = 1, King = 13.
    pub fn value(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  => 11,
            Rank::Queen => 12,
            Rank::King  => 13,
        }
    }
}

/// A single playing card.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Card {
    /// Unique identifier for this card within the deal. Stable across moves and undo.
    pub id: u32,
    /// The card's suit (Clubs, Diamonds, Hearts, Spades).
    pub suit: Suit,
    /// The card's rank (Ace through King).
    pub rank: Rank,
    /// Whether the card is visible to the player. Face-down cards may not be moved.
    pub face_up: bool,
}

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

    #[test]
    fn rank_value_ace_is_one() {
        assert_eq!(Rank::Ace.value(), 1);
    }

    #[test]
    fn rank_value_king_is_thirteen() {
        assert_eq!(Rank::King.value(), 13);
    }

    #[test]
    fn rank_values_are_sequential() {
        let ranks = [
            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,
        ];
        for (i, r) in ranks.iter().enumerate() {
            assert_eq!(r.value(), (i + 1) as u8);
        }
    }

    #[test]
    fn suit_red_is_diamonds_and_hearts() {
        assert!(Suit::Diamonds.is_red());
        assert!(Suit::Hearts.is_red());
        assert!(!Suit::Clubs.is_red());
        assert!(!Suit::Spades.is_red());
    }

    #[test]
    fn suit_black_is_clubs_and_spades() {
        assert!(Suit::Clubs.is_black());
        assert!(Suit::Spades.is_black());
        assert!(!Suit::Diamonds.is_black());
        assert!(!Suit::Hearts.is_black());
    }

    #[test]
    fn card_face_up_field_reflects_construction() {
        let card = Card { id: 0, suit: Suit::Hearts, rank: Rank::Ace, face_up: false };
        assert!(!card.face_up);
        let card2 = Card { id: 1, suit: Suit::Spades, rank: Rank::King, face_up: true };
        assert!(card2.face_up);
    }
}
