//! Custom deep traversal for tag analysis.
//!
//! The default `ExpressionWalk` trait only visits direct children known to
//! `iter_children` / `iter_children_lists`, which misses many struct fields
//! (Select.from, Select.joins, Select.where_clause, etc.). This module
//! provides a comprehensive traversal that reaches ALL nested expressions.

use polyglot_sql::expressions::Expression;

/// Recursively collect ALL Select nodes from the expression tree,
/// including those inside subqueries, CTEs, unions, etc.
pub fn find_all_selects(expr: &Expression) -> Vec<&Expression> {
    let mut result = Vec::new();
    collect_selects(expr, &mut result);
    result
}

fn collect_selects<'a>(expr: &'a Expression, result: &mut Vec<&'a Expression>) {
    match expr {
        Expression::Select(sel) => {
            result.push(expr);
            for e in &sel.expressions {
                collect_selects(e, result);
            }
            if let Some(ref from) = sel.from {
                for e in &from.expressions {
                    collect_selects(e, result);
                }
            }
            for join in &sel.joins {
                collect_selects(&join.this, result);
                if let Some(ref on) = join.on {
                    collect_selects(on, result);
                }
            }
            if let Some(ref w) = sel.where_clause {
                collect_selects(&w.this, result);
            }
            if let Some(ref g) = sel.group_by {
                for e in &g.expressions {
                    collect_selects(e, result);
                }
            }
            if let Some(ref h) = sel.having {
                collect_selects(&h.this, result);
            }
            if let Some(ref with) = sel.with {
                for cte in &with.ctes {
                    collect_selects(&cte.this, result);
                }
            }
            for lv in &sel.lateral_views {
                collect_selects(&lv.this, result);
            }
        }
        Expression::Union(u) => {
            collect_selects(&u.left, result);
            collect_selects(&u.right, result);
        }
        Expression::Intersect(i) => {
            collect_selects(&i.left, result);
            collect_selects(&i.right, result);
        }
        Expression::Except(e) => {
            collect_selects(&e.left, result);
            collect_selects(&e.right, result);
        }
        Expression::Subquery(s) => {
            collect_selects(&s.this, result);
        }
        Expression::Paren(p) => {
            collect_selects(&p.this, result);
        }
        Expression::Alias(a) => {
            collect_selects(&a.this, result);
        }
        Expression::Cte(c) => {
            collect_selects(&c.this, result);
        }
        Expression::With(w) => {
            for cte in &w.ctes {
                collect_selects(&cte.this, result);
            }
        }
        Expression::CreateTable(ct) => {
            if let Some(ref as_select) = ct.as_select {
                collect_selects(as_select, result);
            }
            if let Some(ref with_cte) = ct.with_cte {
                for cte in &with_cte.ctes {
                    collect_selects(&cte.this, result);
                }
            }
        }
        Expression::CreateView(cv) => {
            collect_selects(&cv.query, result);
        }
        Expression::Insert(ins) => {
            if let Some(ref query) = ins.query {
                collect_selects(query, result);
            }
            if let Some(ref with) = ins.with {
                for cte in &with.ctes {
                    collect_selects(&cte.this, result);
                }
            }
        }
        Expression::Merge(m) => {
            collect_selects(&m.this, result);
            collect_selects(&m.using, result);
        }
        Expression::Delete(del) => {
            for join in &del.joins {
                collect_selects(&join.this, result);
            }
            if let Some(ref with) = del.with {
                for cte in &with.ctes {
                    collect_selects(&cte.this, result);
                }
            }
        }
        Expression::Update(upd) => {
            if let Some(ref from) = upd.from_clause {
                for e in &from.expressions {
                    collect_selects(e, result);
                }
            }
            for join in &upd.from_joins {
                collect_selects(&join.this, result);
            }
            for join in &upd.table_joins {
                collect_selects(&join.this, result);
            }
            if let Some(ref with) = upd.with {
                for cte in &with.ctes {
                    collect_selects(&cte.this, result);
                }
            }
        }
        _ => {}
    }
}

/// Comprehensive DFS over ALL reachable expression nodes, including struct fields
/// that the default traversal misses.
pub fn deep_dfs<'a, F>(expr: &'a Expression, visitor: &mut F)
where
    F: FnMut(&'a Expression),
{
    visitor(expr);

    match expr {
        Expression::Select(sel) => {
            for e in &sel.expressions {
                deep_dfs(e, visitor);
            }
            if let Some(ref from) = sel.from {
                for e in &from.expressions {
                    deep_dfs(e, visitor);
                }
            }
            for join in &sel.joins {
                deep_dfs(&join.this, visitor);
                if let Some(ref on) = join.on {
                    deep_dfs(on, visitor);
                }
            }
            if let Some(ref w) = sel.where_clause {
                deep_dfs(&w.this, visitor);
            }
            if let Some(ref g) = sel.group_by {
                for e in &g.expressions {
                    deep_dfs(e, visitor);
                }
            }
            if let Some(ref h) = sel.having {
                deep_dfs(&h.this, visitor);
            }
            if let Some(ref with) = sel.with {
                for cte in &with.ctes {
                    deep_dfs(&cte.this, visitor);
                }
            }
            if let Some(ref order_by) = sel.order_by {
                for o in &order_by.expressions {
                    deep_dfs(&o.this, visitor);
                }
            }
            if let Some(ref limit) = sel.limit {
                deep_dfs(&limit.this, visitor);
            }
            for lv in &sel.lateral_views {
                deep_dfs(&lv.this, visitor);
            }
        }
        Expression::Union(u) => {
            deep_dfs(&u.left, visitor);
            deep_dfs(&u.right, visitor);
        }
        Expression::Intersect(i) => {
            deep_dfs(&i.left, visitor);
            deep_dfs(&i.right, visitor);
        }
        Expression::Except(e) => {
            deep_dfs(&e.left, visitor);
            deep_dfs(&e.right, visitor);
        }
        Expression::Subquery(s) => {
            deep_dfs(&s.this, visitor);
        }
        Expression::Paren(p) => {
            deep_dfs(&p.this, visitor);
        }
        Expression::Alias(a) => {
            deep_dfs(&a.this, visitor);
        }
        Expression::Where(w) => {
            deep_dfs(&w.this, visitor);
        }
        Expression::Having(h) => {
            deep_dfs(&h.this, visitor);
        }
        Expression::Cte(c) => {
            deep_dfs(&c.this, visitor);
        }
        Expression::With(w) => {
            for cte in &w.ctes {
                deep_dfs(&cte.this, visitor);
            }
        }
        Expression::From(f) => {
            for e in &f.expressions {
                deep_dfs(e, visitor);
            }
        }
        Expression::Join(j) => {
            deep_dfs(&j.this, visitor);
            if let Some(ref on) = j.on {
                deep_dfs(on, visitor);
            }
        }
        Expression::Not(u) | Expression::Neg(u) | Expression::BitwiseNot(u) => {
            deep_dfs(&u.this, visitor);
        }
        Expression::And(op)
        | Expression::Or(op)
        | Expression::Add(op)
        | Expression::Sub(op)
        | Expression::Mul(op)
        | Expression::Div(op)
        | Expression::Mod(op)
        | Expression::Eq(op)
        | Expression::Neq(op)
        | Expression::Lt(op)
        | Expression::Lte(op)
        | Expression::Gt(op)
        | Expression::Gte(op)
        | Expression::BitwiseAnd(op)
        | Expression::BitwiseOr(op)
        | Expression::BitwiseXor(op)
        | Expression::Concat(op)
        | Expression::Is(op) => {
            deep_dfs(&op.left, visitor);
            deep_dfs(&op.right, visitor);
        }
        Expression::Like(op) | Expression::ILike(op) => {
            deep_dfs(&op.left, visitor);
            deep_dfs(&op.right, visitor);
        }
        Expression::Between(b) => {
            deep_dfs(&b.this, visitor);
            deep_dfs(&b.low, visitor);
            deep_dfs(&b.high, visitor);
        }
        Expression::In(i) => {
            deep_dfs(&i.this, visitor);
            for e in &i.expressions {
                deep_dfs(e, visitor);
            }
            if let Some(ref query) = i.query {
                deep_dfs(query, visitor);
            }
        }
        Expression::IsNull(i) => {
            deep_dfs(&i.this, visitor);
        }
        Expression::Dot(d) => {
            deep_dfs(&d.this, visitor);
        }
        Expression::Exists(e) => {
            deep_dfs(&e.this, visitor);
        }
        Expression::Case(c) => {
            if let Some(ref operand) = c.operand {
                deep_dfs(operand, visitor);
            }
            for w in &c.whens {
                deep_dfs(&w.0, visitor);
                deep_dfs(&w.1, visitor);
            }
            if let Some(ref else_) = c.else_ {
                deep_dfs(else_, visitor);
            }
        }
        Expression::Cast(c) | Expression::TryCast(c) | Expression::SafeCast(c) => {
            deep_dfs(&c.this, visitor);
        }
        Expression::Function(f) => {
            for a in &f.args {
                deep_dfs(a, visitor);
            }
        }
        Expression::AggregateFunction(f) => {
            for a in &f.args {
                deep_dfs(a, visitor);
            }
        }
        Expression::WindowFunction(wf) => {
            deep_dfs(&wf.this, visitor);
        }
        // Unary functions
        Expression::Upper(u)
        | Expression::Lower(u)
        | Expression::Length(u)
        | Expression::LTrim(u)
        | Expression::RTrim(u)
        | Expression::Reverse(u)
        | Expression::Abs(u)
        | Expression::Sqrt(u)
        | Expression::Cbrt(u)
        | Expression::Ln(u)
        | Expression::Exp(u)
        | Expression::Sign(u)
        | Expression::Initcap(u)
        | Expression::Ascii(u)
        | Expression::Chr(u)
        | Expression::Soundex(u)
        | Expression::ByteLength(u)
        | Expression::Hex(u)
        | Expression::LowerHex(u)
        | Expression::Unicode(u)
        | Expression::Radians(u)
        | Expression::Degrees(u)
        | Expression::Sin(u)
        | Expression::Cos(u)
        | Expression::Tan(u)
        | Expression::Asin(u)
        | Expression::Acos(u)
        | Expression::Atan(u)
        | Expression::IsNan(u)
        | Expression::IsInf(u)
        | Expression::Year(u)
        | Expression::Month(u)
        | Expression::Day(u)
        | Expression::Hour(u)
        | Expression::Minute(u)
        | Expression::Second(u)
        | Expression::DayOfWeek(u)
        | Expression::DayOfWeekIso(u)
        | Expression::DayOfMonth(u)
        | Expression::DayOfYear(u)
        | Expression::WeekOfYear(u)
        | Expression::Quarter(u)
        | Expression::Epoch(u)
        | Expression::EpochMs(u)
        | Expression::TimeStrToUnix(u)
        | Expression::Date(u)
        | Expression::Time(u)
        | Expression::Explode(u)
        | Expression::ExplodeOuter(u)
        | Expression::ArrayLength(u)
        | Expression::ArraySize(u)
        | Expression::Cardinality(u)
        | Expression::ArrayReverse(u)
        | Expression::ArrayDistinct(u)
        | Expression::ArrayFlatten(u)
        | Expression::ArrayCompact(u)
        | Expression::ToArray(u)
        | Expression::MapFromEntries(u)
        | Expression::MapKeys(u)
        | Expression::MapValues(u)
        | Expression::JsonArrayLength(u)
        | Expression::JsonKeys(u)
        | Expression::JsonType(u)
        | Expression::ParseJson(u)
        | Expression::ToJson(u)
        | Expression::Typeof(u)
        | Expression::BitwiseCount(u)
        | Expression::SHA(u)
        | Expression::SHA1Digest(u)
        | Expression::TimeToUnix(u)
        | Expression::DateFromUnixDate(u)
        | Expression::UnixDate(u)
        | Expression::UnixSeconds(u)
        | Expression::UnixMillis(u)
        | Expression::UnixMicros(u)
        | Expression::TimeStrToDate(u)
        | Expression::DateToDi(u)
        | Expression::DiToDate(u)
        | Expression::TsOrDiToDi(u)
        | Expression::TsOrDsToDatetime(u)
        | Expression::TsOrDsToTimestamp(u)
        | Expression::YearOfWeek(u)
        | Expression::YearOfWeekIso(u)
        | Expression::Int64(u)
        | Expression::JSONBool(u)
        | Expression::DateStrToDate(u)
        | Expression::DateToDateStr(u)
        | Expression::MD5NumberLower64(u)
        | Expression::MD5NumberUpper64(u) => {
            deep_dfs(&u.this, visitor);
        }
        Expression::SessionUser(_) => {}
        // Binary functions
        Expression::NullIf(b)
        | Expression::IfNull(b)
        | Expression::Nvl(b)
        | Expression::Power(b)
        | Expression::Contains(b)
        | Expression::StartsWith(b)
        | Expression::EndsWith(b)
        | Expression::Levenshtein(b)
        | Expression::ModFunc(b)
        | Expression::IntDiv(b)
        | Expression::Atan2(b)
        | Expression::AddMonths(b)
        | Expression::MonthsBetween(b)
        | Expression::NextDay(b)
        | Expression::UnixToTimeStr(b)
        | Expression::ArrayContains(b)
        | Expression::ArrayPosition(b)
        | Expression::ArrayAppend(b)
        | Expression::ArrayPrepend(b)
        | Expression::ArrayIntersect(b)
        | Expression::ArrayUnion(b)
        | Expression::ArrayExcept(b)
        | Expression::ArrayRemove(b)
        | Expression::StarMap(b)
        | Expression::MapFromArrays(b)
        | Expression::MapContainsKey(b)
        | Expression::ElementAt(b)
        | Expression::JsonMergePatch(b) => {
            deep_dfs(&b.this, visitor);
            deep_dfs(&b.expression, visitor);
        }
        // Aggregates (single-arg)
        Expression::Sum(a)
        | Expression::Avg(a)
        | Expression::Min(a)
        | Expression::Max(a)
        | Expression::ArrayAgg(a)
        | Expression::CountIf(a)
        | Expression::Stddev(a)
        | Expression::StddevPop(a)
        | Expression::StddevSamp(a)
        | Expression::Variance(a)
        | Expression::VarPop(a)
        | Expression::VarSamp(a)
        | Expression::Median(a)
        | Expression::Mode(a)
        | Expression::First(a)
        | Expression::Last(a)
        | Expression::AnyValue(a)
        | Expression::ApproxDistinct(a)
        | Expression::ApproxCountDistinct(a)
        | Expression::LogicalAnd(a)
        | Expression::LogicalOr(a)
        | Expression::Skewness(a)
        | Expression::ArrayConcatAgg(a)
        | Expression::ArrayUniqueAgg(a)
        | Expression::BoolXorAgg(a)
        | Expression::BitwiseAndAgg(a)
        | Expression::BitwiseOrAgg(a)
        | Expression::BitwiseXorAgg(a) => {
            deep_dfs(&a.this, visitor);
        }
        Expression::Count(c) => {
            if let Some(ref this) = c.this {
                deep_dfs(this, visitor);
            }
        }
        // VarArg functions
        Expression::Coalesce(v)
        | Expression::Greatest(v)
        | Expression::Least(v)
        | Expression::ArrayConcat(v)
        | Expression::MapConcat(v)
        | Expression::JsonArray(v)
        | Expression::ArrayZip(v) => {
            for e in &v.expressions {
                deep_dfs(e, visitor);
            }
        }
        Expression::Trim(t) => {
            deep_dfs(&t.this, visitor);
        }
        Expression::Replace(r) => {
            deep_dfs(&r.this, visitor);
            deep_dfs(&r.old, visitor);
            deep_dfs(&r.new, visitor);
        }
        Expression::Substring(s) => {
            deep_dfs(&s.this, visitor);
        }
        Expression::ConcatWs(c) => {
            deep_dfs(&c.separator, visitor);
            for e in &c.expressions {
                deep_dfs(e, visitor);
            }
        }
        Expression::IfFunc(i) => {
            deep_dfs(&i.condition, visitor);
            deep_dfs(&i.true_value, visitor);
            if let Some(ref fv) = i.false_value {
                deep_dfs(fv, visitor);
            }
        }
        Expression::Nvl2(n) => {
            deep_dfs(&n.this, visitor);
            deep_dfs(&n.true_value, visitor);
            deep_dfs(&n.false_value, visitor);
        }
        Expression::Extract(e) => {
            deep_dfs(&e.this, visitor);
        }
        Expression::GroupBy(g) => {
            for e in &g.expressions {
                deep_dfs(e, visitor);
            }
        }
        Expression::OrderBy(o) => {
            for e in &o.expressions {
                deep_dfs(&e.this, visitor);
            }
        }
        Expression::Limit(l) => {
            deep_dfs(&l.this, visitor);
        }
        Expression::Ordered(o) => {
            deep_dfs(&o.this, visitor);
        }
        Expression::Annotated(a) => {
            deep_dfs(&a.this, visitor);
        }
        // DDL/DML wrappers
        Expression::CreateTable(ct) => {
            if let Some(ref as_select) = ct.as_select {
                deep_dfs(as_select, visitor);
            }
            if let Some(ref with_cte) = ct.with_cte {
                for cte in &with_cte.ctes {
                    deep_dfs(&cte.this, visitor);
                }
            }
        }
        Expression::CreateView(cv) => {
            deep_dfs(&cv.query, visitor);
        }
        Expression::Insert(ins) => {
            if let Some(ref query) = ins.query {
                deep_dfs(query, visitor);
            }
            if let Some(ref with) = ins.with {
                for cte in &with.ctes {
                    deep_dfs(&cte.this, visitor);
                }
            }
        }
        Expression::Delete(del) => {
            if let Some(ref w) = del.where_clause {
                deep_dfs(&w.this, visitor);
            }
            for join in &del.joins {
                deep_dfs(&join.this, visitor);
                if let Some(ref on) = join.on {
                    deep_dfs(on, visitor);
                }
            }
            if let Some(ref with) = del.with {
                for cte in &with.ctes {
                    deep_dfs(&cte.this, visitor);
                }
            }
        }
        Expression::Update(upd) => {
            if let Some(ref w) = upd.where_clause {
                deep_dfs(&w.this, visitor);
            }
            if let Some(ref from) = upd.from_clause {
                for e in &from.expressions {
                    deep_dfs(e, visitor);
                }
            }
            for join in &upd.from_joins {
                deep_dfs(&join.this, visitor);
                if let Some(ref on) = join.on {
                    deep_dfs(on, visitor);
                }
            }
            for join in &upd.table_joins {
                deep_dfs(&join.this, visitor);
                if let Some(ref on) = join.on {
                    deep_dfs(on, visitor);
                }
            }
            if let Some(ref with) = upd.with {
                for cte in &with.ctes {
                    deep_dfs(&cte.this, visitor);
                }
            }
        }
        Expression::Merge(m) => {
            deep_dfs(&m.this, visitor);
            deep_dfs(&m.using, visitor);
            if let Some(ref on) = m.on {
                deep_dfs(on, visitor);
            }
        }
        _ => {}
    }
}
