use std::ops::Range;

use crate::{
    ParseError, Parser, ParserErrorKind,
    ast::{Expr, Ident, Path},
    lexer::Token,
};

#[derive(Debug)]
pub struct StructLiteralField<'a> {
    pub span: Range<usize>,
    pub column: Ident<'a>,
    pub value: Expr<'a>,
}

#[derive(Debug)]
pub enum InsertValues<'a> {
    Named(Vec<StructLiteralField<'a>>),
    Unnamed(Vec<Expr<'a>>),
}

#[derive(Debug)]
pub struct InsertIntoStmt<'a> {
    pub span: Range<usize>,
    pub table: Path<'a>,
    pub values: InsertValues<'a>,
}

impl<'a> Parser<'a> {
    pub(crate) fn parse_tuple_literal_exprs(&mut self) -> Result<Vec<Expr<'a>>, ParseError> {
        let mut exprs = Vec::new();
        loop {
            let tok = self.lexer.peek();
            match &tok.token {
                t if Self::is_expression_start(t) => {
                    match self.parse_expr() {
                        Ok(expr) => exprs.push(expr),
                        Err(err) => {
                            self.errors.push(err);
                            self.sync();
                        }
                    }
                    self.maybe_skip_comma();
                }
                Token::RParen => break,
                _ => {
                    let err = ParseError {
                        span: tok.span.clone(),
                        kind: ParserErrorKind::UnexpectedToken(format!(
                            "expected one of: expression, {}, but got {}",
                            Token::RParen.name(),
                            tok.token.name(),
                        )),
                    };
                    if tok.token == Token::Eof {
                        return Err(err);
                    }
                    self.errors.push(err);
                    self.sync();
                }
            }
        }
        Ok(exprs)
    }

    fn parse_unnamed_insert_values(&mut self) -> Result<Vec<Expr<'a>>, ParseError> {
        self.consume(&[Token::LParen])?;
        let exprs = self.parse_tuple_literal_exprs()?;
        self.consume(&[Token::RParen])?;
        Ok(exprs)
    }

    fn parse_struct_literal_field(&mut self) -> Result<StructLiteralField<'a>, ParseError> {
        let column = self.parse_ident()?;
        self.consume(&[Token::Colon])?;
        let value = self.parse_expr()?;
        Ok(StructLiteralField {
            span: column.span.start..value.span.end,
            column,
            value,
        })
    }

    pub(crate) fn parse_struct_literal_fields(
        &mut self,
    ) -> Result<Vec<StructLiteralField<'a>>, ParseError> {
        let mut values = Vec::new();
        loop {
            let tok = self.lexer.peek();
            match tok.token {
                Token::Identifier(_) => {
                    let value = self.parse_struct_literal_field()?;
                    values.push(value);
                }
                Token::Comma => self.lexer.advance(),
                Token::RBrace => break,
                _ => {
                    let err = ParseError {
                        span: tok.span.clone(),
                        kind: ParserErrorKind::unexpected_token(
                            &[Token::empty_ident(), Token::Comma, Token::RBrace],
                            &tok.token,
                        ),
                    };
                    if tok.token == Token::Eof {
                        return Err(err);
                    }
                    self.errors.push(err);
                    self.sync();
                }
            }
        }
        Ok(values)
    }

    fn parse_named_insert_values(&mut self) -> Result<Vec<StructLiteralField<'a>>, ParseError> {
        self.consume(&[Token::LBrace])?;
        let values = self.parse_struct_literal_fields()?;
        self.consume(&[Token::RBrace])?;
        Ok(values)
    }

    pub(crate) fn parse_insert_values(&mut self) -> Result<InsertValues<'a>, ParseError> {
        let tok = self.lexer.peek();
        match tok.token {
            Token::LParen => self
                .parse_unnamed_insert_values()
                .map(|exprs| InsertValues::Unnamed(exprs)),
            Token::LBrace => self
                .parse_named_insert_values()
                .map(|values| InsertValues::Named(values)),
            _ => Err(ParseError {
                span: tok.span.clone(),
                kind: ParserErrorKind::unexpected_token(
                    &[Token::LParen, Token::LBrace],
                    &tok.token,
                ),
            }),
        }
    }

    pub(crate) fn parse_insert_stmt(&mut self) -> Result<InsertIntoStmt<'a>, ParseError> {
        self.current_span.start = self.consume(&[Token::Insert])?.span.start;
        self.consume(&[Token::Into])?;
        let table = self.parse_path()?;
        let values = self.parse_insert_values()?;
        self.consume(&[Token::Semicolon])?;
        Ok(InsertIntoStmt {
            span: self.current_span.clone(),
            table,
            values,
        })
    }
}

#[cfg(test)]
mod tests {
    use crate::{ast::*, parse, tests::assert_no_errors};

    #[test]
    fn test_parse_insert_value() {
        let source = "insert into mydb.users { id: 42 };";
        let (stmts, errors) = parse(source);
        assert_no_errors(&errors);
        let Stmt::InsertInto(stmt) = &stmts[0] else {
            panic!("expected insert")
        };
        let InsertValues::Named(values) = &stmt.values else {
            panic!("expected named values");
        };
        assert_eq!(values[0].column.name, "id".into());
        assert!(matches!(values[0].value.kind, ExprKind::IntegerLiteral(ref s) if s == "42"));
    }

    #[test]
    fn test_parse_insert_value_list_multiple() {
        let source = r#"insert into mydb.users { id: 1, username: "john" };"#;
        let (stmts, errors) = parse(source);
        assert_no_errors(&errors);
        let Stmt::InsertInto(stmt) = &stmts[0] else {
            panic!("expected insert")
        };
        let InsertValues::Named(values) = &stmt.values else {
            panic!("expected named values");
        };
        assert_eq!(values.len(), 2);
        assert_eq!(values[1].column.name, "username".into());
        assert!(matches!(values[1].value.kind, ExprKind::StringLiteral(ref s) if s == "john"));
    }

    #[test]
    fn test_parse_positional_insert_single() {
        let source = "insert into mydb.points (42);";
        let (stmts, errors) = parse(source);
        assert_no_errors(&errors);
        let Stmt::InsertInto(stmt) = &stmts[0] else {
            panic!("expected insert")
        };
        let InsertValues::Unnamed(exprs) = &stmt.values else {
            panic!("expected positional values, got {:?}", stmt.values);
        };
        assert_eq!(exprs.len(), 1);
        assert!(matches!(exprs[0].kind, ExprKind::IntegerLiteral(ref s) if s == "42"));
    }

    #[test]
    fn test_parse_positional_insert_multiple() {
        let source = r#"insert into mydb.points (1, "hello");"#;
        let (stmts, errors) = parse(source);
        assert_no_errors(&errors);
        let Stmt::InsertInto(stmt) = &stmts[0] else {
            panic!("expected insert")
        };
        let InsertValues::Unnamed(exprs) = &stmt.values else {
            panic!("expected positional values, got {:?}", stmt.values);
        };
        assert_eq!(exprs.len(), 2);
        assert!(matches!(exprs[0].kind, ExprKind::IntegerLiteral(ref s) if s == "1"));
        assert!(matches!(exprs[1].kind, ExprKind::StringLiteral(ref s) if s == "hello"));
    }

    #[test]
    fn test_parse_insert_stmt_table_path() {
        let source = "insert into mydb.users { id: 1 };";
        let (stmts, errors) = parse(source);
        assert_no_errors(&errors);
        let Stmt::InsertInto(stmt) = &stmts[0] else {
            panic!("expected insert")
        };
        assert_eq!(stmt.table.database().unwrap().name, "mydb".into());
        assert_eq!(stmt.table.name().unwrap().name, "users".into());
    }
}
