(function (global, factory) {
  if (typeof define === "function" && define.amd) {
    define(["exports", "./binary", "./column", "./expr", "./insert", "./util"], factory);
  } else if (typeof exports !== "undefined") {
    factory(exports, require("./binary"), require("./column"), require("./expr"), require("./insert"), require("./util"));
  } else {
    var mod = {
      exports: {}
    };
    factory(mod.exports, global.binary, global.column, global.expr, global.insert, global.util);
    global.tables = mod.exports;
  }
})(typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : this, function (_exports, _binary, _column, _expr, _insert, _util) {
  "use strict";

  Object.defineProperty(_exports, "__esModule", {
    value: true
  });
  _exports.operatorToSQL = operatorToSQL;
  _exports.tableOptionToSQL = tableOptionToSQL;
  _exports.tableToSQL = tableToSQL;
  _exports.tablesToSQL = tablesToSQL;
  _exports.unnestToSQL = unnestToSQL;

  function unnestToSQL(unnestExpr) {
    const {
      type,
      as,
      expr,
      with_offset: withOffset
    } = unnestExpr;
    const result = [`${(0, _util.toUpper)(type)}(${expr && (0, _expr.exprToSQL)(expr) || ''})`, (0, _util.commonOptionConnector)('AS', _util.identifierToSql, as), (0, _util.commonOptionConnector)((0, _util.toUpper)(withOffset && withOffset.keyword), _util.identifierToSql, withOffset && withOffset.as)];
    return result.filter(_util.hasVal).join(' ');
  }

  function pivotOperatorToSQL(operator) {
    const {
      as,
      column,
      expr,
      in_expr,
      type
    } = operator;
    const result = [(0, _expr.exprToSQL)(expr), 'FOR', (0, _column.columnRefToSQL)(column), (0, _binary.binaryToSQL)(in_expr)];
    const sql = [`${(0, _util.toUpper)(type)}(${result.join(' ')})`];
    if (as) sql.push('AS', (0, _util.identifierToSql)(as));
    return sql.join(' ');
  }

  function operatorToSQL(operator) {
    if (!operator) return;
    const {
      type
    } = operator;

    switch (type) {
      case 'pivot':
        return pivotOperatorToSQL(operator);

      default:
        return '';
    }
  }

  function tableToSQL(tableInfo) {
    if ((0, _util.toUpper)(tableInfo.type) === 'UNNEST') return unnestToSQL(tableInfo);
    const {
      table,
      db,
      as,
      expr,
      operator,
      prefix: prefixStr,
      schema,
      tablesample
    } = tableInfo;
    const database = (0, _util.identifierToSql)(db);
    const schemaStr = (0, _util.identifierToSql)(schema);
    let tableName = table && (0, _util.identifierToSql)(table);

    if (expr && expr.type === 'values') {
      const {
        parentheses,
        values,
        prefix
      } = expr;
      const valueSQL = [parentheses && '(', '', parentheses && ')'];
      let valuesExpr = (0, _insert.valuesToSQL)(values);
      if (prefix) valuesExpr = valuesExpr.split('(').slice(1).map(val => `${(0, _util.toUpper)(prefix)}(${val}`).join('');
      valueSQL[1] = `VALUES ${valuesExpr}`;
      tableName = valueSQL.filter(_util.hasVal).join('');
    }

    if (expr && expr.type !== 'values') tableName = (0, _expr.exprToSQL)(expr);
    tableName = [(0, _util.toUpper)(prefixStr), tableName].filter(_util.hasVal).join(' ');
    let str = [database, schemaStr, tableName].filter(_util.hasVal).join('.');
    if (tableInfo.parentheses) str = `(${str})`;
    const result = [str, operatorToSQL(operator)];

    if (tablesample) {
      const tableSampleSQL = ['TABLESAMPLE', (0, _expr.exprToSQL)(tablesample.expr), (0, _util.literalToSQL)(tablesample.repeatable)].filter(_util.hasVal).join(' ');
      result.push(tableSampleSQL);
    }

    if (as) result.push('AS', (0, _util.identifierToSql)(as));
    return result.filter(_util.hasVal).join(' ');
  }
  /**
   * @param {Array} tables
   * @return {string}
   */


  function tablesToSQL(tables) {
    if (!tables) return '';
    const baseTable = tables[0];
    const clauses = [];
    if (baseTable.type === 'dual') return 'DUAL';
    clauses.push(tableToSQL(baseTable));

    for (let i = 1; i < tables.length; ++i) {
      const joinExpr = tables[i];
      const {
        on,
        using,
        join
      } = joinExpr;
      const str = [];
      str.push(join ? ` ${join}` : ',');
      str.push(tableToSQL(joinExpr));
      str.push((0, _util.commonOptionConnector)('ON', _expr.exprToSQL, on));
      if (using) str.push(`USING (${using.map(_util.identifierToSql).join(', ')})`);
      clauses.push(str.filter(_util.hasVal).join(' '));
    }

    return clauses.filter(_util.hasVal).join('');
  }

  function tableOptionToSQL(tableOption) {
    const {
      keyword,
      symbol,
      value
    } = tableOption;
    const sql = [keyword.toUpperCase()];
    if (symbol) sql.push(symbol);
    sql.push(value);
    return sql.join(' ');
  }
});