package com.chenyang.druid.sql.builder.impl;

import com.chenyang.druid.DbType;
import com.chenyang.druid.sql.SQLUtils;
import com.chenyang.druid.sql.ast.SQLExpr;
import com.chenyang.druid.sql.ast.SQLStatement;
import com.chenyang.druid.sql.ast.expr.SQLBinaryOperator;
import com.chenyang.druid.sql.ast.expr.SQLIdentifierExpr;
import com.chenyang.druid.sql.ast.statement.SQLExprTableSource;
import com.chenyang.druid.sql.ast.statement.SQLTableSource;
import com.chenyang.druid.sql.ast.statement.SQLUpdateSetItem;
import com.chenyang.druid.sql.ast.statement.SQLUpdateStatement;
import com.chenyang.druid.sql.builder.SQLUpdateBuilder;
import com.chenyang.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
import com.chenyang.druid.sql.dialect.oracle.ast.stmt.OracleUpdateStatement;
import com.chenyang.druid.sql.dialect.postgresql.ast.stmt.PGUpdateStatement;
import com.chenyang.druid.sql.dialect.sqlserver.ast.stmt.SQLServerUpdateStatement;
import java.util.List;
import java.util.Map;

public class SQLUpdateBuilderImpl extends SQLBuilderImpl implements SQLUpdateBuilder {
   private SQLUpdateStatement stmt;
   private DbType dbType;

   public SQLUpdateBuilderImpl(DbType dbType) {
      this.dbType = dbType;
   }

   public SQLUpdateBuilderImpl(String sql, DbType dbType) {
      List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
      if (stmtList.size() == 0) {
         throw new IllegalArgumentException("not support empty-statement :" + sql);
      } else if (stmtList.size() > 1) {
         throw new IllegalArgumentException("not support multi-statement :" + sql);
      } else {
         SQLUpdateStatement stmt = (SQLUpdateStatement)stmtList.get(0);
         this.stmt = stmt;
         this.dbType = dbType;
      }
   }

   public SQLUpdateBuilderImpl(SQLUpdateStatement stmt, DbType dbType) {
      this.stmt = stmt;
      this.dbType = dbType;
   }

   public SQLUpdateBuilderImpl limit(int rowCount) {
      throw new UnsupportedOperationException();
   }

   public SQLUpdateBuilderImpl limit(int rowCount, int offset) {
      throw new UnsupportedOperationException();
   }

   public SQLUpdateBuilderImpl from(String table) {
      return this.from(table, null);
   }

   public SQLUpdateBuilderImpl from(String table, String alias) {
      SQLUpdateStatement update = this.getSQLUpdateStatement();
      SQLExprTableSource from = new SQLExprTableSource(new SQLIdentifierExpr(table), alias);
      update.setTableSource((SQLTableSource)from);
      return this;
   }

   public SQLUpdateBuilderImpl where(String expr) {
      SQLUpdateStatement update = this.getSQLUpdateStatement();
      SQLExpr exprObj = SQLUtils.toSQLExpr(expr, this.dbType);
      update.setWhere(exprObj);
      return this;
   }

   public SQLUpdateBuilderImpl whereAnd(String expr) {
      SQLUpdateStatement update = this.getSQLUpdateStatement();
      SQLExpr exprObj = SQLUtils.toSQLExpr(expr, this.dbType);
      SQLExpr newCondition = SQLUtils.buildCondition(SQLBinaryOperator.BooleanAnd, exprObj, false, update.getWhere());
      update.setWhere(newCondition);
      return this;
   }

   public SQLUpdateBuilderImpl whereOr(String expr) {
      SQLUpdateStatement update = this.getSQLUpdateStatement();
      SQLExpr exprObj = SQLUtils.toSQLExpr(expr, this.dbType);
      SQLExpr newCondition = SQLUtils.buildCondition(SQLBinaryOperator.BooleanOr, exprObj, false, update.getWhere());
      update.setWhere(newCondition);
      return this;
   }

   public SQLUpdateBuilderImpl set(String... items) {
      SQLUpdateStatement update = this.getSQLUpdateStatement();

      for(String item : items) {
         SQLUpdateSetItem updateSetItem = SQLUtils.toUpdateSetItem(item, this.dbType);
         update.addItem(updateSetItem);
      }

      return this;
   }

   public SQLUpdateBuilderImpl setValue(Map<String, Object> values) {
      for(Map.Entry<String, Object> entry : values.entrySet()) {
         this.setValue((String)entry.getKey(), entry.getValue());
      }

      return this;
   }

   public SQLUpdateBuilderImpl setValue(String column, Object value) {
      SQLUpdateStatement update = this.getSQLUpdateStatement();
      SQLExpr columnExpr = SQLUtils.toSQLExpr(column, this.dbType);
      SQLExpr valueExpr = toSQLExpr(value, this.dbType);
      SQLUpdateSetItem item = new SQLUpdateSetItem();
      item.setColumn(columnExpr);
      item.setValue(valueExpr);
      update.addItem(item);
      return this;
   }

   public SQLUpdateStatement getSQLUpdateStatement() {
      if (this.stmt == null) {
         this.stmt = this.createSQLUpdateStatement();
      }

      return this.stmt;
   }

   public SQLUpdateStatement createSQLUpdateStatement() {
      switch (this.dbType) {
         case mysql:
         case mariadb:
            return new MySqlUpdateStatement();
         case oracle:
            return new OracleUpdateStatement();
         case postgresql:
            return new PGUpdateStatement();
         case sqlserver:
            return new SQLServerUpdateStatement();
         default:
            return new SQLUpdateStatement();
      }
   }

   public String toString() {
      return SQLUtils.toSQLString(this.stmt, (DbType)this.dbType);
   }
}
