use thiserror::Error;

use crate::{bag::Tile, dictionary::Dictionary};

#[derive(Error, Debug)]
pub enum MoveError {
    #[error("Out of bounds")]
    OutOfBounds,
    #[error("Occupied")]
    Occupied,
}

#[derive(Error, Debug)]
pub enum BoardError {
    #[error("Invalid space")]
    MoveError(#[from] MoveError),
}

pub enum MoveResult {
    Valid(u32),
    Invalid,
}

#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum SpaceKind {
    #[default]
    Normal,
    Start,
    DoubleLetter,
    TripleLetter,
    DoubleWord,
    TripleWord,
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct Space {
    pub kind: SpaceKind,
    pub tile: Option<Tile>,
}

#[derive(Clone, Debug, PartialEq)]
struct Move {
    x: usize,
    y: usize,
    tile: Tile,
}

#[derive(Debug)]
pub struct Board {
    board: Vec<Vec<Space>>,
    pending_move: Vec<Move>,
    dictionary: Dictionary,
}

impl Board {
    pub fn new(dictionary: Dictionary) -> Board {
        let mut board = vec![vec![Space::default(); 15]; 15];

        // Start space
        board[7][7].kind = SpaceKind::Start;

        // Triple word spaces
        board[0][0].kind = SpaceKind::TripleWord;
        board[0][7].kind = SpaceKind::TripleWord;
        board[0][14].kind = SpaceKind::TripleWord;
        board[7][0].kind = SpaceKind::TripleWord;
        board[7][14].kind = SpaceKind::TripleWord;
        board[14][0].kind = SpaceKind::TripleWord;
        board[14][7].kind = SpaceKind::TripleWord;
        board[14][14].kind = SpaceKind::TripleWord;

        // Double word spaces
        board[1][1].kind = SpaceKind::DoubleWord;
        board[2][2].kind = SpaceKind::DoubleWord;
        board[3][3].kind = SpaceKind::DoubleWord;
        board[4][4].kind = SpaceKind::DoubleWord;
        board[10][10].kind = SpaceKind::DoubleWord;
        board[11][11].kind = SpaceKind::DoubleWord;
        board[12][12].kind = SpaceKind::DoubleWord;
        board[13][13].kind = SpaceKind::DoubleWord;
        board[1][13].kind = SpaceKind::DoubleWord;
        board[2][12].kind = SpaceKind::DoubleWord;
        board[3][11].kind = SpaceKind::DoubleWord;
        board[4][10].kind = SpaceKind::DoubleWord;
        board[10][4].kind = SpaceKind::DoubleWord;
        board[11][3].kind = SpaceKind::DoubleWord;
        board[12][2].kind = SpaceKind::DoubleWord;
        board[13][1].kind = SpaceKind::DoubleWord;

        // Triple letter spaces
        board[1][5].kind = SpaceKind::TripleLetter;
        board[1][9].kind = SpaceKind::TripleLetter;
        board[5][1].kind = SpaceKind::TripleLetter;
        board[5][5].kind = SpaceKind::TripleLetter;
        board[5][9].kind = SpaceKind::TripleLetter;
        board[5][13].kind = SpaceKind::TripleLetter;
        board[9][1].kind = SpaceKind::TripleLetter;
        board[9][5].kind = SpaceKind::TripleLetter;
        board[9][9].kind = SpaceKind::TripleLetter;
        board[9][13].kind = SpaceKind::TripleLetter;
        board[13][5].kind = SpaceKind::TripleLetter;
        board[13][9].kind = SpaceKind::TripleLetter;

        // Double letter spaces
        board[0][3].kind = SpaceKind::DoubleLetter;
        board[0][11].kind = SpaceKind::DoubleLetter;
        board[2][6].kind = SpaceKind::DoubleLetter;
        board[2][8].kind = SpaceKind::DoubleLetter;
        board[3][0].kind = SpaceKind::DoubleLetter;
        board[3][7].kind = SpaceKind::DoubleLetter;
        board[3][14].kind = SpaceKind::DoubleLetter;
        board[6][2].kind = SpaceKind::DoubleLetter;
        board[6][6].kind = SpaceKind::DoubleLetter;
        board[6][8].kind = SpaceKind::DoubleLetter;
        board[6][12].kind = SpaceKind::DoubleLetter;
        board[7][3].kind = SpaceKind::DoubleLetter;
        board[7][11].kind = SpaceKind::DoubleLetter;
        board[8][2].kind = SpaceKind::DoubleLetter;
        board[8][6].kind = SpaceKind::DoubleLetter;
        board[8][8].kind = SpaceKind::DoubleLetter;
        board[8][12].kind = SpaceKind::DoubleLetter;
        board[11][0].kind = SpaceKind::DoubleLetter;
        board[11][7].kind = SpaceKind::DoubleLetter;
        board[11][14].kind = SpaceKind::DoubleLetter;
        board[12][6].kind = SpaceKind::DoubleLetter;
        board[12][8].kind = SpaceKind::DoubleLetter;
        board[14][3].kind = SpaceKind::DoubleLetter;
        board[14][11].kind = SpaceKind::DoubleLetter;

        Board {
            board,
            pending_move: Vec::new(),
            dictionary,
        }
    }

    fn check_space_for_move(&self) -> Result<(), MoveError> {
        todo!()
    }

    pub fn place_tile(&mut self, x: usize, y: usize, tile: Tile) -> Result<(), BoardError> {
        self.check_space_for_move()?;
        self.pending_move.push(Move { x, y, tile });
        Ok(())
    }

    pub fn validate_move(&self) -> MoveResult {
        todo!()
    }

    pub fn commit_move() -> MoveResult {
        todo!()
    }
}

/// Print the board as a grid
impl std::fmt::Display for Board {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(f, "Board {{")?;
        for row in &self.board {
            write!(f, "  [")?;
            let mut row = row.iter().peekable();
            while let Some(space) = row.next() {
                match space.tile {
                    Some(tile) => write!(f, "{}", tile.letter())?,
                    None => match space.kind {
                        SpaceKind::Normal => write!(f, "■")?,
                        SpaceKind::Start => write!(f, "☆")?,
                        SpaceKind::DoubleLetter => write!(f, "2")?,
                        SpaceKind::TripleLetter => write!(f, "3")?,
                        SpaceKind::DoubleWord => write!(f, "②")?,
                        SpaceKind::TripleWord => write!(f, "③")?,
                    },
                }
                if row.peek().is_some() {
                    write!(f, " ")?;
                }
            }
            writeln!(f, "]")?;
        }
        write!(f, "}}")
    }
}
