use std::fmt::{self, Display, Formatter};

use thiserror::Error;

use crate::data::BagData;

#[derive(Error, Debug)]
pub enum BagError {
    #[error("Not enough tiles")]
    NotEnoughTiles,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Tile {
    letter: char,
    value: u8,
}

impl Tile {
    pub fn letter(&self) -> char {
        self.letter
    }

    pub fn value(&self) -> u8 {
        self.value
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Bag {
    tiles: Vec<Tile>,
}

impl Bag {
    pub fn take(&mut self, count: usize) -> Result<Bag, BagError> {
        if count > self.tiles.len() {
            return Err(BagError::NotEnoughTiles);
        }
        let mut tiles = Vec::new();
        for _ in 0..count {
            let random_tile = self
                .tiles
                .remove(rand::random::<usize>() % self.tiles.len());
            tiles.push(random_tile);
        }
        Ok(Bag { tiles })
    }
}

impl From<BagData> for Bag {
    fn from(data: BagData) -> Self {
        let mut tiles = Vec::new();
        for tile in data.tiles() {
            for _ in 0..tile.count() {
                tiles.push(Tile {
                    letter: tile.letter(),
                    value: tile.value(),
                });
            }
        }
        Bag { tiles }
    }
}

impl Display for Bag {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "Bag {{ tiles: [")?;
        for tile in &self.tiles {
            write!(f, "{}, ", tile.letter)?;
        }
        write!(f, "] }}")
    }
}
