package util.sqlparse.visitor.es.dense;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.druid.sql.dialect.es.ast.EsParser;
import com.alibaba.druid.sql.dialect.es.ast.EsParserContextBase;
import org.antlr.v4.runtime.tree.ParseTree;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import util.sqlparse.visitor.es.memo.ApiResult;
import util.sqlparse.visitor.es.memo.FlowType;
import util.sqlparse.visitor.es.memo.MappingMemo;
import util.sqlparse.visitor.es.memo.Reference;

public abstract class Denseable {
   public static final String INDEX_TAG = "_index";
   public static final String TYPE_TAG = "_type";
   public static final String ID_TAG = "_id";
   public static final String SOURCE_TAG = "_source";
   public static final String HITS_TAG = "hits";
   public static final String RESPONSES_TAG = "responses";
   public static final String DOC_TAG = "_doc";
   public static final String DOCS_TAG = "docs";
   public static final String FIND_TAG = "find";

   public void denseTable(ParamContext.TableDenseContext params) {
      ApiResult api = params.getApi();
      String newTable = params.replaceNewTable();
      String table = params.replaceTable();
      Pattern p = Pattern.compile(table, 40);
      List<MappingMemo> tables = api.getTables();
      EsBuilder builder = EsBuilder.builder();

      for(MappingMemo map : tables) {
         if (map.name != null && map.name.length() != 0 && p.matcher(map.name).matches() && map.refs != null && map.refs.size() > 0) {
            for(ParseTree ref : map.refs) {
               if (ref != null) {
                  EsParserContextBase ctx = (EsParserContextBase)ref;
                  int type = ref.getClass().toString().contains("Uri") ? 1 : 0;
                  builder.reset().enter(ctx).setString(newTable, type);
               }
            }
         }
      }

   }

   @FlowType(FlowType.FlowTypeValue.down)
   public void denseColumn(ParamContext.ColumnDenseContext params) {
   }

   @FlowType(FlowType.FlowTypeValue.down)
   public void denseData(ParamContext.DenseAllContext params) {
   }

   @FlowType(FlowType.FlowTypeValue.none)
   public void denseRow(ParamContext.RowDenseContext params) {
   }

   protected void maskQueryDataStatementSearchBodyQuery(String painless, EsParserContextBase parent, EsParser.QueryDataStatementSearchBodyContext searchBody) {
      if (painless != null && painless.length() != 0) {
         EsBuilder builder = EsBuilder.builder();
         if (searchBody == null) {
            searchBody = (EsParser.QueryDataStatementSearchBodyContext)builder.enter(parent).queryDataStatementSearchBody().current();
         }

         List<EsParser.QueryDataStatementSearchBodyQueryContext> queries = searchBody.queryDataStatementSearchBodyQuery();
         EsParser.QueryDataStatementSearchBodyQueryContext query = queries != null && queries.size() != 0 ? (EsParser.QueryDataStatementSearchBodyQueryContext)queries.get(0) : null;
         if (query == null) {
            builder.reset();
            builder.enter(searchBody).at(1).queryDataStatementSearchBodyQuery().enter().rear(0).queryBody().enter().at(3);
            builder.queryFilter().enter();
            builder.comboundFilter().enter().leafFilter().enter().leafOtherFilter().enter().scriptFilter(painless).reset();
         } else {
            builder.reset();
            EsParser.QueryBodyContext queryBody = query.queryBody();
            this.maskQueryBody(painless, builder, queryBody);
         }

      }
   }

   protected void maskQueryBody(String painless, EsBuilder builder, EsParser.QueryBodyContext queryBody) {
      if (painless != null && painless.length() != 0) {
         EsParser.QueryFilterContext srcFilter = queryBody.queryFilter();
         EsParser.ComboundFilterContext srcComboundFilter = srcFilter.comboundFilter();
         builder.enter(queryBody).remove(srcFilter).reset();
         builder.enter(srcFilter).remove(srcComboundFilter).reset();
         EsParserContextBase newFilter = builder.queryFilter().enter().current();
         EsParser.BoolFilterContext boolFilter = (EsParser.BoolFilterContext)builder.comboundFilter().enter().boolFilter().enter().current();
         builder.reset();
         EsParserContextBase nestBoolFilter = builder.comboundFilter().enter().current();
         builder.comboundFilter().enter().mustFilter(false).enter().mustFilter1().enter().rear();
         builder.append(srcComboundFilter, 1).reset();
         EsParserContextBase scriptFilter = builder.comboundFilter().enter().current();
         builder.filterFilter().enter().filterFilter1().enter().rear();
         builder.comboundFilter().enter().leafFilter().enter().leafOtherFilter().enter().scriptFilter(painless).reset();
         builder.enter(boolFilter).append(3, nestBoolFilter, scriptFilter);
         builder.reset();
         builder.enter(queryBody).append(newFilter, 3);
      }
   }

   protected String buildPainless(ParamContext.RowDenseContext params) {
      Map<String, List<String>> modifyTable = params.modifyTable();
      if (modifyTable != null && modifyTable.size() != 0) {
         Map<String, String> tableKeyWord = params.tableKeyWord();
         if (tableKeyWord != null && tableKeyWord.size() != 0) {
            int i = 0;
            StringBuilder script = new StringBuilder();
            String table = (String)tableKeyWord.keySet().iterator().next();
            String value = (String)((List)modifyTable.values().iterator().next()).get(0);
            String col = (String)tableKeyWord.values().iterator().next();
            if (value == null) {
               return "";
            } else {
               String fullCol = col;
               String varName = "contains" + i++;
               String varName1 = "contains" + i++;
               script.append(String.format("boolean %s=doc._type.toString().equals('[%s]');", varName1, table));
               script.append(String.format("boolean %s=doc.containsKey('%s');", varName, col));
               script.append(String.format("if(%s && %s){", varName1, varName));
               String[] tokens = (String[])this.tokenize(value).toArray(new String[0]);
               StringBuilder contains = new StringBuilder();
               contains.append("(");

               for(int j = 0; j < tokens.length; ++j) {
                  String token = tokens[j];
                  if (j < tokens.length - 1) {
                     if (token != null && token.length() > 0) {
                        contains.append(String.format("doc['%s'].contains('%s') &&", fullCol, token));
                     }
                  } else if (contains.length() == 1) {
                     contains.append(String.format("doc['%s'].contains('%s') &&", fullCol, token));
                  } else {
                     contains = contains.delete(contains.length() - 3, contains.length());
                     contains.append(String.format(") || doc['%s'].contains('%s') &&", fullCol, token));
                  }
               }

               contains = contains.delete(contains.length() - 3, contains.length());
               script.append(String.format("if(doc['%s'].size()>0 && (%s)) { return false;}", fullCol, contains, value));
               script.append("}");
               script.append("return true;");
               return script.toString();
            }
         } else {
            return null;
         }
      } else {
         return null;
      }
   }

   private List<String> tokenize(String text) {
      if (text != null && text.length() != 0) {
         Set<String> values = new HashSet();
         int len = text.length();
         int at = -1;

         for(int i = 0; i < len; ++i) {
            char c = text.charAt(i);
            if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9')) {
               if (at != -1) {
                  String token = text.substring(at, i);
                  values.add(token);
                  at = -1;
               }

               if ((c >= 19968 && c <= '龥' || c >= '가' && c <= '힣' || c >= 2048 && c <= 19968) && c != 12290 && c != 8221 && c != 12305 && c != 12304 && c != 12298 && c != 12299) {
                  values.add(c + "");
               }
            } else if (at == -1) {
               at = i;
            }
         }

         if (at != -1) {
            String token = text.substring(at);
            values.add(token);
         }

         List<String> datas = new ArrayList(values);
         datas.add(text);
         return datas;
      } else {
         return null;
      }
   }

   protected void denseColumnData(Pattern pat, JSONObject data, String expression, StringBuilder path, Map params) {
      for(String name : data.keySet()) {
         Object value = data.get(name);
         if (value != null) {
            if (value instanceof JSONObject) {
               StringBuilder subPath = new StringBuilder(path);
               if (path.charAt(path.length() - 1) != '.') {
                  subPath.append('.');
               }

               subPath.append(name);
               this.denseColumnData(pat, (JSONObject)value, expression, subPath, params);
            } else {
               String fullName = path.charAt(path.length() - 1) == '.' ? path + name : path + "." + name;
               if (pat.matcher(fullName).matches()) {
                  Object newVal = ExlActor.replace(expression, value);
                  data.put(name, newVal);
                  this.tableMatched(params);
               }
            }
         }
      }

   }

   protected boolean denseRowData(Pattern pat, JSONObject data, StringBuilder path, String ignore) {
      for(String name : data.keySet()) {
         Object value = data.get(name);
         if (value != null) {
            if (value instanceof JSONObject) {
               StringBuilder subPath = new StringBuilder(path);
               if (path.charAt(path.length() - 1) != '.') {
                  subPath.append('.');
               }

               subPath.append(name);
               boolean isValid = this.denseRowData(pat, (JSONObject)value, subPath, ignore);
               if (!isValid) {
                  return false;
               }
            } else {
               String fullName = path.charAt(path.length() - 1) == '.' ? path + name : path + "." + name;
               if (pat.matcher(fullName).matches()) {
                  String val = value == null ? null : value.toString();
                  if (val.equals(ignore)) {
                     return false;
                  }
               }
            }
         }
      }

      return true;
   }

   protected String transPattern(String str) {
      String[] patterns = new String[]{".", "[", "]", "(", ")", "{", "}"};

      for(String pattern : patterns) {
         str = str.replaceAll(String.format("\\%s", pattern), String.format("\\\\%s", pattern));
      }

      return str;
   }

   protected Pattern createColumnPattern(String replaceSchema, String replaceTable, String replaceColumn) {
      StringBuilder sb = new StringBuilder();
      if (replaceSchema != null) {
         sb.append(replaceSchema);
      } else {
         sb.append(".*");
      }

      sb.append("\\.");
      if (replaceTable != null) {
         sb.append(replaceTable);
      } else {
         sb.append(".*");
      }

      sb.append("\\.");
      if (replaceColumn != null) {
         sb.append(replaceColumn);
      } else {
         sb.append(".*");
      }

      return Pattern.compile(sb.toString(), 10);
   }

   protected Pattern createColumnPattern(String replaceColumn) {
      StringBuilder sb = new StringBuilder();
      if (replaceColumn != null) {
         sb.append(replaceColumn);
      } else {
         sb.append(".*");
      }

      return Pattern.compile(sb.toString(), 8);
   }

   protected <T extends Reference> EsParserContextBase buildEntityUri(EsBuilder builder, List<T> entities) {
      if (entities != null && entities.size() != 0) {
         builder.reset();
         EsParserContextBase uriIndex = builder.uriSegIndex().current();
         builder.enter();
         int i = 0;

         for(Reference schema : entities) {
            Iterator<ParseTree> iterator = schema.refs.iterator();

            while(iterator.hasNext()) {
               ParseTree next = (ParseTree)iterator.next();
               if (next != null && next.getClass().equals(EsParser.UriSegIndexContext.class)) {
                  iterator.remove();
               }
            }

            if (i == 0) {
               EsParserContextBase ref = builder.uriIndexAtom(schema.name).current();
               schema.refs.add(ref);
            } else {
               EsParserContextBase ref = builder.uriSeg(schema.name).current();
               schema.refs.add(ref);
            }

            ++i;
         }

         return uriIndex;
      } else {
         return null;
      }
   }

   protected String getStringValue(EsParser.StringContext ctx) {
      if (ctx == null) {
         return null;
      } else {
         String text = ctx.getText();
         return text.substring(1, text.length() - 2);
      }
   }

   protected boolean tableMatched(Map params) {
      params.put("isMatched", "1");
      return true;
   }
}
