use crate::{
    ast::types::{
        BinaryOp, BlockStmt, CompileUnit, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, Param,
        ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue,
    },
    diagnostic::{Diagnositics, span::Span},
    frontend::{
        err::ParseError,
        types::{Token, TokenValue, TypeIdent},
    },
};

pub struct Parser {
    tokens: Vec<Token>,
    diagnostics: Diagnositics,
    pos: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum ParseType {
    MustParse,
    TryParse,
}
// const FUNC_OR_VAR_DECL_AT_TOP_LEVEL: &str = "function or variable declaration at top level";
// const PARAM_DECL: &str = "parameter declaration";
// const BODY_DECL: &str = "function body";
impl Parser {
    pub fn new(tokens: Vec<Token>, diagnostics: Diagnositics) -> Self {
        Self {
            tokens,
            diagnostics,
            pos: 0,
        }
    }
    pub fn parse(&mut self) {}
    fn peek(&self) -> Option<&Token> {
        self.tokens.get(self.pos)
    }
    fn next(&mut self) -> Option<&Token> {
        let token = self.tokens.get(self.pos);
        if token.is_some() {
            self.pos += 1;
        }
        token
    }
    fn advance(&mut self, n: usize) {
        self.pos += n;
        assert!(self.pos <= self.tokens.len());
    }
    fn back(&mut self, n: usize) {
        assert!(self.pos >= n);
        self.pos -= n;
    }
    fn parse_compile_unit(&mut self) -> CompileUnit {
        let mut global_decls = vec![];
        while self.peek().is_some() {
            if let Some(decl) = self.parse_global_decl_stmt() {
                global_decls.push(decl);
            }
        }
        CompileUnit { global_decls }
    }

    fn parse_global_decl_stmt(&mut self) -> Option<GlobalDeclStmt> {
        assert!(self.peek().is_some());
        if let Some(func_decl) = self.parse_func_decl_stmt() {
            return Some(GlobalDeclStmt::FuncDecl(func_decl));
        }
        if let Some(var_decl) = self.parse_var_decl_stmt() {
            return Some(GlobalDeclStmt::VarDecl(var_decl));
        }
        let token = self.next().unwrap().clone();
        self.diagnostics.add_from_frontend_error(
            ParseError::UnexpectedToken(token.value, "function or variable declaration at top level"),
            token.span,
        );
        None
    }
    fn parse_type_and_name(&mut self) -> Option<(TypeIdent, String, Span)> {
        assert!(self.peek().is_some());
        let start_span = self.peek().unwrap().span;
        let type_ident = self.peek().unwrap().value.as_type_ident()?;
        self.advance(1);
        let name = match self.peek().map(|t| t.value.as_ident()).flatten() {
            None => {
                let span = self.next().unwrap().span;
                self.diagnostics.add_from_frontend_error(
                    ParseError::CantCombineWith(TokenValue::TypeIdent(type_ident)),
                    span,
                );
                return None;
            }
            Some(ident) => ident,
        };
        let end_span = self.peek().unwrap().span;
        self.advance(1);
        Some((type_ident, name, Span::from_two(start_span, end_span)))
    }
    fn parse_func_decl_stmt(&mut self) -> Option<FuncDeclStmt> {
        assert!(self.peek().is_some());
        let start_span = self.peek().unwrap().span;
        let (return_type, name, _) = self.parse_type_and_name()?;
        if self
            .peek()
            .is_some_and(|t| matches!(t.value, TokenValue::LParen))
        {
            self.advance(1);
        } else {
            self.back(2);
            return None;
        }
        // from here we can be sure it's a function declaration, so we can report error if the syntax is wrong
        let params = match self.peek().map(|t| &t.value) {
            Some(TokenValue::RParen) => {
                self.advance(1);
                vec![]
            }
            Some(_) => self.parse_param_list()?,
            None => {
                let span = self.next().unwrap().span;
                self.diagnostics
                    .add_from_frontend_error(ParseError::ExpectButEof("parameter declaration"), span);
                return None;
            }
        };
        let body = match self.peek().map(|t| &t.value) {
            Some(_) => self.parse_block_stmt(ParseType::MustParse)?,
            None => {
                let span = self.next().unwrap().span;
                self.diagnostics
                    .add_from_frontend_error(ParseError::ExpectButEof("function body"), span);
                return None;
            }
        };
        let end_span = body.span;
        Some(FuncDeclStmt {
            return_type: return_type.into(),
            name,
            params,
            body,
            span: Span::from_two(start_span, end_span),
        })
    }
    fn parse_param_list(&mut self) -> Option<Vec<Param>> {
        assert!(self.peek().is_some());
        let mut params = vec![];

        while self.peek().is_some() {
            if !params.is_empty() {
                if self.peek().map(|t| &t.value) == Some(&TokenValue::Comma) {
                    self.advance(1);
                } else {
                    let token = self.next().unwrap().clone();
                    self.diagnostics.add_from_frontend_error(
                        ParseError::UnexpectedToken(token.value, "`,`"),
                        token.span,
                    );
                    return None;
                }
            }
            if let Some(param) = self.parse_param() {
                params.push(param);
            } else {
                return None;
            }
        }
        Some(params)
    }
    fn parse_param(&mut self) -> Option<Param> {
        assert!(self.peek().is_some());
        let (param_type, name, span) = self.parse_type_and_name()?;
        Some(Param {
            param_type: param_type.into(),
            name,
            span,
        })
    }
    fn must_match_semicolon(&mut self) -> Option<()> {
        if self
            .peek()
            .is_some_and(|t| matches!(t.value, TokenValue::Semicolon))
        {
            self.advance(1);
            Some(())
        } else {
            let token = self.next().unwrap().clone();
            self.diagnostics
                .add_from_frontend_error(ParseError::UnexpectedToken(token.value, "`;`"), token.span);
            while let Some(t) = self.peek() {
                if matches!(t.value, TokenValue::Semicolon) {
                    self.advance(1);
                    break;
                }
                if matches!(t.value, TokenValue::RBrace) {
                    break;
                }
                self.advance(1);
            }
            None
        }
    }
    fn parse_var_decl_stmt(&mut self) -> Option<VarDeclStmt> {
        assert!(self.peek().is_some());
        let mut values = vec![];
        let (var_type, name, span) = self.parse_type_and_name()?;
        values.push(VarDeclStmtValue { name, var_type: var_type.into(), span });
        while let Some(t) = self.peek() {
            if matches!(t.value, TokenValue::Semicolon) {
                break;
            }
            if matches!(t.value, TokenValue::Comma) {
                self.advance(1);
                if let Some(ident) = self.peek().map(|t| t.value.as_ident()).flatten() {
                    let span = self.peek().unwrap().span;
                    self.advance(1);
                    values.push(VarDeclStmtValue { name: ident, var_type: var_type.into(), span });
                } else {
                    let token = self.next().unwrap().clone();
                    self.diagnostics.add_from_frontend_error(
                        ParseError::CantCombineWith(TokenValue::TypeIdent(var_type)),
                        token.span,
                    );
                    return None;
                }
                continue;
            }
            let token = self.next().unwrap().clone();
            self.diagnostics.add_from_frontend_error(
                ParseError::UnexpectedToken(token.value, "variable declaration"),
                token.span,
            );
        }
        self.must_match_semicolon()?;
        let span = Span::from_two(values.first().unwrap().span, values.last().unwrap().span);
        Some(VarDeclStmt { values, span })
    }

    fn parse_block_stmt(&mut self, parse_type: ParseType) -> Option<BlockStmt> {
        assert!(self.peek().is_some());
        let start_span = self.peek().unwrap().span;
        if !self
            .peek()
            .map(|t| matches!(t.value, TokenValue::LBrace))
            .unwrap_or(false)
        {
            if parse_type == ParseType::MustParse {
                let token = self.next().unwrap().clone();
                self.diagnostics.add_from_frontend_error(
                    ParseError::UnexpectedToken(token.value, "`{`"),
                    token.span,
                );
            }
            return None;
        }
        self.advance(1);
        let mut statements = vec![];
        println!("parse block stmt");
        loop {
            if self.peek().is_none() {
                let span = self.next().unwrap().span;
                self.diagnostics
                    .add_from_frontend_error(ParseError::ExpectButEof("`}`"), span);
                return None;
            }
            if self
                .peek()
                .map(|t| matches!(t.value, TokenValue::Semicolon))
                .unwrap()
            {
                self.advance(1);
                continue;
            }
            if self
                .peek()
                .map(|t| matches!(t.value, TokenValue::RBrace))
                .unwrap()
            {
                self.advance(1);
                break;
            }
            // parse statement here
            statements.push(self.parse_stmt()?);
        }
        println!("finish parse block stmt");
        let end_span = statements.last().map(|s| s.span()).unwrap_or(start_span);
        Some(BlockStmt {
            statements,
            span: Span::from_two(start_span, end_span),
        })
    }

    fn parse_stmt(&mut self) -> Option<Statement> {
        assert!(self.peek().is_some());
        if let Some(var_decl) = self.parse_var_decl_stmt() {
            return Some(Statement::VarDecl(var_decl));
        }
        if let Some(return_stmt) = self.parse_return_stmt() {
            return Some(Statement::Return(return_stmt));
        }
        if let Some(block_stmt) = self.parse_block_stmt(ParseType::TryParse) {
           return Some(Statement::Block(block_stmt));
        }
        if let Some(expr) = self.parse_expr() {
            return Some(Statement::Expr(expr));
        }
        let token = self.next().unwrap().clone();
        self.diagnostics.add_from_frontend_error(
            ParseError::UnexpectedToken(token.value, "statement"),
            token.span,
        );
        while let Some(t) = self.peek() {
            if matches!(t.value, TokenValue::Semicolon) {
                self.advance(1);
                break;
            }
            if matches!(t.value, TokenValue::RBrace) {
                break;
            }
            self.advance(1);
        }
        None
    }

    fn parse_return_stmt(&mut self) -> Option<ReturnStmt> {
        assert!(self.peek().is_some());
        let start_span = self.peek().unwrap().span;
        if !self
            .peek()
            .map(|t| matches!(t.value, TokenValue::Return))
            .unwrap_or(false)
        {
            return None;
        }
        self.advance(1);
        let value = if self
            .peek()
            .map(|t| matches!(t.value, TokenValue::Semicolon))
            .unwrap_or(false)
        {
            None
        } else {
            Some(self.parse_expr()?)
        };
        self.must_match_semicolon()?;
        let end_span = self.peek().unwrap().span;
        Some(ReturnStmt {
            value,
            span: Span::from_two(start_span, end_span),
        })
    }
    // fn parse_expr_tail(&mut self, left: Expr) -> Option<Expr> {
    //    match self.peek() {
    //        None => Some(left),
    //        Some(t1) => {
    //            // TODO: add delimiter judge to support better error recovery
    //            let op = BinaryOp::from_token_value(&t1.value)?;
    //            match op {
    //                BinaryOp::Add
    //            }
    //            let right = self.parse_term()?;
    //            let expr = Expr { value: ExprValue::BinaryOp { lhs: Box::new(left), op, rhs: Box::new(right) }, span: Span::from_two(left.span, right.span) };
    //            self.parse_expr_tail(expr)
    //        }
    //    }
    // }
    fn parse_primary(&mut self) -> Option<Expr> {
        assert!(self.peek().is_some());
        let token = self.next().unwrap().clone();
        match token.value {
            TokenValue::Ident(name) => Some(Expr {
                value: ExprValue::Var(name),
                span: token.span,
            }),
            TokenValue::IntLit(value) => Some(Expr {
                value: ExprValue::IntLit(value),
                span: token.span,
            }),
            TokenValue::LParen => {
                let expr = match self.peek() {
                    Some(_) => self.parse_expr()?,
                    None => {
                        self.diagnostics.add_from_frontend_error(
                            ParseError::ExpectButEof("expression"),
                            token.span,
                        );
                        return None;
                    }
                };
                match self.peek() {
                    Some(t) if matches!(t.value, TokenValue::RParen) => {
                        let end_span = t.span;
                        self.advance(1);
                        Some(Expr {
                            span: Span::from_two(token.span, end_span),
                            ..expr
                        })
                    }
                    Some(_) => {
                        let token = self.next().unwrap().clone();
                        self.diagnostics.add_from_frontend_error(
                            ParseError::UnexpectedToken(token.value, "`)`"),
                            token.span,
                        );
                        None
                    }
                    None => {
                        self.diagnostics
                            .add_from_frontend_error(ParseError::ExpectButEof("`)`"), expr.span);
                        None
                    }
                }
            }
            _ => {
                self.diagnostics.add_from_frontend_error(
                    ParseError::UnexpectedToken(token.value, "expression"),
                    token.span,
                );
                None
            }
        }
    }
    fn parse_unary(&mut self) -> Option<Expr> {
        assert!(self.peek().is_some());
        let token = self.peek().unwrap().clone();
        match token.value {
            TokenValue::Plus => {
                self.advance(1);
                let expr = match self.peek() {
                    Some(_) => self.parse_unary()?,
                    None => {
                        self.diagnostics.add_from_frontend_error(
                            ParseError::ExpectButEof("expression"),
                            token.span,
                        );
                        return None;
                    }
                };
                Some(Expr {
                    span: Span::from_two(token.span, expr.span),
                    ..expr
                })
            }
            TokenValue::Minus => {
                self.advance(1);
                let rhs = match self.peek() {
                    Some(_) => self.parse_unary()?,
                    None => {
                        self.diagnostics.add_from_frontend_error(
                            ParseError::ExpectButEof("expression"),
                            token.span,
                        );
                        return None;
                    }
                };
                let lhs = Expr {
                    value: ExprValue::IntLit(0),
                    span: token.span,
                };
                let span = Span::from_two(token.span, rhs.span);
                Some(Expr {
                    value: ExprValue::BinaryOp {
                        lhs: Box::new(lhs),
                        op: BinaryOp::Sub,
                        rhs: Box::new(rhs),
                    },
                    span,
                })
            }
            _ => self.parse_primary(),
        }
    }
    fn parse_multiplicative(&mut self) -> Option<Expr> {
        assert!(self.peek().is_some());
        let mut left = self.parse_unary()?;
        while let Some(t) = self.peek() {
            let op = match t.value {
                TokenValue::Star => BinaryOp::Mul,
                TokenValue::Slash => BinaryOp::Div,
                TokenValue::Percent => BinaryOp::Mod,
                _ => break,
            };
            self.advance(1);
            let right = match self.peek() {
                Some(_) => self.parse_unary()?,
                None => {
                    self.diagnostics
                        .add_from_frontend_error(ParseError::ExpectButEof("expression"), left.span);
                    return None;
                }
            };
            let span = Span::from_two(left.span, right.span);
            left = Expr {
                value: ExprValue::BinaryOp {
                    lhs: Box::new(left),
                    op,
                    rhs: Box::new(right),
                },
                span,
            };
        }
        Some(left)
    }
    fn parse_additive(&mut self) -> Option<Expr> {
        assert!(self.peek().is_some());
        let mut left = self.parse_multiplicative()?;
        while let Some(t) = self.peek() {
            let op = match t.value {
                TokenValue::Plus => BinaryOp::Add,
                TokenValue::Minus => BinaryOp::Sub,
                _ => break,
            };
            self.advance(1);
            let right = match self.peek() {
                Some(_) => self.parse_multiplicative()?,
                None => {
                    self.diagnostics
                        .add_from_frontend_error(ParseError::ExpectButEof("expression"), left.span);
                    return None;
                }
            };
            let span = Span::from_two(left.span, right.span);
            left = Expr {
                value: ExprValue::BinaryOp {
                    lhs: Box::new(left),
                    op,
                    rhs: Box::new(right),
                },
                span,
            };
        }
        Some(left)
    }
    fn parse_relational(&mut self) -> Option<Expr> {
        assert!(self.peek().is_some());
        let mut left = self.parse_additive()?;
        while let Some(t) = self.peek() {
            let op = match t.value {
                TokenValue::Less => BinaryOp::Less,
                TokenValue::Greater => BinaryOp::Greater,
                TokenValue::LessEqual => BinaryOp::LessEqual,
                TokenValue::GreaterEqual => BinaryOp::GreaterEqual,
                TokenValue::DoubleEqual => BinaryOp::Equal,
                TokenValue::NotEqual => BinaryOp::NotEqual,
                _ => break,
            };
            self.advance(1);
            let right = match self.peek() {
                Some(_) => self.parse_additive()?,
                None => {
                    self.diagnostics
                        .add_from_frontend_error(ParseError::ExpectButEof("expression"), left.span);
                    return None;
                }
            };
            let span = Span::from_two(left.span, right.span);
            left = Expr {
                value: ExprValue::BinaryOp {
                    lhs: Box::new(left),
                    op,
                    rhs: Box::new(right),
                },
                span,
            };
        }
        Some(left)
    }
    fn parse_assign(&mut self) -> Option<Expr> {
        assert!(self.peek().is_some());
        let is_assign = matches!(
            (self.tokens.get(self.pos), self.tokens.get(self.pos + 1)),
            (
                Some(Token {
                    value: TokenValue::Ident(_),
                    ..
                }),
                Some(Token {
                    value: TokenValue::Equal,
                    ..
                })
            )
        );
        if !is_assign {
            return self.parse_relational();
        }

        let lvalue_token = self.next().unwrap().clone();
        let name = lvalue_token.value.as_ident().unwrap();
        self.advance(1);
        let rvalue = match self.peek() {
            Some(_) => self.parse_assign()?,
            None => {
                self.diagnostics.add_from_frontend_error(
                    ParseError::ExpectButEof("expression"),
                    lvalue_token.span,
                );
                return None;
            }
        };
        let lvalue = Expr {
            value: ExprValue::Var(name),
            span: lvalue_token.span,
        };
        let span = Span::from_two(lvalue.span, rvalue.span);
        Some(Expr {
            value: ExprValue::Assign {
                lvalue: Box::new(lvalue),
                rvalue: Box::new(rvalue),
            },
            span,
        })
    }
    /*
       expr
           := assign

       assign
           := relational
           | IDENT "=" assign

       relational
           := additive
           | relational "<" additive
           | relational ">" additive
           | relational "<=" additive
           | relational ">=" additive

       additive
           := multiplicative
           | additive "+" multiplicative
           | additive "-" multiplicative

       multiplicative
           := unary
           | multiplicative "*" unary
           | multiplicative "/" unary

       unary
           := primary
           | "+" unary
           | "-" unary

       primary
           := IDENT
           | NUMBER
           | "(" expr ")"
    */
    fn parse_expr(&mut self) -> Option<Expr> {
        assert!(self.peek().is_some());
        self.parse_assign()
    }
}

#[cfg(test)]
mod tests {
    use std::io::BufRead;
    use std::path::Path;
    use std::fs::File;
    use crate::ast::graph::AstGraphExt;
    use crate::frontend::lexer::Lexer;
    use crate::utils::case_list::CaseList;
    use crate::utils::num_sequence::NumberSequence;

    pub use super::*;
    fn test_case(case_str: &str) {
        let case_sequence = NumberSequence::from_str(case_str).unwrap();
        let case_list = CaseList::from_dir(&Path::new("./testcases")).unwrap();
        let mut error_case_cnt = 0;
        for case_no in case_sequence {
            let case_path = case_list.get_case_path(case_no).unwrap();
            println!("{}", case_path.display());
            let file = File::open(&case_path).unwrap();
            let mut buf_reader = std::io::BufReader::new(file);
            let mut lexer = Lexer::new();
            let mut full_text = String::new();
            loop {
                let mut line = String::new();
                let bytes_read = buf_reader.read_line(&mut line).unwrap();
                if bytes_read == 0 {
                    break;
                }
                full_text.push_str(&line);
                lexer.parse_next_str(&line);
            }
            let (tokens, diagnostics) = lexer.finish();
            let mut is_error = false;
            if !diagnostics.is_empty() {
                diagnostics.print(&format!("{}", case_path.display()), &full_text);
                is_error = true;
            }
            let mut parser = Parser::new(tokens, diagnostics);
            let compile_unit = parser.parse_compile_unit();
            let dot = compile_unit.to_dot();
            let case_name = case_list.get_case_name(case_no).unwrap().strip_suffix(".c").unwrap();
            std::fs::write(format!("output/{}.dot", case_name), dot).unwrap();
            if !parser.diagnostics.is_empty() {
                parser.diagnostics.print(&format!("{}", case_path.display()), &full_text);
                is_error = true;
            }
            if is_error {
                error_case_cnt += 1;
            }
        }
        if error_case_cnt > 0 {
            panic!("Found {} cases with errors", error_case_cnt);
        }

    }
    #[test]
    fn test_expr() {
        test_case("0-3,14-25");
        // test_case("0-3,14-25");
    }
}