use crate::parser::ast::*;
use crate::parser::error::{Expected, ParseResult};
use crate::parser::state::Parser;
use crate::parser::token::Keyword;

pub fn parse_alter(parser: &mut Parser) -> ParseResult<Statement> {
    if parser.at_keyword(Keyword::Table) {
        let _ = parser.next();
        let table = super::parse_multipart_name(parser)?;
        if parser.at_keyword(Keyword::Add) {
            let _ = parser.next();
            if parser.at_keyword(Keyword::Constraint) {
                let _ = parser.next();
                let constraint = super::ddl::parse_alter_table_add_constraint(parser)?;
                Ok(Statement::Ddl(DdlStatement::AlterTable {
                    table,
                    action: AlterTableAction::AddConstraint(constraint),
                }))
            } else {
                let col = super::ddl::parse_column_def(parser)?;
                Ok(Statement::Ddl(DdlStatement::AlterTable {
                    table,
                    action: AlterTableAction::AddColumn(col),
                }))
            }
        } else if parser.at_keyword(Keyword::Alter) {
            let _ = parser.next();
            parser.expect_keyword(Keyword::Column)?;
            let col_name = match parser.next() {
                Some(Token::Identifier(id)) => id.clone(),
                Some(Token::Keyword(k)) => k.as_ref().to_string(),
                _ => return parser.backtrack(Expected::Description("column name")),
            };
            let data_type = crate::parser::parse::expressions::parse_data_type(parser)?;
            let mut nullable = None;
            if parser.at_keyword(Keyword::Not) {
                let _ = parser.next();
                parser.expect_keyword(Keyword::Null)?;
                nullable = Some(false);
            } else if parser.at_keyword(Keyword::Null) {
                let _ = parser.next();
                nullable = Some(true);
            }
            Ok(Statement::Ddl(DdlStatement::AlterTable {
                table,
                action: AlterTableAction::AlterColumn {
                    name: col_name,
                    data_type,
                    nullable,
                },
            }))
        } else if parser.at_keyword(Keyword::Drop) {
            let _ = parser.next();
            if parser.at_keyword(Keyword::Column) {
                let _ = parser.next();
                let col_name = match parser.next() {
                    Some(Token::Identifier(id)) => id.clone(),
                    Some(Token::Keyword(k)) => k.as_ref().to_string(),
                    _ => return parser.backtrack(Expected::Description("column name")),
                };
                Ok(Statement::Ddl(DdlStatement::AlterTable {
                    table,
                    action: AlterTableAction::DropColumn(col_name),
                }))
            } else if parser.at_keyword(Keyword::Constraint) {
                let _ = parser.next();
                let constraint_name = match parser.next() {
                    Some(Token::Identifier(id)) => id.clone(),
                    Some(Token::Keyword(k)) => k.as_ref().to_string(),
                    _ => return parser.backtrack(Expected::Description("constraint name")),
                };
                Ok(Statement::Ddl(DdlStatement::AlterTable {
                    table,
                    action: AlterTableAction::DropConstraint(constraint_name),
                }))
            } else {
                parser.backtrack(Expected::Description("column or constraint"))
            }
        } else {
            parser.backtrack(Expected::Description("add or drop"))
        }
    } else {
        parser.backtrack(Expected::Description("table"))
    }
}
