package util;

import bean.Column;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.statement.SQLInsertStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.queryparser.classic.QueryParser;
import util.log.SQLLogger;
import vo.Auth;
import vo.Fieldinfo;
import vo.Tableinfo;

public class DataUtil {
   public static Map<String, Map<String, String>> secretMap = new HashMap();
   public static long lastModify = 0L;

   public static String dataDesensitization(Map<String, Object> paramMap) throws Exception {
      try {
         SQLLogger.log((String)paramMap.get("sql"));
         String type = (String)paramMap.get("type");
         String sql = (String)paramMap.get("sql");
         if (type == null) {
            return sql;
         } else {
            String schema = (String)paramMap.get("schema");
            List<Column> columnList = (List)paramMap.get("columnList");
            Map<String, String> tableMap = (Map)paramMap.get("tableMap");
            Map<String, String> params = (Map)paramMap.get("params");
            Map<String, String> schemaMap = (Map)paramMap.get("schemaMap");
            init(params);
            String oldSql = sql;
            if ("insert".equals(type)) {
               SQLInsertStatement insert = (SQLInsertStatement)paramMap.get("statement");
               sql = insertDesensitization(insert, schema);
            } else if ("insert_select".equals(type)) {
               SQLInsertStatement insert_select = (SQLInsertStatement)paramMap.get("statement");
               sql = insertSelectDesensitization(insert_select, schema);
            } else if ("update".equals(type)) {
               SQLUpdateStatement update = (SQLUpdateStatement)paramMap.get("statement");
               sql = updateDesensitization(update, schema);
            }

            if (!sql.equalsIgnoreCase(oldSql)) {
               paramMap.put("isMatched", "true");
            }

            return sql;
         }
      } catch (Exception e) {
         SQLLogger.log((String)paramMap.get("sql"), e.toString());
         throw e;
      }
   }

   public static String sqlModify(Map<String, Object> paramMap) throws Exception {
      try {
         SQLLogger.log((String)paramMap.get("sql"));
         Map<String, Map<String, String>> rulesMap = new HashMap();
         String type = (String)paramMap.get("type");
         String sql = (String)paramMap.get("sql");
         if (type == null) {
            return sql;
         } else {
            String schema = "";
            List<Column> columnList = (List)paramMap.get("columnList");
            Map<String, String> tableMap = (Map)paramMap.get("tableMap");
            Map<String, String> schemaMap = (Map)paramMap.get("schemaMap");
            if ("with_select".equals(type)) {
               sql = withSelectDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
            } else if ("select".equals(type)) {
               sql = selectDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
            } else if ("union".equals(type)) {
               sql = unionDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
            } else if ("insert".equals(type)) {
               SQLInsertStatement insert = (SQLInsertStatement)paramMap.get("statement");
               sql = insertDesensitization(insert, schema);
            } else if ("insert_select".equals(type)) {
               SQLInsertStatement insert_select = (SQLInsertStatement)paramMap.get("statement");
               sql = insertSelectDesensitization(insert_select, schema);
            } else if ("update".equals(type)) {
               SQLUpdateStatement update = (SQLUpdateStatement)paramMap.get("statement");
               sql = updateDesensitization(update, schema);
            }

            return sql;
         }
      } catch (Exception e) {
         SQLLogger.log((String)paramMap.get("sql"), e.toString());
         throw e;
      }
   }

   public static String columnDesensitization(Map<String, Object> paramMap) throws Exception {
      try {
         SQLLogger.log((String)paramMap.get("sql"));
         Map<String, Map<String, String>> rulesMap = new HashMap();
         String type = (String)paramMap.get("type");
         String sql = (String)paramMap.get("sql");
         if (type == null) {
            return sql;
         } else {
            Map<String, String> params = (Map)paramMap.get("params");
            String replaceSchema = ((String)params.get("replaceSchema")).replaceAll("[\"`]", "");
            String replaceTable = ((String)params.get("replaceTable")).replaceAll("[\"`]", "").trim();
            String replaceColumn = ((String)params.get("replaceColumn")).replaceAll("[\"`]", "").trim();
            String expression = (String)params.get("expression");
            Map<String, String> exp = new HashMap();
            exp.put(replaceColumn, expression);
            rulesMap.put(replaceSchema + "." + replaceTable, exp);
            String schema = (String)paramMap.get("schema");
            List<Column> columnList = (List)paramMap.get("columnList");
            Map<String, String> tableMap = (Map)paramMap.get("tableMap");
            Map<String, String> schemaMap = (Map)paramMap.get("schemaMap");
            String oldSql = sql;
            if ("with_select".equals(type)) {
               sql = withSelectDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
            } else if ("select".equals(type)) {
               sql = selectDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
            } else if ("union".equals(type)) {
               sql = unionDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
            } else if ("insert".equals(type)) {
               SQLInsertStatement insert = (SQLInsertStatement)paramMap.get("statement");
               sql = insertDesensitization(insert, schema);
            } else if ("insert_select".equals(type)) {
               SQLInsertStatement insert_select = (SQLInsertStatement)paramMap.get("statement");
               sql = insertSelectDesensitization(insert_select, schema);
            } else if ("update".equals(type)) {
               SQLUpdateStatement update = (SQLUpdateStatement)paramMap.get("statement");
               sql = updateDesensitization(update, schema);
            }

            if (!sql.equalsIgnoreCase(oldSql)) {
               paramMap.put("isMatched", "true");
            }

            return sql;
         }
      } catch (Exception e) {
         SQLLogger.log((String)paramMap.get("sql"), e.toString());
         throw e;
      }
   }

   public static String tableDesensitization(Map<String, Object> paramMap) throws Exception {
      try {
         SQLLogger.log((String)paramMap.get("sql"));
         String sql = (String)paramMap.get("sql");
         Map<String, String> params = (Map)paramMap.get("params");
         Map<String, String> tableMap = (Map)paramMap.get("tableMap");
         String replaceTable = "^" + ((String)params.get("replaceTable")).replaceAll("`", "") + "$";
         String replaceNewTable = ((String)params.get("replaceNewTable")).replaceAll("`", "").trim();
         List<String> replaceList = new ArrayList();

         for(Map.Entry<String, String> entry : tableMap.entrySet()) {
            String value = (String)entry.getValue();
            if (JdbcUtil.followRules(value.replaceAll("[\"`]", ""), replaceTable)) {
               replaceList.add(value);
            }
         }

         String oldSql = sql;

         for(String s : replaceList) {
            sql = sql.replaceAll("(?i)" + s + "\\.", replaceNewTable + ".");
            sql = sql.replaceAll("(?i)\\." + s + "", "." + replaceNewTable);
         }

         if (!sql.equalsIgnoreCase(oldSql)) {
            paramMap.put("isMatched", "true");
         }

         return sql;
      } catch (Exception e) {
         SQLLogger.log((String)paramMap.get("sql"), e.toString());
         throw e;
      }
   }

   public static String desensitization(Map<String, Object> paramMap) throws Exception {
      try {
         SQLLogger.log((String)paramMap.get("sql"));
         Map<String, Map<String, String>> rulesMap = new HashMap();
         String type = (String)paramMap.get("type");
         String sql = (String)paramMap.get("sql");
         if (type == null) {
            return sql;
         } else {
            Map<String, String> params = (Map)paramMap.get("params");
            String replaceSchema = ((String)params.get("replaceSchema")).replaceAll("`", "").trim();
            String replaceTable = ((String)params.get("replaceTable")).replaceAll("`", "").trim();
            String replaceColumn = ((String)params.get("replaceColumn")).replaceAll("`", "").trim();
            String expression = (String)params.get("expression");
            if (expression != null && !"".equals(expression)) {
               String oldSql = sql;
               String schema = (String)paramMap.get("schema");
               List<Column> columnList = (List)paramMap.get("columnList");
               Map<String, String> tableMap = (Map)paramMap.get("tableMap");
               Map<String, String> schemaMap = (Map)paramMap.get("schemaMap");

               for(Column column : columnList) {
                  String columnObj = column.getColumnName();
                  String[] cols = columnObj.split("\\.");
                  if (cols.length >= 2) {
                     String alias = cols[0];
                     if (alias != null && alias.length() > 0) {
                        alias = alias.replaceAll("[\"`]", "");
                     }

                     String name = cols[1];
                     String tableName = "";
                     if (tableMap.containsKey(alias)) {
                        tableName = ((String)tableMap.get(alias)).replaceAll("[\"`]", "").trim();
                     } else if (tableMap.containsKey(alias.toUpperCase())) {
                        tableName = ((String)tableMap.get(alias.toUpperCase())).replaceAll("[\"`]", "").trim();
                     }

                     String schema_temp = (String)schemaMap.get(tableName);
                     String schema_real = schema_temp == null ? schema : schema_temp;
                     if (schema_real != null) {
                        schema_real = schema_real.replaceAll("`", "").trim();
                     }

                     if (JdbcUtil.followRules(name, replaceColumn) && JdbcUtil.followRules(tableName, replaceTable) && JdbcUtil.followRules(schema_real, replaceSchema)) {
                        String schemaName = schema_real.replaceAll("[\"`]", "") + "." + tableName.replaceAll("[\"`]", "");
                        Map<String, String> exp = (Map)rulesMap.get(schemaName);
                        if (exp == null) {
                           exp = new HashMap();
                        }

                        exp.put(name, expression);
                        rulesMap.put(schemaName, exp);
                     }
                  }
               }

               if ("with_select".equals(type)) {
                  sql = withSelectDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
               } else if ("select".equals(type)) {
                  sql = selectDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
               } else if ("union".equals(type)) {
                  sql = unionDesensitization(sql, schema, columnList, tableMap, schemaMap, rulesMap);
               } else if ("insert".equals(type)) {
                  SQLInsertStatement insert = (SQLInsertStatement)paramMap.get("statement");
                  sql = insertDesensitization(insert, schema);
               } else if ("insert_select".equals(type)) {
                  SQLInsertStatement insert_select = (SQLInsertStatement)paramMap.get("statement");
                  sql = insertSelectDesensitization(insert_select, schema);
               } else if ("update".equals(type)) {
                  SQLUpdateStatement update = (SQLUpdateStatement)paramMap.get("statement");
                  sql = updateDesensitization(update, schema);
               }

               if (!sql.equalsIgnoreCase(oldSql)) {
                  paramMap.put("isMatched", "true");
               }

               return sql;
            } else {
               return sql;
            }
         }
      } catch (Exception e) {
         SQLLogger.log((String)paramMap.get("sql"), e.toString());
         throw e;
      }
   }

   public static void init(Map<String, String> params) {
      String path = (String)params.get("configPath");
      List<JSONObject> ruleList = readRules(path);
      if (ruleList != null) {
         for(JSONObject rule : ruleList) {
            for(String key : rule.keySet()) {
               String type = key.split("_")[2];
               if ("1".equals(type)) {
                  parseRules(rule.get(key).toString());
               } else if (!"2".equals(type) && "3".equals(type)) {
                  parseSecrt(rule.get(key).toString());
               }
            }
         }

      }
   }

   public static void parseRules(String rule) {
      Auth auth = (Auth)JSONObject.parseObject(rule, Auth.class);

      for(Tableinfo tableinfo : auth.getTableinfo()) {
         Map<String, String> fieldMap = new HashMap();

         for(Fieldinfo fieldinfo : tableinfo.getFieldinfo()) {
            fieldMap.put(fieldinfo.getColumnname(), fieldinfo.getMap().getRuleinfo().getDesensitizationexpression());
         }
      }

   }

   public static void parseSecrt(String rule) {
      Auth auth = (Auth)JSONObject.parseObject(rule, Auth.class);

      for(Tableinfo tableinfo : auth.getTableinfo()) {
         Map<String, String> fieldMap = new HashMap();

         for(Fieldinfo fieldinfo : tableinfo.getFieldinfo()) {
            fieldMap.put(fieldinfo.getColumnname(), fieldinfo.getMap().getRuleinfo().getDesensitizationexpression());
         }

         secretMap.put(tableinfo.getSchemaname() + "." + tableinfo.getTablename(), fieldMap);
      }

   }

   public static List<JSONObject> readRules(String path) {
      if (path != null && !"".equals(path)) {
         File file = new File(path);
         if (!file.exists()) {
            return null;
         } else {
            long time = file.lastModified();
            if (time == lastModify) {
               return null;
            } else {
               lastModify = time;
               List<JSONObject> list = new ArrayList();

               try {
                  BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path)));
                  String content = "";

                  while((content = reader.readLine()) != null) {
                     JSONObject jsonObject = JSONObject.parseObject(content);
                     list.add(jsonObject);
                  }
               } catch (Exception e) {
                  e.printStackTrace();
               }

               return list;
            }
         }
      } else {
         return null;
      }
   }

   public static String withSelectDesensitization(String sql, String schema, List<Column> columnList, Map<String, String> tableMap, Map<String, String> schemaMap, Map<String, Map<String, String>> rulesMap) {
      for(Column column : columnList) {
         String alias = column.getColumnName().split("\\.")[0];
         String columnName = column.getColumnName().split("\\.")[1];
         String tableName = ((String)tableMap.get(alias)).replaceAll("`", "");
         String schema_temp = (String)schemaMap.get(tableName);
         String schema_real = (schema_temp == null ? schema : schema_temp).replaceAll("`", "");
         boolean addAlias = false;
         Map<String, String> map = (Map)rulesMap.get(schema_real + "." + tableName);
         if (map != null) {
            String rule = (String)map.get(columnName.replaceAll("[\"`]", ""));
            if (rule != null) {
               if (column.getColumnAlisa() == null && !column.isMethod()) {
                  rule = rule + " as " + columnName;
               }

               String col = column.getColumnName();
               rule = rule.replaceAll("\\$\\{value}", col);
               if (column.isSubQuery()) {
                  rule = rule.replaceAll("(?i)" + column.getColumnName(), column.getSubAlisa() + "." + columnName);
                  addAlias = true;
               }

               sql = replaceRule(sql, col, rule);
            }
         }

         if (column.isSubQuery() && !addAlias) {
            sql = sql.replaceFirst("(?i)" + column.getColumnName(), column.getSubAlisa() + "." + columnName);
         }

         String replaceColumn = (new StringBuilder(column.getColumnName())).insert(1, "#@#").toString();
         sql = sql.replaceFirst("(?i)" + column.getColumnName(), replaceColumn);
      }

      return sql.replaceAll("#@#", "");
   }

   public static String selectDesensitization(String sql, String schema, List<Column> columnList, Map<String, String> tableMap, Map<String, String> schemaMap, Map<String, Map<String, String>> rulesMap) {
      for(Column column : columnList) {
         String[] cols = column.getColumnName().split("\\.");
         if (cols.length >= 2) {
            String alias = cols[0];
            if (alias != null) {
               alias = alias.replaceAll("[`\"]", "");
            }

            String columnName = cols[1];
            if (tableMap.containsKey(alias)) {
               alias = alias;
            } else {
               if (!tableMap.containsKey(alias.toUpperCase())) {
                  continue;
               }

               alias = alias.toUpperCase();
            }

            String tableName = ((String)tableMap.get(alias)).replaceAll("[`]", "");
            String schema_temp = (String)schemaMap.get(tableName);
            String schema_real = (schema_temp == null ? schema : schema_temp).replaceAll("[\"`]", "");
            boolean addAlias = false;
            String col = column.isSubQuery() ? column.getNowName() : column.getColumnName();
            String aliasColumnName = column.getNowName() == null ? columnName : column.getNowName().split("\\.")[1];
            Map<String, String> map = (Map)rulesMap.get(schema_real + "." + tableName.replaceAll("\"", ""));
            if (map != null) {
               String rule = (String)map.get(columnName.replaceAll("[\"`]", ""));
               if (rule != null) {
                  if ("null".equalsIgnoreCase(rule)) {
                     col = QueryParser.escape(column.getFullName());
                  }

                  if (column.isSubQuery()) {
                     aliasColumnName = column.getNowName().split("\\.")[1];
                  }

                  if (column.getColumnAlisa() == null && !column.isMethod()) {
                     rule = rule + " as " + aliasColumnName;
                  }

                  rule = rule.replaceAll("\\$\\{value}", col);
                  if (column.isSubQuery()) {
                     rule = rule.replaceAll("(?i)" + col.replaceAll("\\.", "\\\\."), column.getSubAlisa() + "." + aliasColumnName);
                     addAlias = true;
                  }

                  sql = replaceRule(sql, col.replaceAll("\\.", "\\\\."), rule);
               }
            }

            if (column.isSubQuery() && !addAlias) {
               sql = sql.replaceFirst("(?i)" + col.replaceAll("\\.", "\\\\."), column.getSubAlisa() + "." + aliasColumnName);
            }

            String replaceColumn = (new StringBuilder(col)).insert(1, "#@#").toString();
            sql = sql.replaceFirst("(?i)" + col.replaceAll("\\.", "\\\\."), replaceColumn);
         }
      }

      return sql.replaceAll("#@#", "");
   }

   public static String unionDesensitization(String sql, String schema, List<Column> columnList, Map<String, String> tableMap, Map<String, String> schemaMap, Map<String, Map<String, String>> rulesMap) {
      for(Column column : columnList) {
         String alias = column.getColumnName().split("\\.")[0];
         String columnName = column.getColumnName().split("\\.")[1];
         String tableName = ((String)tableMap.get(alias)).replaceAll("`", "");
         String schema_temp = (String)schemaMap.get(tableName);
         String schema_real = (schema_temp == null ? schema : schema_temp).replaceAll("`", "");
         boolean addAlias = false;
         Map<String, String> map = (Map)rulesMap.get(schema_real + "." + tableName);
         if (map != null) {
            String rule = (String)map.get(columnName.replaceAll("[\"`]", ""));
            if (rule != null) {
               if (column.getColumnAlisa() == null && !column.isMethod()) {
                  rule = rule + " as " + columnName;
               }

               String col = column.getColumnName();
               rule = rule.replaceAll("\\$\\{value}", col);
               if (column.isSubQuery()) {
                  rule = rule.replaceAll("(?i)" + column.getColumnName(), column.getSubAlisa() + "." + columnName);
                  addAlias = true;
               }

               sql = replaceRule(sql, col, rule);
            }
         }

         if (column.isSubQuery() && !addAlias) {
            sql = sql.replaceAll("(?i)" + column.getColumnName(), column.getSubAlisa() + "." + columnName);
         }

         String replaceColumn = (new StringBuilder(column.getColumnName())).insert(1, "#@#").toString();
         sql = sql.replaceFirst("(?i)" + column.getColumnName(), replaceColumn);
      }

      return sql.replaceAll("#@#", "");
   }

   private static String insertDesensitization(SQLInsertStatement statement, String schema) {
      String tableName = statement.getTableName().getSimpleName();
      List<SQLExpr> columns = statement.getColumns();

      for(SQLInsertStatement.ValuesClause valuesClause : statement.getValuesList()) {
         List<SQLExpr> values = valuesClause.getValues();

         for(int i = 0; i < values.size(); ++i) {
            String column = ((SQLExpr)columns.get(i)).toString();
            Map<String, String> map = (Map)secretMap.get(schema + "." + tableName);
            if (map != null) {
               String rule = (String)map.get(column);
               if (rule != null) {
                  rule = rule.replaceAll("\\$\\{value}", ((SQLExpr)values.get(i)).toString());
                  SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr();
                  sqlIdentifierExpr.setName(rule);
                  values.set(i, sqlIdentifierExpr);
               }
            }
         }
      }

      return statement.toString();
   }

   private static String insertSelectDesensitization(SQLInsertStatement statement, String schema) {
      String tableName = statement.getTableName().getSimpleName();
      List<SQLExpr> columns = statement.getColumns();
      SQLSelectQueryBlock query = (SQLSelectQueryBlock)statement.getQuery().getQuery();
      List<SQLSelectItem> selectList = query.getSelectList();

      for(int i = 0; i < selectList.size(); ++i) {
         SQLSelectItem values = (SQLSelectItem)selectList.get(i);
         String column = ((SQLExpr)columns.get(i)).toString();
         Map<String, String> map = (Map)secretMap.get(schema + "." + tableName);
         if (map != null) {
            String rule = (String)map.get(column);
            if (rule != null) {
               rule = rule.replaceAll("\\$\\{value}", values.toString());
               SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr();
               sqlIdentifierExpr.setName(rule);
               values.setExpr(sqlIdentifierExpr);
            }
         }
      }

      return statement.toString();
   }

   private static String updateDesensitization(SQLUpdateStatement statement, String schema) {
      String tableName = statement.getTableName().getSimpleName();

      for(SQLUpdateSetItem item : statement.getItems()) {
         String[] columns = item.getColumn().toString().split("\\.");
         String column = columns.length > 1 ? columns[1] : columns[0];
         SQLExpr value = item.getValue();
         Map<String, String> map = (Map)secretMap.get(schema + "." + tableName);
         if (map != null) {
            String rule = (String)map.get(column);
            if (rule != null) {
               rule = rule.replaceAll("\\$\\{value}", value.toString());
               SQLIdentifierExpr sqlIdentifierExpr = new SQLIdentifierExpr();
               sqlIdentifierExpr.setName(rule);
               item.setValue(sqlIdentifierExpr);
            }
         }
      }

      return statement.toString();
   }

   public static String replaceRule(String sql, String col, String rule) {
      sql = sql.replaceFirst(col, rule);
      return sql;
   }

   public static void main(String[] args) throws SQLException {
   }

   public String longestCommonPrefix(String[] strs) {
      return null;
   }
}
