use crate::error::{Result, SqlError};
use crate::types::{ColumnDef, DataType, Value};
use sqlparser::ast::{ColumnDef as AstColumnDef, DataType as AstDataType, Expr, Statement};
use sqlparser::dialect::GenericDialect;
use sqlparser::parser::Parser;

/// Parsed SQL statement
#[derive(Debug, Clone)]
pub enum SqlStatement {
    CreateTable {
        table_name: String,
        columns: Vec<ColumnDef>,
        primary_key: Vec<String>,
    },
    DropTable {
        table_name: String,
    },
    Insert {
        table_name: String,
        columns: Vec<String>,
        values: Vec<Value>,
    },
    Select {
        table_name: String,
        columns: Vec<String>, // Empty means SELECT *
        where_clause: Option<WhereClause>,
    },
    Update {
        table_name: String,
        assignments: Vec<(String, Value)>,
        where_clause: Option<WhereClause>,
    },
    Delete {
        table_name: String,
        where_clause: Option<WhereClause>,
    },
}

/// WHERE clause representation
#[derive(Debug, Clone)]
pub enum WhereClause {
    Comparison {
        column: String,
        op: ComparisonOp,
        value: Value,
    },
    And(Box<WhereClause>, Box<WhereClause>),
    Or(Box<WhereClause>, Box<WhereClause>),
}

#[derive(Debug, Clone)]
pub enum ComparisonOp {
    Eq,
    Ne,
    Lt,
    Le,
    Gt,
    Ge,
}

/// Parse SQL string into SqlStatement
pub fn parse_sql(sql: &str) -> Result<SqlStatement> {
    let dialect = GenericDialect {};
    let statements = Parser::parse_sql(&dialect, sql)
        .map_err(|e| SqlError::ParseError(format!("Parse error: {}", e)))?;

    if statements.is_empty() {
        return Err(SqlError::ParseError("No statement found".to_string()));
    }

    if statements.len() > 1 {
        return Err(SqlError::ParseError(
            "Multiple statements not supported".to_string(),
        ));
    }

    let statement = &statements[0];
    parse_statement(statement)
}

fn parse_statement(stmt: &Statement) -> Result<SqlStatement> {
    match stmt {
        Statement::CreateTable { .. } => parse_create_table(stmt),
        Statement::Drop { names, .. } => {
            if names.len() != 1 {
                return Err(SqlError::ParseError(
                    "Expected single table name".to_string(),
                ));
            }
            Ok(SqlStatement::DropTable {
                table_name: names[0].to_string(),
            })
        }
        Statement::Insert { .. } => parse_insert(stmt),
        Statement::Query(query) => parse_select(query),
        Statement::Update { .. } => parse_update(stmt),
        Statement::Delete { .. } => parse_delete(stmt),
        _ => Err(SqlError::ParseError(format!(
            "Unsupported statement: {:?}",
            stmt
        ))),
    }
}

fn parse_create_table(stmt: &Statement) -> Result<SqlStatement> {
    let Statement::CreateTable {
        name,
        columns: col_defs,
        constraints,
        ..
    } = stmt
    else {
        return Err(SqlError::ParseError(
            "Expected CREATE TABLE statement".to_string(),
        ));
    };

    let table_name = name.to_string();
    let mut columns = Vec::new();
    let mut primary_key = Vec::new();

    for column in col_defs {
        let col_def = parse_column_def(column)?;
        columns.push(col_def);
    }

    // Extract primary key from constraints
    for constraint in constraints {
        if let sqlparser::ast::TableConstraint::Unique {
            columns: pk_cols,
            is_primary: true,
            ..
        } = constraint
        {
            for pk_col in pk_cols {
                primary_key.push(pk_col.value.to_string());
            }
        }
    }

    // If no explicit PRIMARY KEY constraint, check for PRIMARY KEY in column definitions
    if primary_key.is_empty() {
        for column in col_defs {
            for option in &column.options {
                if matches!(
                    option.option,
                    sqlparser::ast::ColumnOption::Unique { is_primary: true }
                ) {
                    primary_key.push(column.name.value.to_string());
                    break;
                }
            }
        }
    }

    if primary_key.is_empty() {
        return Err(SqlError::ParseError("PRIMARY KEY is required".to_string()));
    }

    Ok(SqlStatement::CreateTable {
        table_name,
        columns,
        primary_key,
    })
}

fn parse_column_def(col: &AstColumnDef) -> Result<ColumnDef> {
    let name = col.name.value.to_string();
    let data_type = parse_data_type(&col.data_type)?;
    let mut nullable = true;
    let mut default_value = None;

    for option in &col.options {
        match &option.option {
            sqlparser::ast::ColumnOption::NotNull => nullable = false,
            sqlparser::ast::ColumnOption::Null => nullable = true,
            sqlparser::ast::ColumnOption::Default(expr) => {
                default_value = Some(parse_expr_as_value(expr)?);
            }
            _ => {}
        }
    }

    Ok(ColumnDef {
        name,
        data_type,
        nullable,
        default_value,
    })
}

fn parse_data_type(dt: &AstDataType) -> Result<DataType> {
    match dt {
        AstDataType::Int(_) | AstDataType::Integer(_) | AstDataType::SmallInt(_) => {
            Ok(DataType::Integer)
        }
        AstDataType::BigInt(_) => Ok(DataType::BigInt),
        AstDataType::Text | AstDataType::Varchar(_) | AstDataType::Char(_) => Ok(DataType::Text),
        AstDataType::Boolean => Ok(DataType::Boolean),
        AstDataType::Timestamp(_, _) => Ok(DataType::Timestamp),
        _ => Err(SqlError::ParseError(format!(
            "Unsupported data type: {:?}",
            dt
        ))),
    }
}

fn parse_insert(stmt: &Statement) -> Result<SqlStatement> {
    let Statement::Insert {
        table_name,
        columns: col_idents,
        source,
        ..
    } = stmt
    else {
        return Err(SqlError::ParseError(
            "Expected INSERT statement".to_string(),
        ));
    };

    let table_name = table_name.to_string();

    let columns: Vec<String> = col_idents.iter().map(|c| c.value.to_string()).collect();

    // Extract values from the first VALUES row
    if let sqlparser::ast::SetExpr::Values(values) = source.body.as_ref() {
        if values.rows.is_empty() {
            return Err(SqlError::ParseError("No values provided".to_string()));
        }

        let first_row = &values.rows[0];
        let mut parsed_values = Vec::new();

        for expr in first_row {
            parsed_values.push(parse_expr_as_value(expr)?);
        }

        Ok(SqlStatement::Insert {
            table_name,
            columns,
            values: parsed_values,
        })
    } else {
        Err(SqlError::ParseError("Expected VALUES clause".to_string()))
    }
}

fn parse_select(query: &sqlparser::ast::Query) -> Result<SqlStatement> {
    // For simplicity, only handle basic SELECT FROM WHERE
    if let sqlparser::ast::SetExpr::Select(select) = query.body.as_ref() {
        // Extract table name
        if select.from.is_empty() {
            return Err(SqlError::ParseError("No FROM clause".to_string()));
        }

        let table_name = match &select.from[0].relation {
            sqlparser::ast::TableFactor::Table { name, .. } => name.to_string(),
            _ => {
                return Err(SqlError::ParseError(
                    "Complex FROM clauses not supported".to_string(),
                ))
            }
        };

        // Extract columns
        let columns: Vec<String> = select
            .projection
            .iter()
            .filter_map(|item| match item {
                sqlparser::ast::SelectItem::UnnamedExpr(Expr::Identifier(ident)) => {
                    Some(ident.value.to_string())
                }
                sqlparser::ast::SelectItem::Wildcard(_) => None, // SELECT * returns empty vec
                _ => None,
            })
            .collect();

        // Parse WHERE clause if present
        let where_clause = if let Some(expr) = &select.selection {
            Some(parse_where_expr(expr)?)
        } else {
            None
        };

        Ok(SqlStatement::Select {
            table_name,
            columns,
            where_clause,
        })
    } else {
        Err(SqlError::ParseError(
            "Only SELECT queries supported".to_string(),
        ))
    }
}

fn parse_update(stmt: &Statement) -> Result<SqlStatement> {
    let Statement::Update {
        table,
        assignments,
        from,
        selection,
        returning,
    } = stmt
    else {
        return Err(SqlError::ParseError(
            "Expected UPDATE statement".to_string(),
        ));
    };

    if from.is_some() {
        return Err(SqlError::ParseError(
            "UPDATE ... FROM is not supported".to_string(),
        ));
    }
    if returning.is_some() {
        return Err(SqlError::ParseError(
            "UPDATE ... RETURNING is not supported".to_string(),
        ));
    }
    if !table.joins.is_empty() {
        return Err(SqlError::ParseError(
            "UPDATE with joins is not supported".to_string(),
        ));
    }

    let table_name = match &table.relation {
        sqlparser::ast::TableFactor::Table { name, .. } => name.to_string(),
        _ => {
            return Err(SqlError::ParseError(
                "Complex UPDATE targets are not supported".to_string(),
            ))
        }
    };

    let mut parsed_assignments = Vec::with_capacity(assignments.len());
    for assignment in assignments {
        if assignment.id.len() != 1 {
            return Err(SqlError::ParseError(
                "Only simple column assignments are supported".to_string(),
            ));
        }
        let column = assignment.id[0].value.to_string();
        let value = parse_expr_as_value(&assignment.value)?;
        parsed_assignments.push((column, value));
    }

    let where_clause = if let Some(expr) = selection {
        Some(parse_where_expr(expr)?)
    } else {
        None
    };

    Ok(SqlStatement::Update {
        table_name,
        assignments: parsed_assignments,
        where_clause,
    })
}

fn parse_delete(stmt: &Statement) -> Result<SqlStatement> {
    let Statement::Delete {
        tables,
        from,
        using,
        selection,
        returning,
        order_by,
        limit,
    } = stmt
    else {
        return Err(SqlError::ParseError(
            "Expected DELETE statement".to_string(),
        ));
    };

    if !tables.is_empty() {
        return Err(SqlError::ParseError(
            "Multi-table DELETE is not supported".to_string(),
        ));
    }
    if using.is_some() {
        return Err(SqlError::ParseError(
            "DELETE ... USING is not supported".to_string(),
        ));
    }
    if returning.is_some() {
        return Err(SqlError::ParseError(
            "DELETE ... RETURNING is not supported".to_string(),
        ));
    }
    if !order_by.is_empty() {
        return Err(SqlError::ParseError(
            "DELETE ... ORDER BY is not supported".to_string(),
        ));
    }
    if limit.is_some() {
        return Err(SqlError::ParseError(
            "DELETE ... LIMIT is not supported".to_string(),
        ));
    }
    if from.len() != 1 {
        return Err(SqlError::ParseError(
            "DELETE must target exactly one table".to_string(),
        ));
    }

    let table = &from[0];
    if !table.joins.is_empty() {
        return Err(SqlError::ParseError(
            "DELETE with joins is not supported".to_string(),
        ));
    }

    let table_name = match &table.relation {
        sqlparser::ast::TableFactor::Table { name, .. } => name.to_string(),
        _ => {
            return Err(SqlError::ParseError(
                "Complex DELETE targets are not supported".to_string(),
            ))
        }
    };

    let where_clause = if let Some(expr) = selection {
        Some(parse_where_expr(expr)?)
    } else {
        None
    };

    Ok(SqlStatement::Delete {
        table_name,
        where_clause,
    })
}

fn parse_where_expr(expr: &Expr) -> Result<WhereClause> {
    match expr {
        Expr::BinaryOp { left, op, right } => {
            use sqlparser::ast::BinaryOperator;
            match op {
                BinaryOperator::Eq
                | BinaryOperator::NotEq
                | BinaryOperator::Lt
                | BinaryOperator::LtEq
                | BinaryOperator::Gt
                | BinaryOperator::GtEq => {
                    let column = if let Expr::Identifier(ident) = left.as_ref() {
                        ident.value.to_string()
                    } else {
                        return Err(SqlError::ParseError(
                            "Left side of comparison must be column name".to_string(),
                        ));
                    };

                    let value = parse_expr_as_value(right)?;

                    let op = match op {
                        BinaryOperator::Eq => ComparisonOp::Eq,
                        BinaryOperator::NotEq => ComparisonOp::Ne,
                        BinaryOperator::Lt => ComparisonOp::Lt,
                        BinaryOperator::LtEq => ComparisonOp::Le,
                        BinaryOperator::Gt => ComparisonOp::Gt,
                        BinaryOperator::GtEq => ComparisonOp::Ge,
                        _ => unreachable!(),
                    };

                    Ok(WhereClause::Comparison { column, op, value })
                }
                BinaryOperator::And => {
                    let left_clause = parse_where_expr(left)?;
                    let right_clause = parse_where_expr(right)?;
                    Ok(WhereClause::And(
                        Box::new(left_clause),
                        Box::new(right_clause),
                    ))
                }
                BinaryOperator::Or => {
                    let left_clause = parse_where_expr(left)?;
                    let right_clause = parse_where_expr(right)?;
                    Ok(WhereClause::Or(
                        Box::new(left_clause),
                        Box::new(right_clause),
                    ))
                }
                _ => Err(SqlError::ParseError(format!(
                    "Unsupported operator: {:?}",
                    op
                ))),
            }
        }
        _ => Err(SqlError::ParseError(format!(
            "Unsupported WHERE expression: {:?}",
            expr
        ))),
    }
}

fn parse_expr_as_value(expr: &Expr) -> Result<Value> {
    match expr {
        Expr::Value(sqlparser::ast::Value::Number(n, _)) => {
            if let Ok(i) = n.parse::<i64>() {
                Ok(Value::Integer(i))
            } else {
                Err(SqlError::ParseError(format!("Invalid number: {}", n)))
            }
        }
        Expr::Value(sqlparser::ast::Value::SingleQuotedString(s)) => Ok(Value::Text(s.clone())),
        Expr::Value(sqlparser::ast::Value::Boolean(b)) => Ok(Value::Boolean(*b)),
        Expr::Value(sqlparser::ast::Value::Null) => Ok(Value::Null),
        _ => Err(SqlError::ParseError(format!(
            "Unsupported value expression: {:?}",
            expr
        ))),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_update_statement() {
        let parsed = parse_sql("UPDATE users SET name = 'Bob', active = true WHERE id = 7")
            .expect("update should parse");

        match parsed {
            SqlStatement::Update {
                table_name,
                assignments,
                where_clause,
            } => {
                assert_eq!(table_name, "users");
                assert_eq!(
                    assignments,
                    vec![
                        ("name".to_string(), Value::Text("Bob".to_string())),
                        ("active".to_string(), Value::Boolean(true)),
                    ]
                );
                assert!(matches!(
                    where_clause,
                    Some(WhereClause::Comparison {
                        column,
                        op: ComparisonOp::Eq,
                        value: Value::Integer(7),
                    }) if column == "id"
                ));
            }
            other => panic!("expected update statement, got {:?}", other),
        }
    }

    #[test]
    fn parses_delete_statement() {
        let parsed =
            parse_sql("DELETE FROM users WHERE name = 'Alice'").expect("delete should parse");

        match parsed {
            SqlStatement::Delete {
                table_name,
                where_clause,
            } => {
                assert_eq!(table_name, "users");
                assert!(matches!(
                    where_clause,
                    Some(WhereClause::Comparison {
                        column,
                        op: ComparisonOp::Eq,
                        value: Value::Text(value),
                    }) if column == "name" && value == "Alice"
                ));
            }
            other => panic!("expected delete statement, got {:?}", other),
        }
    }
}
