use std::{collections::HashMap, iter::Peekable, rc::Rc, slice::Iter, vec::IntoIter};

use crate::node::{
    NodeEnum,
    add::Add,
    constant::{Constant, ConstantValue},
    divide::Divide,
    multiply::Multiply,
    subtract::Subtract,
};

#[derive(Debug)]
pub struct Token(usize, TokenType);

#[derive(Debug)]
pub enum TokenType {
    // Space,
    Number(ConstantValue),

    Plus,
    Minus,
    Star,
    Slash,

    RParen,
    LParen,

    If,
    Then,
    Else,
    End,
}

pub struct Lexer<'a> {
    source: &'a String,
}

#[derive(Debug)]
pub enum LexerError {
    UnexpectedChar(usize, String),
}

impl<'a> Lexer<'a> {
    pub fn new(source: &'a String) -> Self {
        Self { source }
    }

    pub fn lex(&'a mut self) -> Result<Vec<Token>, LexerError> {
        let mut src = self.source.chars().peekable();
        let mut i = 0;
        let mut tokens = vec![];

        while let Some(c) = src.next() {
            match c {
                // Collapse spaces into a single Space token
                ' ' => {
                    while src.peek() == Some(&' ') {
                        src.next();
                        i += 1;
                    }
                    // tokens.push(Token(i, TokenType::Space));
                }
                // Comments with //
                '/' if src.peek() == Some(&'/') => {
                    while src.next() != Some('\n') {
                        i += 1;
                    }
                }
                // Numbers with decimal points
                '0'..'9' | '.' => {
                    let mut digit = String::from(c);
                    loop {
                        let d = src.peek();
                        let mut has_decimal = c == '.';
                        match d {
                            Some('0'..'9') => {
                                digit.push(*d.unwrap());
                                src.next();
                                i += 1;
                            }
                            #[allow(unused_assignments)] // For some reason it thinks has_decimal
                            // is never read
                            Some('.') => {
                                if has_decimal {
                                    return Err(LexerError::UnexpectedChar(
                                        i,
                                        "Invalid digit with multiple decimal points".into(),
                                    ));
                                }

                                digit.push(*d.unwrap());
                                has_decimal = true;
                            }
                            _ => {
                                break;
                            }
                        }
                    }
                    let number = digit.parse::<ConstantValue>().unwrap();

                    tokens.push(Token(i, TokenType::Number(number)));
                }

                '+' => tokens.push(Token(i, TokenType::Plus)),
                '-' => tokens.push(Token(i, TokenType::Minus)),
                '*' => tokens.push(Token(i, TokenType::Star)),
                '/' => tokens.push(Token(i, TokenType::Slash)),

                '(' => tokens.push(Token(i, TokenType::LParen)),
                ')' => tokens.push(Token(i, TokenType::RParen)),

                _ => {
                    return Err(LexerError::UnexpectedChar(
                        i,
                        format!("Unexpected char {}", c),
                    ));
                }
            }
            i += 1;
        }

        Ok(tokens)
    }
}

pub enum ParserError {
    UnexpectedEndOfTokens(String),
    UnexpectedToken(usize, String),
}

/// Recursive descent parser
pub struct Parser {
    tokens: Peekable<IntoIter<Token>>,
}

type Tokens<'a> = Peekable<Iter<'a, Token>>;

impl Parser {
    pub fn new(tokens: Vec<Token>) -> Self {
        // #[cfg(debug_assertions)]
        // println!("\r{tokens:#?}");
        Self {
            tokens: tokens.into_iter().peekable(),
        }
    }

    // Parse tokens recursively and descendentantly
    pub fn parse(&mut self) -> Result<Vec<Rc<NodeEnum>>, ParserError> {
        let mut expressions = vec![];

        while self.tokens.peek().is_some() {
            expressions.push(self.expression()?);
        }

        Ok(expressions)
    }

    fn expression(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        self.equality()
    }

    fn equality(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        // TODO: Implement equality
        self.comparison()
    }

    fn comparison(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        // TODO: Implement comparison
        self.term()
    }

    fn term(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        let expr = self.factor()?;
        if let Some(Token(_, TokenType::Plus)) = self.tokens.peek() {
            self.tokens.next();
            Ok(Rc::new(Add::new(expr, self.comparison()?).into()))
        } else if let Some(Token(_, TokenType::Minus)) = self.tokens.peek() {
            self.tokens.next();
            Ok(Rc::new(Subtract::new(expr, self.comparison()?).into()))
        } else {
            Ok(expr)
        }
    }

    fn factor(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        let expr = self.unary()?;
        if let Some(Token(_, TokenType::Star)) = self.tokens.peek() {
            self.tokens.next();
            Ok(Rc::new(Multiply::new(expr, self.comparison()?).into()))
        } else if let Some(Token(_, TokenType::Slash)) = self.tokens.peek() {
            self.tokens.next();
            Ok(Rc::new(Divide::new(expr, self.comparison()?).into()))
        } else {
            Ok(expr)
        }
    }

    fn unary(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        self.exponent()
    }

    fn exponent(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        self.call()
    }

    fn call(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        self.function()
    }

    fn function(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        self.primary()
    }

    fn primary(&mut self) -> Result<Rc<NodeEnum>, ParserError> {
        let (i, token) = if let Some(Token(i, token)) = self.tokens.next() {
            (i, token)
        } else {
            return Err(ParserError::UnexpectedEndOfTokens(
                "Expected a Primary here".into(),
            ));
        };

        match token {
            TokenType::Number(value) => Ok(Rc::new(Constant::new(value).into())),
            _ => Err(ParserError::UnexpectedToken(
                i,
                format!("Unexpected token {token:?}"),
            )),
        }
    }
}
