use crate::like::LikeExpression;
use crate::raw_sql::RawSql;
use crate::value::{ConditionValue, Op, Value};
use crate::where_clause::{IntoIncluded, IntoRangeClause, WhereClause};

#[derive(Debug, Clone)]
pub enum SortDir {
    Asc,
    Desc,
}

#[derive(Debug, Clone)]
pub enum OrderByClause<V: Clone = Value> {
    /// A column reference with a sort direction.
    Col { col: Col, dir: SortDir },
    /// A raw SQL expression rendered as-is (e.g., `"RAND()"`, `"id DESC NULLS FIRST"`).
    Expr(RawSql<V>),
}

impl<V: Clone> OrderByClause<V> {
    /// Convert an `OrderByClause<Value>` into `OrderByClause<V>` for any `V`.
    pub fn from_default(clause: OrderByClause) -> Self {
        match clause {
            OrderByClause::Col { col, dir } => OrderByClause::Col { col, dir },
            OrderByClause::Expr(raw) => {
                assert!(
                    raw.binds.is_empty(),
                    "Cannot convert OrderByClause::Expr with binds to a different value type"
                );
                OrderByClause::Expr(RawSql::new(raw.as_str()))
            }
        }
    }

    /// Transform all bind values in this clause.
    pub fn map_values<U: Clone>(self, f: &dyn Fn(V) -> U) -> OrderByClause<U> {
        match self {
            OrderByClause::Col { col, dir } => OrderByClause::Col { col, dir },
            OrderByClause::Expr(raw) => OrderByClause::Expr(raw.map_values(f)),
        }
    }
}

/// Backwards-compatible alias — `ColRef` is now just `Col`.
pub type ColRef = Col;

/// A table reference for building qualified column references and join targets.
#[derive(Debug, Clone)]
pub struct TableRef {
    pub name: String,
    pub(crate) alias: Option<String>,
}

/// Create a table reference for qualified column names.
pub fn table(name: &str) -> TableRef {
    TableRef {
        name: name.to_string(),
        alias: None,
    }
}

impl TableRef {
    pub fn col(&self, col: &str) -> Col {
        Col {
            table: Some(self.name.clone()),
            column: col.to_string(),
            alias: None,
            aggregate: None,
        }
    }

    /// Create multiple qualified column references at once.
    pub fn cols(&self, cols: &[&str]) -> Vec<Col> {
        cols.iter()
            .map(|c| Col {
                table: Some(self.name.clone()),
                column: c.to_string(),
                alias: None,
                aggregate: None,
            })
            .collect()
    }

    pub fn as_(mut self, alias: &str) -> Self {
        self.alias = Some(alias.to_string());
        self
    }
}

/// A column reference, optionally qualified with a table name and/or aliased.
///
/// - `col("name")` creates an unqualified column.
/// - `table("users").col("name")` creates a table-qualified column.
/// - `col("name").as_("n")` creates an aliased column.
///
/// Both forms support the same set of methods (e.g., `eq`, `asc`).
#[derive(Debug, Clone)]
pub struct Col {
    pub table: Option<String>,
    pub column: String,
    pub alias: Option<String>,
    /// When set, this Col represents an aggregate function call (e.g., `COUNT(*)`, `SUM("price")`).
    /// The inner `Option<Box<Col>>` is the function argument (`None` means `*`).
    pub(crate) aggregate: Option<(SelectFunc, Option<Box<Col>>)>,
}

/// Create a column reference.
pub fn col(name: &str) -> Col {
    Col {
        table: None,
        column: name.to_string(),
        alias: None,
        aggregate: None,
    }
}

impl From<&str> for Col {
    fn from(name: &str) -> Self {
        Col {
            table: None,
            column: name.to_string(),
            alias: None,
            aggregate: None,
        }
    }
}

/// Trait for the right-hand side of a comparison condition.
///
/// Implemented for [`Col`] (column-to-column comparison) and for all types
/// that implement [`ConditionValue`] (value comparison with bind parameters).
///
/// The associated type `Output` determines the return type of
/// [`ConditionExpr::eq`] and friends:
/// - `Col` → [`ColCondition`] (usable in both JOIN ON and WHERE clauses)
/// - value types → [`WhereClause<V>`]
pub trait ConditionRhs {
    type Output;
    fn apply_condition(self, col: Col, op: Op) -> Self::Output;
}

impl ConditionRhs for Col {
    type Output = ColCondition;
    fn apply_condition(self, left: Col, op: Op) -> ColCondition {
        ColCondition {
            left,
            op,
            right: self,
        }
    }
}

impl<V: ConditionValue> ConditionRhs for V {
    type Output = WhereClause<V>;
    fn apply_condition(self, col: Col, op: Op) -> WhereClause<V> {
        WhereClause::Condition { col, op, val: self }
    }
}

/// Trait for types that can produce WHERE/HAVING conditions.
///
/// Implemented by [`Col`] and [`SelectItem`]. Adding a new condition method
/// here automatically makes it available on both types — no manual delegation
/// needed.
pub trait ConditionExpr: Sized {
    /// Convert this expression into a `Col` for use in WHERE/HAVING clauses.
    fn into_condition_col(self) -> Col;

    /// Generate an equality (`=`) condition.
    ///
    /// Accepts either a value (producing a bind-parameter condition) or a
    /// [`Col`] (producing a column-to-column comparison):
    /// - `col("id").eq(1)` → `"id" = ?`
    /// - `col("id").eq(col("other_id"))` → `"id" = "other_id"`
    fn eq<R: ConditionRhs>(self, rhs: R) -> R::Output {
        rhs.apply_condition(self.into_condition_col(), Op::Eq)
    }

    /// Generate an inequality (`!=`) condition.
    ///
    /// When used with a [`Col`] argument, the resulting [`ColCondition`] can be
    /// used in WHERE clauses but **not** in JOIN ON (which only supports equality).
    fn ne<R: ConditionRhs>(self, rhs: R) -> R::Output {
        rhs.apply_condition(self.into_condition_col(), Op::Ne)
    }

    /// Generate a greater-than (`>`) condition.
    ///
    /// When used with a [`Col`] argument, the resulting [`ColCondition`] can be
    /// used in WHERE clauses but **not** in JOIN ON (which only supports equality).
    fn gt<R: ConditionRhs>(self, rhs: R) -> R::Output {
        rhs.apply_condition(self.into_condition_col(), Op::Gt)
    }

    /// Generate a less-than (`<`) condition.
    ///
    /// When used with a [`Col`] argument, the resulting [`ColCondition`] can be
    /// used in WHERE clauses but **not** in JOIN ON (which only supports equality).
    fn lt<R: ConditionRhs>(self, rhs: R) -> R::Output {
        rhs.apply_condition(self.into_condition_col(), Op::Lt)
    }

    /// Generate a greater-than-or-equal (`>=`) condition.
    ///
    /// When used with a [`Col`] argument, the resulting [`ColCondition`] can be
    /// used in WHERE clauses but **not** in JOIN ON (which only supports equality).
    fn gte<R: ConditionRhs>(self, rhs: R) -> R::Output {
        rhs.apply_condition(self.into_condition_col(), Op::Gte)
    }

    /// Generate a less-than-or-equal (`<=`) condition.
    ///
    /// When used with a [`Col`] argument, the resulting [`ColCondition`] can be
    /// used in WHERE clauses but **not** in JOIN ON (which only supports equality).
    fn lte<R: ConditionRhs>(self, rhs: R) -> R::Output {
        rhs.apply_condition(self.into_condition_col(), Op::Lte)
    }

    /// Generate a `LIKE` condition.
    ///
    /// Accepts a [`LikeExpression`] to ensure safe pattern construction:
    /// - `col("name").like(LikeExpression::contains("foo"))` → `"name" LIKE '%foo%' ESCAPE '\'`
    /// - `col("name").like(LikeExpression::starts_with("foo"))` → `"name" LIKE 'foo%' ESCAPE '\'`
    /// - `col("name").like(LikeExpression::ends_with("foo"))` → `"name" LIKE '%foo' ESCAPE '\'`
    ///
    /// User input is automatically escaped (`%` and `_` are treated as literals).
    /// The `ESCAPE` clause is derived from the [`LikeExpression`].
    fn like(self, expr: LikeExpression) -> WhereClause<String> {
        let val = expr.to_pattern();
        WhereClause::Like {
            col: self.into_condition_col(),
            expr,
            val,
        }
    }

    /// Generate a `NOT LIKE` condition.
    ///
    /// Accepts a [`LikeExpression`] to ensure safe pattern construction:
    /// - `col("name").not_like(LikeExpression::contains("foo"))` → `"name" NOT LIKE '%foo%' ESCAPE '\'`
    ///
    /// User input is automatically escaped (`%` and `_` are treated as literals).
    /// The `ESCAPE` clause is derived from the [`LikeExpression`].
    fn not_like(self, expr: LikeExpression) -> WhereClause<String> {
        let val = expr.to_pattern();
        WhereClause::NotLike {
            col: self.into_condition_col(),
            expr,
            val,
        }
    }

    /// Generate an `IN (...)` condition.
    ///
    /// Accepts a slice of values or a `&SelectQuery` for subqueries:
    /// - `col("id").included(&[1, 2, 3])` → `"id" IN (?, ?, ?)`
    /// - `col("id").included(sub_query)` → `"id" IN (SELECT ...)`
    ///
    /// When a value list is empty, this produces `1 = 0` (always false) instead of
    /// invalid SQL. If you need to distinguish "no filter" from "match nothing",
    /// check that the slice is non-empty before calling this method.
    fn included<V: Clone>(self, source: impl IntoIncluded<V>) -> WhereClause<V> {
        source.into_in_clause(self.into_condition_col())
    }

    /// Generate a `NOT IN (...)` condition.
    ///
    /// Accepts a slice of values or a `SelectQuery` for subqueries:
    /// - `col("id").not_included(&[1, 2, 3])` → `"id" NOT IN (?, ?, ?)`
    /// - `col("id").not_included(sub_query)` → `"id" NOT IN (SELECT ...)`
    ///
    /// When a value list is empty, this produces `1 = 1` (always true) instead of
    /// invalid SQL.
    fn not_included<V: Clone>(self, source: impl IntoIncluded<V>) -> WhereClause<V> {
        source.into_not_in_clause(self.into_condition_col())
    }

    /// Generate a `BETWEEN` condition.
    fn between<V: Clone>(self, low: V, high: V) -> WhereClause<V> {
        WhereClause::Between {
            col: self.into_condition_col(),
            low,
            high,
        }
    }

    /// Generate a `NOT BETWEEN` condition.
    fn not_between<V: Clone>(self, low: V, high: V) -> WhereClause<V> {
        WhereClause::NotBetween {
            col: self.into_condition_col(),
            low,
            high,
        }
    }

    /// Convert a Rust range into SQL conditions.
    ///
    /// - `20..=30` → `BETWEEN 20 AND 30`
    /// - `20..30`  → `col >= 20 AND col < 30`
    /// - `20..`    → `col >= 20`
    /// - `..30`    → `col < 30`
    /// - `..=30`   → `col <= 30`
    fn in_range<V: Clone>(self, range: impl IntoRangeClause<V>) -> WhereClause<V> {
        range.into_where_clause(self.into_condition_col())
    }
}

impl ConditionExpr for Col {
    fn into_condition_col(self) -> Col {
        self
    }
}

/// `SelectItem::Col` and `SelectItem::Function` variants are supported.
///
/// # Panics
///
/// Panics if called on a `SelectItem::Expr` variant, which cannot be safely
/// converted to a column reference. Use [`RawSql`](crate::RawSql) in
/// WHERE/HAVING clauses through other means instead.
impl<V: Clone> ConditionExpr for SelectItem<V> {
    fn into_condition_col(self) -> Col {
        match self {
            SelectItem::Col(col) => col,
            SelectItem::Function { func, col, .. } => Col {
                table: None,
                column: String::new(),
                alias: None,
                aggregate: Some((func, col.map(Box::new))),
            },
            SelectItem::Expr { .. } => {
                panic!("cannot convert raw Expr SelectItem to Col for WHERE/HAVING")
            }
            SelectItem::WindowFunction { .. } => {
                panic!("cannot convert WindowFunction SelectItem to Col for WHERE/HAVING")
            }
        }
    }
}

impl Col {
    pub fn as_(mut self, alias: &str) -> Col {
        self.alias = Some(alias.to_string());
        self
    }

    /// Create a `COUNT(col)` aggregate expression.
    ///
    /// - `col("id").count()` → `COUNT("id")`
    /// - `table("users").col("id").count()` → `COUNT("users"."id")`
    /// - `col("id").count().as_("cnt")` → `COUNT("id") AS "cnt"`
    pub fn count(self) -> SelectItem {
        SelectItem::Function {
            func: SelectFunc::Count,
            col: Some(self),
            alias: None,
        }
    }

    /// Create a `SUM(col)` aggregate expression.
    ///
    /// - `col("price").sum()` → `SUM("price")`
    /// - `col("price").sum().as_("total")` → `SUM("price") AS "total"`
    pub fn sum(self) -> SelectItem {
        SelectItem::Function {
            func: SelectFunc::Sum,
            col: Some(self),
            alias: None,
        }
    }

    /// Create an `AVG(col)` aggregate expression.
    ///
    /// - `col("price").avg()` → `AVG("price")`
    /// - `col("price").avg().as_("avg_price")` → `AVG("price") AS "avg_price"`
    pub fn avg(self) -> SelectItem {
        SelectItem::Function {
            func: SelectFunc::Avg,
            col: Some(self),
            alias: None,
        }
    }

    /// Create a `MIN(col)` aggregate expression.
    ///
    /// - `col("price").min()` → `MIN("price")`
    /// - `col("price").min().as_("min_price")` → `MIN("price") AS "min_price"`
    pub fn min(self) -> SelectItem {
        SelectItem::Function {
            func: SelectFunc::Min,
            col: Some(self),
            alias: None,
        }
    }

    /// Create a `MAX(col)` aggregate expression.
    ///
    /// - `col("price").max()` → `MAX("price")`
    /// - `col("price").max().as_("max_price")` → `MAX("price") AS "max_price"`
    pub fn max(self) -> SelectItem {
        SelectItem::Function {
            func: SelectFunc::Max,
            col: Some(self),
            alias: None,
        }
    }

    /// Create a `COUNT(col) OVER (...)` window function expression.
    pub fn count_over(self, spec: WindowSpec) -> SelectItem {
        SelectItem::WindowFunction {
            func: WindowFunc::Count,
            col: Some(self),
            window: spec,
            alias: None,
        }
    }

    /// Create a `SUM(col) OVER (...)` window function expression.
    pub fn sum_over(self, spec: WindowSpec) -> SelectItem {
        SelectItem::WindowFunction {
            func: WindowFunc::Sum,
            col: Some(self),
            window: spec,
            alias: None,
        }
    }

    /// Create an `AVG(col) OVER (...)` window function expression.
    pub fn avg_over(self, spec: WindowSpec) -> SelectItem {
        SelectItem::WindowFunction {
            func: WindowFunc::Avg,
            col: Some(self),
            window: spec,
            alias: None,
        }
    }

    /// Create a `MIN(col) OVER (...)` window function expression.
    pub fn min_over(self, spec: WindowSpec) -> SelectItem {
        SelectItem::WindowFunction {
            func: WindowFunc::Min,
            col: Some(self),
            window: spec,
            alias: None,
        }
    }

    /// Create a `MAX(col) OVER (...)` window function expression.
    pub fn max_over(self, spec: WindowSpec) -> SelectItem {
        SelectItem::WindowFunction {
            func: WindowFunc::Max,
            col: Some(self),
            window: spec,
            alias: None,
        }
    }

    pub fn asc(self) -> OrderByClause {
        OrderByClause::Col {
            col: self,
            dir: SortDir::Asc,
        }
    }

    pub fn desc(self) -> OrderByClause {
        OrderByClause::Col {
            col: self,
            dir: SortDir::Desc,
        }
    }
}

/// A column-to-column comparison, usable in both JOIN ON and WHERE clauses.
///
/// Created by [`Col::eq`] with a `Col` argument. Can be passed directly to `and_where` / `or_where`
/// (WHERE clause) or to `join` / `left_join` (JOIN ON clause).
#[derive(Debug, Clone)]
pub struct ColCondition {
    pub(crate) left: Col,
    pub(crate) op: Op,
    pub(crate) right: Col,
}

/// An item in a SELECT list — either a column reference or a raw SQL expression.
#[derive(Debug, Clone)]
pub enum SelectItem<V: Clone = Value> {
    /// A column reference (optionally table-qualified and/or aliased).
    Col(Col),
    /// A raw SQL expression (e.g., `"COUNT(*)"`, `"price * quantity"`).
    ///
    /// **Warning:** `raw` is embedded into SQL without escaping.
    /// Never pass user-supplied input — see [`SelectQuery::add_select_expr`](crate::SelectQuery::add_select_expr).
    Expr {
        raw: crate::raw_sql::RawSql<V>,
        alias: Option<String>,
    },
    /// A function applied to a column (e.g., `COUNT("id")`, `SUM("price")`).
    Function {
        func: SelectFunc,
        col: Option<Col>,
        alias: Option<String>,
    },
    /// A window function (e.g., `ROW_NUMBER() OVER (PARTITION BY "dept" ORDER BY "salary" DESC)`).
    WindowFunction {
        func: WindowFunc,
        col: Option<Col>,
        window: WindowSpec<V>,
        alias: Option<String>,
    },
}

impl<V: Clone> SelectItem<V> {
    /// Transform all bind values in this item.
    pub fn map_values<U: Clone>(self, f: &dyn Fn(V) -> U) -> SelectItem<U> {
        match self {
            SelectItem::Col(col) => SelectItem::Col(col),
            SelectItem::Expr { raw, alias } => SelectItem::Expr {
                raw: raw.map_values(f),
                alias,
            },
            SelectItem::Function { func, col, alias } => SelectItem::Function { func, col, alias },
            SelectItem::WindowFunction {
                func,
                col,
                window,
                alias,
            } => SelectItem::WindowFunction {
                func,
                col,
                window: window.map_values(f),
                alias,
            },
        }
    }
}

/// Window function names (includes both pure window functions and aggregates used with OVER).
#[derive(Debug, Clone)]
pub enum WindowFunc {
    RowNumber,
    Rank,
    DenseRank,
    /// Aggregate functions used as window functions.
    Count,
    Sum,
    Avg,
    Min,
    Max,
}

impl WindowFunc {
    pub fn as_str(&self) -> &'static str {
        match self {
            WindowFunc::RowNumber => "ROW_NUMBER",
            WindowFunc::Rank => "RANK",
            WindowFunc::DenseRank => "DENSE_RANK",
            WindowFunc::Count => "COUNT",
            WindowFunc::Sum => "SUM",
            WindowFunc::Avg => "AVG",
            WindowFunc::Min => "MIN",
            WindowFunc::Max => "MAX",
        }
    }
}

/// Window specification for OVER clauses.
#[derive(Debug, Clone)]
pub struct WindowSpec<V: Clone = Value> {
    pub partition_by: Vec<Col>,
    pub order_by: Vec<OrderByClause<V>>,
    /// When set, this window spec is rendered as a named WINDOW clause
    /// and referenced by name in OVER.
    pub name: Option<String>,
}

/// Create an empty window specification.
pub fn window() -> WindowSpec {
    WindowSpec {
        partition_by: Vec::new(),
        order_by: Vec::new(),
        name: None,
    }
}

impl<V: Clone> WindowSpec<V> {
    /// Add PARTITION BY columns.
    pub fn partition_by(mut self, cols: &[Col]) -> Self {
        self.partition_by.extend(cols.iter().cloned());
        self
    }

    /// Add an ORDER BY clause.
    pub fn order_by(mut self, clause: OrderByClause<V>) -> Self {
        self.order_by.push(clause);
        self
    }

    /// Assign a name to this window spec for use in a WINDOW clause.
    ///
    /// When a named `WindowSpec` is used with `.over()` or `.*_over()`,
    /// the query generates `OVER "name"` references and a
    /// `WINDOW "name" AS (...)` clause.
    pub fn as_(mut self, name: &str) -> Self {
        self.name = Some(name.to_string());
        self
    }

    /// Transform all bind values in this spec.
    pub fn map_values<U: Clone>(self, f: &dyn Fn(V) -> U) -> WindowSpec<U> {
        WindowSpec {
            partition_by: self.partition_by,
            order_by: self
                .order_by
                .into_iter()
                .map(|ob| ob.map_values(f))
                .collect(),
            name: self.name,
        }
    }

    /// Convert a `WindowSpec<Value>` into `WindowSpec<V>` for any `V`.
    pub fn from_default(spec: WindowSpec) -> Self {
        WindowSpec {
            partition_by: spec.partition_by,
            order_by: spec
                .order_by
                .into_iter()
                .map(OrderByClause::from_default)
                .collect(),
            name: spec.name,
        }
    }
}

/// Create a `ROW_NUMBER()` window function select item (no OVER clause yet).
pub fn row_number() -> SelectItem {
    SelectItem::WindowFunction {
        func: WindowFunc::RowNumber,
        col: None,
        window: WindowSpec {
            partition_by: Vec::new(),
            order_by: Vec::new(),
            name: None,
        },
        alias: None,
    }
}

/// Create a `RANK()` window function select item.
pub fn rank() -> SelectItem {
    SelectItem::WindowFunction {
        func: WindowFunc::Rank,
        col: None,
        window: WindowSpec {
            partition_by: Vec::new(),
            order_by: Vec::new(),
            name: None,
        },
        alias: None,
    }
}

/// Create a `DENSE_RANK()` window function select item.
pub fn dense_rank() -> SelectItem {
    SelectItem::WindowFunction {
        func: WindowFunc::DenseRank,
        col: None,
        window: WindowSpec {
            partition_by: Vec::new(),
            order_by: Vec::new(),
            name: None,
        },
        alias: None,
    }
}

/// Supported SQL functions for SELECT items.
#[derive(Debug, Clone)]
pub enum SelectFunc {
    /// `COUNT(col)` or `COUNT(*)`.
    Count,
    /// `COUNT(1)`.
    CountOne,
    /// `SUM(col)`.
    Sum,
    /// `AVG(col)`.
    Avg,
    /// `MIN(col)`.
    Min,
    /// `MAX(col)`.
    Max,
}

impl SelectFunc {
    pub fn as_str(&self) -> &'static str {
        match self {
            SelectFunc::Count => "COUNT",
            SelectFunc::CountOne => "COUNT",
            SelectFunc::Sum => "SUM",
            SelectFunc::Avg => "AVG",
            SelectFunc::Min => "MIN",
            SelectFunc::Max => "MAX",
        }
    }
}

/// Create a `COUNT(*)` expression.
///
/// - `count_all()` → `COUNT(*)`
/// - `count_all().as_("cnt")` → `COUNT(*) AS "cnt"`
pub fn count_all() -> SelectItem {
    SelectItem::Function {
        func: SelectFunc::Count,
        col: None,
        alias: None,
    }
}

/// Create a `COUNT(1)` expression.
///
/// - `count_one()` → `COUNT(1)`
/// - `count_one().as_("cnt")` → `COUNT(1) AS "cnt"`
pub fn count_one() -> SelectItem {
    SelectItem::Function {
        func: SelectFunc::CountOne,
        col: None,
        alias: None,
    }
}

impl<V: Clone> SelectItem<V> {
    /// Add an alias to this select item.
    ///
    /// - `col("id").count().as_("cnt")` → `COUNT("id") AS "cnt"`
    pub fn as_(mut self, alias: &str) -> SelectItem<V> {
        match &mut self {
            SelectItem::Col(col) => {
                col.alias = Some(alias.to_string());
            }
            SelectItem::Expr { alias: a, .. } => {
                *a = Some(alias.to_string());
            }
            SelectItem::Function { alias: a, .. } => {
                *a = Some(alias.to_string());
            }
            SelectItem::WindowFunction { alias: a, .. } => {
                *a = Some(alias.to_string());
            }
        }
        self
    }

    /// Attach an OVER (window specification) clause.
    ///
    /// Supported on `WindowFunction` variants (e.g., those created by `row_number()`).
    /// For aggregate-as-window, use `col("x").sum_over(spec)` etc.
    ///
    /// # Panics
    ///
    /// Panics if called on a `Col`, `Expr`, or `Function` variant.
    pub fn over(self, spec: WindowSpec<V>) -> SelectItem<V> {
        match self {
            SelectItem::WindowFunction {
                func, col, alias, ..
            } => SelectItem::WindowFunction {
                func,
                col,
                window: spec,
                alias,
            },
            _ => panic!("over() is only supported on WindowFunction variants"),
        }
    }
}

impl<V: Clone> SelectItem<V> {
    /// Convert a `SelectItem<Value>` into `SelectItem<V>` for any `V`.
    ///
    /// This is safe for `Col` and `Function` variants (which carry no bind values).
    /// For `Expr` variants, the RawSql must have no binds (panics otherwise).
    pub fn from_default(item: SelectItem) -> Self {
        match item {
            SelectItem::Col(col) => SelectItem::Col(col),
            SelectItem::Function { func, col, alias } => SelectItem::Function { func, col, alias },
            SelectItem::Expr { raw, alias } => {
                assert!(
                    raw.binds.is_empty(),
                    "Cannot convert SelectItem::Expr with binds to a different value type"
                );
                SelectItem::Expr {
                    raw: RawSql::new(raw.as_str()),
                    alias,
                }
            }
            SelectItem::WindowFunction {
                func,
                col,
                window,
                alias,
            } => SelectItem::WindowFunction {
                func,
                col,
                window: WindowSpec::from_default(window),
                alias,
            },
        }
    }
}

impl<V: Clone> From<Col> for SelectItem<V> {
    fn from(col: Col) -> Self {
        SelectItem::Col(col)
    }
}

impl<'a, V: Clone> From<&'a str> for SelectItem<V> {
    fn from(s: &'a str) -> Self {
        SelectItem::Col(Col {
            table: None,
            column: s.to_string(),
            alias: None,
            aggregate: None,
        })
    }
}
