use crate::{
    condition::ConditionKind,
    group::{Conjunction, Node},
    query::Query,
    value::Value,
};

pub fn build_conditions(query: &Query, start: usize) -> (String, Vec<Value>) {
    let mut values: Vec<Value> = vec![];
    let mut n = start;
    let parts: Vec<String> = query.nodes.iter()
        .map(|node| build_node(node, &mut n, &mut values))
        .collect();
    (parts.join(" AND "), values)
}

fn build_node(node: &Node, n: &mut usize, values: &mut Vec<Value>) -> String {
    match node {
        Node::Cond(c) => build_cond(&c.kind, &c.values, n, values),
        Node::Group(g) => {
            let sep = match g.conjunction {
                Conjunction::And => " AND ",
                Conjunction::Or => " OR ",
            };
            let parts: Vec<String> = g.nodes.iter()
                .map(|node| build_node(node, n, values))
                .collect();
            format!("({})", parts.join(sep))
        }
    }
}

fn build_cond(kind: &ConditionKind, vals: &[Value], n: &mut usize, values: &mut Vec<Value>) -> String {
    match kind {
        ConditionKind::Eq(col) => { let s = format!("{} = ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::Ne(col) => { let s = format!("{} != ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::Gt(col) => { let s = format!("{} > ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::Gte(col) => { let s = format!("{} >= ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::Lt(col) => { let s = format!("{} < ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::Lte(col) => { let s = format!("{} <= ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::IsNull(col) => format!("{} IS NULL", col),
        ConditionKind::IsNotNull(col) => format!("{} IS NOT NULL", col),
        ConditionKind::FilterOptional(col) => {
            let s = format!("(?{0} IS NULL OR {1} = ?{0})", n, col);
            values.push(vals[0].clone()); *n += 1; s
        }
        ConditionKind::FilterOptionalGte(col) => {
            let s = format!("(?{0} IS NULL OR {1} >= ?{0})", n, col);
            values.push(vals[0].clone()); *n += 1; s
        }
        ConditionKind::FilterOptionalLte(col) => {
            let s = format!("(?{0} IS NULL OR {1} <= ?{0})", n, col);
            values.push(vals[0].clone()); *n += 1; s
        }
        ConditionKind::In(col) => {
            let phs: Vec<String> = (0..vals.len()).map(|i| format!("?{}", *n + i)).collect();
            let s = format!("{} IN ({})", col, phs.join(", "));
            values.extend(vals.iter().cloned());
            *n += vals.len();
            s
        }
        ConditionKind::NotIn(col) => {
            let phs: Vec<String> = (0..vals.len()).map(|i| format!("?{}", *n + i)).collect();
            let s = format!("{} NOT IN ({})", col, phs.join(", "));
            values.extend(vals.iter().cloned());
            *n += vals.len();
            s
        }
        ConditionKind::Like(col) => { let s = format!("{} LIKE ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::NotLike(col) => { let s = format!("{} NOT LIKE ?{}", col, n); values.push(vals[0].clone()); *n += 1; s }
        ConditionKind::Between(col) => {
            let s = format!("{} BETWEEN ?{} AND ?{}", col, *n, *n + 1);
            values.push(vals[0].clone());
            values.push(vals[1].clone());
            *n += 2;
            s
        }
    }
}

pub fn build_tail(query: &Query) -> String {
    let mut sql = String::new();
    if let Some((col, dir)) = &query.order {
        sql.push_str(&format!(" ORDER BY {} {}", col, dir.as_sql()));
    }
    if let Some(l) = query.limit {
        sql.push_str(&format!(" LIMIT {}", l));
    }
    if let Some(o) = query.offset {
        sql.push_str(&format!(" OFFSET {}", o));
    }
    sql
}
