(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports", "./util", "./alter", "./aggregation", "./assign", "./binary", "./case", "./collate", "./column-ref-registry", "./func", "./interval", "./json", "./select", "./show", "./array-struct", "./tables", "./union", "./window"], factory);
  } else if (typeof exports !== "undefined") {
    factory(exports, require("./util"), require("./alter"), require("./aggregation"), require("./assign"), require("./binary"), require("./case"), require("./collate"), require("./column-ref-registry"), require("./func"), require("./interval"), require("./json"), require("./select"), require("./show"), require("./array-struct"), require("./tables"), require("./union"), require("./window"));
  } else {
    var mod = {
      exports: {}
    };
    factory(mod.exports, global.util, global.alter, global.aggregation, global.assign, global.binary, global._case, global.collate, global.columnRefRegistry, global.func, global.interval, global.json, global.select, global.show, global.arrayStruct, global.tables, global.union, global.window);
    global.expr = mod.exports;
  }
})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _util, _alter, _aggregation, _assign, _binary, _case, _collate, _columnRefRegistry, _func, _interval, _json, _select, _show, _arrayStruct, _tables, _union, _window) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.exprToSQL = exprToSQL;
  _exports.exprToSQLConvertFn = void 0;
  _exports.getExprListSQL = getExprListSQL;
  _exports.orderOrPartitionByToSQL = orderOrPartitionByToSQL;
  _exports.varToSQL = varToSQL;
  const exprToSQLConvertFn = _exports.exprToSQLConvertFn = {};

  // Fallback handlers for column functions to avoid circular dependency
  function columnRefFallback(expr) {
    const {
      column,
      table,
      db,
      schema
    } = expr;
    if (column === '*') {
      const parts = [];
      if (db && typeof db === 'string') parts.push((0, _util.identifierToSql)(db));
      if (schema && typeof schema === 'string') parts.push((0, _util.identifierToSql)(schema));
      if (table && typeof table === 'string') parts.push((0, _util.identifierToSql)(table));
      parts.push('*');
      return parts.join('.');
    }
    // For non-* columns, return a simple identifier to avoid breaking functionality
    if (typeof column === 'string') {
      return (0, _util.identifierToSql)(column);
    }
    if (column && column.expr && column.expr.value) {
      return (0, _util.identifierToSql)(column.expr.value);
    }
    return (0, _util.literalToSQL)(expr);
  }
  function fullTextSearchFallback(expr) {
    const {
      match,
      columns,
      against
    } = expr;
    const columnNames = columns ? columns.map(col => col.column || 'column').join(', ') : 'column';
    const matchPart = `${(0, _util.toUpper)(match || 'MATCH')} (${columnNames})`;
    const againstPart = `${(0, _util.toUpper)(against || 'AGAINST')} (?)`;
    return `${matchPart} ${againstPart}`;
  }
  function columnDefinitionFallback(expr) {
    const {
      column,
      definition
    } = expr;
    let columnName = '';
    if (column && column.column) {
      if (typeof column.column === 'string') {
        columnName = (0, _util.identifierToSql)(column.column);
      } else if (column.column.expr && column.column.expr.value) {
        columnName = (0, _util.identifierToSql)(column.column.expr.value);
      }
    }
    const dataType = definition && definition.dataType ? definition.dataType : '';
    return `${columnName} ${dataType}`.trim();
  }
  function varToSQL(expr) {
    const {
      prefix = '@',
      name,
      members,
      quoted,
      suffix
    } = expr;
    const val = [];
    const varName = members && members.length > 0 ? `${name}.${members.join('.')}` : name;
    let result = `${prefix || ''}${varName}`;
    if (suffix) result += suffix;
    val.push(result);
    return [quoted, val.join(' '), quoted].filter(_util.hasVal).join('');
  }
  function exprToSQL(exprOrigin) {
    if (!exprOrigin) return;
    const expr = exprOrigin;
    if (exprOrigin.ast) {
      const {
        ast
      } = expr;
      Reflect.deleteProperty(expr, ast);
      for (const key of Object.keys(ast)) {
        expr[key] = ast[key];
      }
    }
    const {
      type
    } = expr;
    if (type === 'expr') return exprToSQL(expr.expr);

    // Handle column_ref cases - use registry to avoid circular dependency
    if (type === 'column_ref') {
      const columnRefToSQL = (0, _columnRefRegistry.getColumnRefToSQL)();
      if (columnRefToSQL) {
        return columnRefToSQL(expr);
      }
      // Fallback if not yet registered
      return columnRefFallback(expr);
    }

    // Handle fulltext_search cases - use registry to avoid circular dependency
    if (type === 'fulltext_search') {
      const fullTextSearchToSQL = (0, _columnRefRegistry.getFullTextSearchToSQL)();
      if (fullTextSearchToSQL) {
        return fullTextSearchToSQL(expr);
      }
      // Fallback if not yet registered
      return fullTextSearchFallback(expr);
    }

    // Handle column_definition cases - use registry to avoid circular dependency
    if (type === 'column_definition') {
      const columnDefinitionToSQL = (0, _columnRefRegistry.getColumnDefinitionToSQL)();
      if (columnDefinitionToSQL) {
        return columnDefinitionToSQL(expr);
      }
      // Fallback if not yet registered
      return columnDefinitionFallback(expr);
    }
    return exprToSQLConvertFn[type] ? exprToSQLConvertFn[type](expr) : (0, _util.literalToSQL)(expr);
  }
  function arrayAccessToSQL(expr) {
    const {
      expr: baseExpr,
      indices
    } = expr;
    let result = null;
    // For column references in array access, use the column name directly without backticks
    if (baseExpr.type === 'column_ref') {
      result = baseExpr.column;
    } else {
      result = exprToSQL(baseExpr);
    }
    if (indices && indices.length > 0) {
      for (const index of indices) {
        result += `[${exprToSQL(index)}]`;
      }
    }
    return result;
  }
  function unaryToSQL(unarExpr) {
    const {
      operator,
      parentheses,
      expr
    } = unarExpr;
    const space = operator === '-' || operator === '+' || operator === '~' || operator === '!' ? '' : ' ';
    const str = `${operator}${space}${exprToSQL(expr)}`;
    return parentheses ? `(${str})` : str;
  }
  function getExprListSQL(exprList) {
    if (!exprList) return [];
    if (!Array.isArray(exprList)) exprList = [exprList];
    return exprList.map(exprToSQL);
  }
  function mapObjectToSQL(mapExpr) {
    const {
      keyword,
      expr
    } = mapExpr;
    const exprStr = expr.map(exprItem => [(0, _util.literalToSQL)(exprItem.key), (0, _util.literalToSQL)(exprItem.value)].join(', ')).join(', ');
    return [(0, _util.toUpper)(keyword), `[${exprStr}]`].join('');
  }
  function orderOrPartitionByToSQL(expr, prefix) {
    if (!Array.isArray(expr)) return '';
    let expressions = [];
    const upperPrefix = (0, _util.toUpper)(prefix);
    switch (upperPrefix) {
      case 'ORDER BY':
        expressions = expr.map(info => [exprToSQL(info.expr), info.type || 'ASC', (0, _util.toUpper)(info.nulls)].filter(_util.hasVal).join(' '));
        break;
      case 'PARTITION BY':
        expressions = expr.map(info => exprToSQL(info.expr));
        break;
      default:
        expressions = expr.map(info => exprToSQL(info.expr));
        break;
    }
    return (0, _util.connector)(upperPrefix, expressions.join(', '));
  }

  // Populate exprToSQLConvertFn object after all functions are defined
  Object.assign(exprToSQLConvertFn, {
    alter: _alter.alterExprToSQL,
    aggr_func: _aggregation.aggrToSQL,
    any_value: _func.anyValueFuncToSQL,
    window_func: _window.windowFuncToSQL,
    'array': _arrayStruct.arrayStructExprToSQL,
    array_access: arrayAccessToSQL,
    assign: _assign.assignToSQL,
    binary_expr: _binary.binaryToSQL,
    case: _case.caseToSQL,
    cast: _func.castToSQL,
    collate: _collate.collateToSQL,
    datatype: _util.dataTypeToSQL,
    extract: _func.extractFunToSQL,
    flatten: _func.flattenFunToSQL,
    function: _func.funcToSQL,
    lambda: _func.lambdaToSQL,
    insert: _union.unionToSQL,
    interval: _interval.intervalToSQL,
    json: _json.jsonExprToSQL,
    json_object_arg: _func.jsonObjectArgToSQL,
    json_visitor: _json.jsonVisitorExprToSQL,
    func_arg: _func.funcArgToSQL,
    show: _show.showToSQL,
    struct: _arrayStruct.arrayStructExprToSQL,
    tablefunc: _func.tablefuncFunToSQL,
    tables: _tables.tablesToSQL,
    unnest: _tables.unnestToSQL,
    'window': _window.namedWindowExprListToSQL,
    var: varToSQL,
    unary_expr: unaryToSQL,
    map_object: mapObjectToSQL
  });
  exprToSQLConvertFn.expr_list = expr => {
    const result = getExprListSQL(expr.value);
    const {
      parentheses,
      separator
    } = expr;
    if (!parentheses && !separator) return result;
    const joinSymbol = separator || ', ';
    const str = result.join(joinSymbol);
    return parentheses ? `(${str})` : str;
  };
  exprToSQLConvertFn.select = expr => {
    const str = typeof expr._next === 'object' ? (0, _union.unionToSQL)(expr) : (0, _select.selectToSQL)(expr);
    return expr.parentheses ? `(${str})` : str;
  };
});