package util.sqlparse.visitor.es;

import bean.Column;
import bean.DataBase;
import bean.Schema;
import bean.Table;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.dialect.es.ast.EsParser;
import com.alibaba.druid.sql.dialect.es.ast.EsParserContextBase;
import com.alibaba.druid.sql.dialect.es.ast.EsStatementType;
import com.alibaba.druid.sql.dialect.es.visitor.EsUtils;
import com.alibaba.druid.stat.TableStat;
import org.antlr.v4.runtime.tree.ParseTree;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import util.sqlparse.visitor.common.Context;
import util.sqlparse.visitor.common.names.NameWrapper;
import util.sqlparse.visitor.es.memo.ApiResult;
import util.sqlparse.visitor.es.memo.FieldMemo;
import util.sqlparse.visitor.es.memo.IndexMemo;
import util.sqlparse.visitor.es.memo.MappingMemo;
import util.sqlparse.visitor.es.memo.Reference;
import util.sqlparse.visitor.es.visitor.Scope;
import util.sqlparse.visitor.es.visitor.ScopeVisitor;
import util.sqlparse.visitor.es.visitor.WhereVisitor;

public class ApiParser {
   public ApiResult parse(String uri, String method, String body, DataBase dataBase) throws MalformedURLException {
      StringBuilder header = new StringBuilder();
      header.append(method);
      header.append(" ");
      header.append(uri);
      header.append("\n");
      header.append(body == null ? "" : body);
      return this.parse(header.toString(), dataBase);
   }

   public ApiResult parse(String request, DataBase dataBase) throws MalformedURLException {
      EsParser.StatementContext statement = EsUtils.parse(request);
      ApiResult result = new ApiResult();
      result.setStatement(statement);
      int apiType = (Integer)statement.getAttribute("apiType");

      for(TableStat.Mode value : TableStat.Mode.values()) {
         if (value.mark == apiType) {
            result.apiType = value;
            break;
         }
      }

      switch (result.apiType) {
         case Alter:
         case Other:
            return result;
         default:
            int stmtType = (Integer)statement.getAttribute("stmtType");

            for(EsStatementType value : EsStatementType.values()) {
               if (value.getCode() == stmtType) {
                  result.stmtType = value;
                  break;
               }
            }

            switch (result.stmtType) {
               case unknown:
               case other:
                  return result;
               default:
                  Context context = new Context();
                  context.initialize(dataBase, "", "es");
                  Scope scope = new Scope((ParseTree)null);
                  ScopeVisitor visitor = new ScopeVisitor(scope);
                  visitor.visit((ParseTree)statement);
                  List<EsParserContextBase> indices = visitor.indices;
                  List<EsParserContextBase> values = visitor.values;
                  List<EsParserContextBase> fields = visitor.fields;
                  List<EsParserContextBase> mappings = visitor.mappings;
                  new ArrayList();
                  List<Item> regindexitems = new ArrayList();
                  if (indices != null && indices.size() != 0) {
                     for(EsParserContextBase i : indices) {
                        String index = i.getTextValue();
                        if (index.contains(",")) {
                           for(String s : Arrays.asList(index.split(","))) {
                              Item item = new Item();
                              item.ref = i;
                              s = s.replace("[", "\\[").replace("{", "\\{").replace("(", "\\(").replace(")", "\\)").replace("]", "\\]").replace("}", "\\}").replace("*", ".*");
                              item.regex = s;
                              regindexitems.add(item);
                           }
                        } else {
                           Item item = new Item();
                           item.ref = i;
                           index = index.replace("[", "\\[").replace("{", "\\{").replace("(", "\\(").replace(")", "\\)").replace("]", "\\]").replace("}", "\\}").replace("*", ".*");
                           item.regex = index;
                           regindexitems.add(item);
                        }
                     }
                  }

                  if (regindexitems.size() == 0) {
                     Item item = new Item();
                     item.regex = ".*";
                     regindexitems.add(item);
                  }

                  new ArrayList();
                  List<Item> regtablesitems = new ArrayList();
                  if (mappings != null && mappings.size() != 0) {
                     for(EsParserContextBase i : mappings) {
                        String index = i.getTextValue();
                        if (index != null && index.equalsIgnoreCase("_doc")) {
                           index = ".*";
                        } else {
                           index = index.replace("*", ".*");
                        }

                        Item item = new Item();
                        item.ref = i;
                        item.regex = index;
                        regtablesitems.add(item);
                     }
                  }

                  if (regtablesitems.size() == 0) {
                     Item item = new Item();
                     item.regex = ".*";
                     regtablesitems.add(item);
                  }

                  new ArrayList();
                  List<Item> regfieldsitems = new ArrayList();
                  if (fields != null && fields.size() != 0) {
                     for(EsParserContextBase i : fields) {
                        String index = i.getTextValue().replace("*", ".*");
                        List<EsParser.StringContext> complexFields = (List)i.getAttribute("complexFields");
                        if (complexFields != null && complexFields.size() > 1) {
                           StringBuilder s = new StringBuilder();

                           for(EsParser.StringContext c : complexFields) {
                              s.append(c.getTextValue()).append(".");
                           }

                           s.deleteCharAt(s.length() - 1);
                           index = s.toString().replace("*", ".*");
                        }

                        Item item = new Item();
                        item.ref = i;
                        item.regex = index;
                        regfieldsitems.add(item);
                     }
                  }

                  if (regfieldsitems.size() == 0) {
                     Item item = new Item();
                     item.regex = ".*";
                     regfieldsitems.add(item);
                  }

                  List<String> star = Arrays.asList("*");
                  List<RegexInfo> descartesList = descartes(regindexitems, regtablesitems, regfieldsitems);
                  List<String> imf = new ArrayList();
                  List<String> dataTypes = new ArrayList();

                  for(Schema schema : dataBase == null ? new ArrayList<Schema>() : dataBase.getSchemaList()) {
                     List<Table> tables = schema.getTableList();

                     for(Table table : tables) {
                        List<Column> columns = table.getColumnList();

                        for(Column column : columns) {
                           imf.add(schema.getName() + "<" + table.getName() + "<" + column.getColumnName());
                           dataTypes.add(column.getDataType());
                        }

                        if (columns == null || columns.size() == 0) {
                           imf.add(schema.getName() + "<" + table.getName() + "<");
                           dataTypes.add(null);
                        }
                     }

                     if (tables == null || tables.size() == 0) {
                        imf.add(schema.getName() + "<<");
                        dataTypes.add(null);
                     }
                  }

                  this.checkRegex(result, descartesList, imf, dataTypes, dataBase);
                  return result;
            }
      }
   }

   public void checkRegex(ApiResult result, List<RegexInfo> regexPatterns, List<String> lookupTables, List<String> dataTypes, DataBase dataBase) {
      List<Pattern> patterns = new ArrayList();

      for(RegexInfo regexPattern : regexPatterns) {
         patterns.add(Pattern.compile(regexPattern.regex));
      }

      Set<IndexMemo> indexMemoHashSet = new RefSet<IndexMemo>();
      Set<MappingMemo> mappingMemoHashSet = new RefSet<MappingMemo>();
      Set<FieldMemo> fieldMemoHashSet = new RefSet<FieldMemo>();
      if (!dataBase.isRedis()) {
         int k = 0;

         for(String item : lookupTables) {
            for(int i = 0; i < patterns.size(); ++i) {
               Pattern p = (Pattern)patterns.get(i);
               Matcher m = p.matcher(item);
               if (m.matches()) {
                  RegexInfo info = (RegexInfo)regexPatterns.get(i);
                  this.normalizeColumn((String)dataTypes.get(k), indexMemoHashSet, mappingMemoHashSet, fieldMemoHashSet, item, info);
               }
            }

            ++k;
         }
      } else {
         for(RegexInfo regexPattern : regexPatterns) {
            String[] split = regexPattern.regex.split("<");
            if (split.length >= 3) {
               NameWrapper nameWrapper = NameWrapper.create(DbType.of(dataBase.getDbType()), dataBase.simpleCache);
               if (".*".equals(split[0])) {
                  for(String schemaName : dataBase.getSchemaNamelistKey()) {
                     this.checkRegexForSchemaRedis(dataBase, indexMemoHashSet, mappingMemoHashSet, fieldMemoHashSet, regexPattern, split, nameWrapper, schemaName);
                  }
               } else {
                  this.checkRegexForSchemaRedis(dataBase, indexMemoHashSet, mappingMemoHashSet, fieldMemoHashSet, regexPattern, split, nameWrapper, split[0]);
               }
            }
         }
      }

      for(IndexMemo i : indexMemoHashSet) {
         result.getSchemas().add(i);
      }

      for(MappingMemo m : mappingMemoHashSet) {
         result.getTables().add(m);
      }

      for(FieldMemo f : fieldMemoHashSet) {
         result.getFields().add(f);
      }

   }

   private void checkRegexForSchemaRedis(DataBase dataBase, Set<IndexMemo> indexMemoHashSet, Set<MappingMemo> mappingMemoHashSet, Set<FieldMemo> fieldMemoHashSet, RegexInfo regexPattern, String[] split, NameWrapper nameWrapper, String schemaName) {
      Schema schemaFromRedis = nameWrapper.getSchemaFromRedis(schemaName, dataBase.isCaseSensitive());
      if (".*".equals(split[1])) {
         for(String name : dataBase.getTableNamelistKey(schemaName)) {
            this.checkRegexForTableRedis(nameWrapper, schemaFromRedis, dataBase, indexMemoHashSet, mappingMemoHashSet, fieldMemoHashSet, regexPattern, split, name);
         }
      } else {
         this.checkRegexForTableRedis(nameWrapper, schemaFromRedis, dataBase, indexMemoHashSet, mappingMemoHashSet, fieldMemoHashSet, regexPattern, split, split[1]);
      }

   }

   private void checkRegexForTableRedis(NameWrapper nameWrapper, Schema schemaFromRedis, DataBase dataBase, Set<IndexMemo> indexMemoHashSet, Set<MappingMemo> mappingMemoHashSet, Set<FieldMemo> fieldMemoHashSet, RegexInfo regexPattern, String[] split, String tableName) {
      Table table = nameWrapper.getTableFromRedis(schemaFromRedis, tableName, dataBase.isCaseSensitive());

      for(Column column : table.getColumnList()) {
         String item = schemaFromRedis.getName() + "<" + tableName + "<" + column.getColumnName();
         if (".*".equals(split[2])) {
            this.normalizeColumn(column.getDataType(), indexMemoHashSet, mappingMemoHashSet, fieldMemoHashSet, item, regexPattern);
         } else if (column.getColumnName().equals(split[2])) {
            this.normalizeColumn(column.getDataType(), indexMemoHashSet, mappingMemoHashSet, fieldMemoHashSet, item, regexPattern);
         }
      }

   }

   private void normalizeColumn(String dataType, Set<IndexMemo> indexMemoHashSet, Set<MappingMemo> mappingMemoHashSet, Set<FieldMemo> fieldMemoHashSet, String item, RegexInfo info) {
      String[] array = item.split("<");
      IndexMemo indexMemo = new IndexMemo();
      indexMemo.name = array[0];
      indexMemoHashSet.add(indexMemo);
      indexMemo.ref = info.indexRef;
      if (array.length > 1) {
         MappingMemo mappingMemo = new MappingMemo();
         mappingMemo.name = array[1];
         mappingMemo.ref = info.mappingRef;
         mappingMemo.index = indexMemo;
         mappingMemoHashSet.add(mappingMemo);
         if (array.length > 2) {
            FieldMemo fieldMemo = new FieldMemo();
            fieldMemo.ref = info.fieldRef;
            fieldMemo.name = item.split("<")[2];
            fieldMemo.index = indexMemo;
            fieldMemo.mapping = mappingMemo;
            fieldMemo.dataType = dataType;
            fieldMemoHashSet.add(fieldMemo);
         }
      }

   }

   public static List<RegexInfo> descartes(List<Item> regindexs, List<Item> regtables, List<Item> regfields) {
      List<RegexInfo> tempList = new ArrayList();
      Item item = new Item();
      item.regex = "*";
      if (regindexs.size() == 0) {
         regindexs.add(item);
      }

      for(Item regindex : regindexs) {
         for(Item regtable : regtables) {
            for(Item regfield : regfields) {
               RegexInfo regexInfo = new RegexInfo();
               regexInfo.indexRef = regindex.ref;
               regexInfo.mappingRef = regtable.ref;
               regexInfo.fieldRef = regfield.ref;
               regexInfo.regex = regindex.regex;
               regexInfo.regex = regindex.regex + "<" + regtable.regex + "<" + regfield.regex;
               tempList.add(regexInfo);
            }
         }
      }

      return tempList;
   }

   public List<String> where(String request) {
      try {
         EsParser.StatementContext statement = EsUtils.parse(request);
         WhereVisitor visitor = new WhereVisitor();
         visitor.visit(statement);
         return visitor.getWheres();
      } catch (Exception e) {
         e.printStackTrace();
         return new ArrayList();
      }
   }

   static class RegexInfo {
      EsParserContextBase indexRef;
      EsParserContextBase mappingRef;
      EsParserContextBase fieldRef;
      String regex;
   }

   static class Item {
      EsParserContextBase ref;
      String regex;
   }

   static class RefSet<T extends Reference> implements Set<T> {
      static final Object EMPTY = new Object();
      Map<T, T> map = new HashMap();

      public int size() {
         return this.map.size();
      }

      public boolean isEmpty() {
         return this.size() == 0;
      }

      public boolean contains(Object o) {
         return this.map.containsKey(o);
      }

      public Iterator<T> iterator() {
         return this.map.keySet().iterator();
      }

      public Object[] toArray() {
         return this.map.keySet().toArray();
      }

      public <T1> T1[] toArray(T1[] a) {
         return (T1[])this.map.keySet().toArray(a);
      }

      public boolean add(T t) {
         if (t == null) {
            return false;
         } else {
            T t1 = (T)(this.map.get(t));
            if (t1 != null) {
               if (!t1.refs.contains(t.ref)) {
                  t1.refs.add(t.ref);
               }

               return true;
            } else {
               this.map.put(t, t);
               return true;
            }
         }
      }

      public boolean remove(Object o) {
         return this.map.remove(o) != null;
      }

      public boolean containsAll(Collection<?> c) {
         for(Object o : c) {
            if (!this.map.containsKey(o)) {
               return false;
            }
         }

         return true;
      }

      public boolean addAll(Collection<? extends T> c) {
         for(T t : c) {
            this.add(t);
         }

         return true;
      }

      public boolean retainAll(Collection<?> c) {
         return false;
      }

      public boolean removeAll(Collection<?> c) {
         return false;
      }

      public void clear() {
         this.map.clear();
      }
   }
}
