use crate::{
    ast::err::ParseError,
    ast::types::{
        BlockStmt, BreakStmt, ContinueStmt, ForInit, ForStmt, IfElseBranch, IfStmt, ReturnStmt,
        Statement, WhileStmt,
    },
    lexer::types::TokenValue,
};

use super::{ParseProcessError, ParseType, Parser};

impl Parser {
    pub(super) fn parse_block_stmt(&mut self, parse_type: ParseType) -> Result<BlockStmt, ParseProcessError> {
        if self.peek().value != TokenValue::LBrace {
            if parse_type == ParseType::MustParse {
                let token = self.next().clone();
                self.diagnostics.add_from_frontend_error(
                    ParseError::ExpectedBefore(token.value, "`{`"),
                    token.span,
                );
                return Err(ParseProcessError::ErrorInMatch);
            }
            return Err(ParseProcessError::TryNext);
        }
        self.advance(1);
        let mut statements = vec![];
        // println!("parse block stmt");
        loop {
            if self.is_eof() {
                let span = self.last().span;
                self.diagnostics
                    .add_from_frontend_error(ParseError::ExpectedBefore(TokenValue::Eof, "`}`"), span);
                return Err(ParseProcessError::ErrorInMatch);
            }
            if matches!(self.peek().value, TokenValue::Semicolon) {
                // like a();;
                self.advance(1);
                continue;
            }
            if matches!(self.peek().value, TokenValue::RBrace) {
                self.advance(1);
                break;
            }
            // parse statement here
            match self.parse_stmt() {
                Ok(stmt) => statements.push(stmt),
                Err(_) => {
                    self.until_next_token(&[TokenValue::Semicolon, TokenValue::RBrace]);
                    if self.last().value == TokenValue::RBrace {
                        break;
                    }
                }
            }
        }
        // println!("finish parse block stmt");
        Ok(BlockStmt {
            statements
        })
    }

    fn parse_stmt(&mut self) -> Result<Statement, ParseProcessError> {
        match self.parse_var_decl_stmt(ParseType::TryParse) {
            Ok(var_decl) => return Ok(Statement::VarDecl(var_decl)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        };
        match self.parse_return_stmt() {
            Ok(return_stmt) => return Ok(Statement::Return(return_stmt)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        };
        match self.parse_if_stmt() {
            Ok(if_stmt) => return Ok(Statement::If(if_stmt)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        }
        match self.parse_while_stmt() {
            Ok(while_stmt) => return Ok(Statement::While(while_stmt)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        }
        match self.parse_for_stmt() {
            Ok(for_stmt) => return Ok(Statement::For(for_stmt)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        }
        match self.parse_break_stmt() {
            Ok(break_stmt) => return Ok(Statement::Break(break_stmt)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        }
        match self.parse_continue_stmt() {
            Ok(continue_stmt) => return Ok(Statement::Continue(continue_stmt)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        }
        match self.parse_block_stmt(ParseType::TryParse) {
            Ok(block_stmt) => return Ok(Statement::Block(block_stmt)),
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        }
        match self.parse_expr() {
            Ok(expr) => {
                self.must_match_token(&TokenValue::Semicolon, "`;`")?;
                return Ok(Statement::Expr(expr))
            },
            Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch),
            Err(_) => {},
        }
        self.until_next_token(&[TokenValue::Semicolon]);
        Err(ParseProcessError::ErrorInMatch)
    }

    fn parse_return_stmt(&mut self) -> Result<ReturnStmt, ParseProcessError> {
        if self.peek().value != TokenValue::Return {
             return Err(ParseProcessError::TryNext);
        }
        let span = self.next().span;
        let value = if matches!(self.peek().value, TokenValue::Semicolon) {
            None
        } else {
            Some(self.parse_expr()?)
        };
        self.must_match_token(&TokenValue::Semicolon, "`;`")?;
        Ok(ReturnStmt {
            value,
            span,
        })
    }
    fn parse_if_stmt(&mut self) -> Result<IfStmt, ParseProcessError> {
        if self.peek().value != TokenValue::If {
            return Err(ParseProcessError::TryNext);
        }
        self.advance(1);
        self.must_match_token(&TokenValue::LParen, "`(`")?;
        self.must_have_some("if condition expr")?;
        let condition = self.parse_expr()?;
        self.must_match_token(&TokenValue::RParen, "`)`")?;
        let then_branch;
        let mut ifelse_branch = vec![];
        let mut else_branch = None;
        self.must_have_some("if statement body")?;
        if self.peek().value != TokenValue::LBrace {
            let stmt = self.parse_stmt()?;
            then_branch = BlockStmt { statements: vec![stmt] };
        } else {
            then_branch = self.parse_block_stmt(ParseType::MustParse)?;
        }
        loop {
            if self.is_eof() {
                break;
            }
            if self.peek().value == TokenValue::Else {
                self.advance(1);
                self.must_have_some("else body")?;
                if self.peek().value != TokenValue::If {
                    self.back(1);
                    break;
                }
                // else if
                self.advance(1);
                self.must_match_token(&TokenValue::LParen, "`(`")?;
                self.must_have_some("else if condition expr")?;
                let condition = self.parse_expr()?;
                self.must_match_token(&TokenValue::RParen, "`)`")?;
                let then_branch;
                if self.peek().value != TokenValue::LBrace {
                    let stmt = self.parse_stmt()?;
                    then_branch = BlockStmt { statements: vec![stmt] };
                } else {
                    then_branch = self.parse_block_stmt(ParseType::MustParse)?;
                }
                ifelse_branch.push(IfElseBranch { condition, then_branch });
            } else {
                // if end ?
                break;
            }
        }
        if self.peek().value == TokenValue::Else {
            // Parse else branch
            self.advance(1);
            self.must_have_some("else body")?;
            if self.peek().value != TokenValue::LBrace {
                let stmt = self.parse_stmt()?;
                else_branch = Some(BlockStmt { statements: vec![stmt] });
            } else {
                else_branch = Some(self.parse_block_stmt(ParseType::MustParse)?);
            }
        }
        Ok(IfStmt {
            condition,
            then_branch,
            ifelse_branch,
            else_branch,
        })
    }

    fn parse_while_stmt(&mut self) -> Result<WhileStmt, ParseProcessError> {
        if self.peek().value != TokenValue::While {
            return Err(ParseProcessError::TryNext);
        }
        self.advance(1);
        self.must_match_token(&TokenValue::LParen, "`(`")?;
        self.must_have_some("while condition expr")?;
        let condition = self.parse_expr()?;
        self.must_match_token(&TokenValue::RParen, "`)`")?;
        let body;
        if self.peek().value != TokenValue::LBrace {
            let stmt = self.parse_stmt()?;
            body = BlockStmt { statements: vec![stmt] };
        } else {
            body = self.parse_block_stmt(ParseType::MustParse)?;
        }
        Ok(WhileStmt {
            condition,
            body,
        })
    }

    fn parse_for_stmt(&mut self) -> Result<ForStmt, ParseProcessError> {
        if self.peek().value != TokenValue::For {
            return Err(ParseProcessError::TryNext);
        }
        self.advance(1);
        self.must_match_token(&TokenValue::LParen, "`(`")?;
        let init = if self.peek().value == TokenValue::Semicolon {
            None
        } else if matches!(self.peek().value, TokenValue::Static | TokenValue::TypeIdent(_)) {
            Some(ForInit::VarDecl(self.parse_var_decl_stmt_inner(ParseType::MustParse, false)?))
        } else {
            Some(ForInit::Expr(self.parse_expr()?))
        };
        self.must_match_token(&TokenValue::Semicolon, "`;`")?;
        let condition = if self.peek().value == TokenValue::Semicolon {
            None
        } else {
            Some(self.parse_expr()?)
        };
        self.must_match_token(&TokenValue::Semicolon, "`;`")?;
        let update = if self.peek().value == TokenValue::RParen {
            None
        } else {
            Some(self.parse_expr()?)
        };
        self.must_match_token(&TokenValue::RParen, "`)`")?;
        let body;
        if self.peek().value != TokenValue::LBrace {
            let stmt = self.parse_stmt()?;
            body = BlockStmt { statements: vec![stmt] };
        } else {
            body = self.parse_block_stmt(ParseType::MustParse)?;
        }
        Ok(ForStmt {
            init,
            condition,
            update,
            body,
        })
    }

    fn parse_break_stmt(&mut self) -> Result<BreakStmt, ParseProcessError> {
        let start_span = self.peek().span;
        if self.peek().value == TokenValue::Break {
            self.advance(1);
            self.must_match_token(&TokenValue::Semicolon, "`;`")?;
            Ok(BreakStmt {
                span: start_span,
            })
        } else {
            Err(ParseProcessError::TryNext)
        }
    }

    fn parse_continue_stmt(&mut self) -> Result<ContinueStmt, ParseProcessError> {
        let start_span = self.peek().span;
        if self.peek().value == TokenValue::Continue {
            self.advance(1);
            self.must_match_token(&TokenValue::Semicolon, "`;`")?;
            Ok(ContinueStmt {
                span: start_span,
            })
        } else {
            Err(ParseProcessError::TryNext)
        }
    }
}
