package com.chenyang.nse.bussiness.tools.dataprocess.elasticsearch;

import cn.hutool.core.convert.Convert;
import com.ghca.fastjson.JSONArray;
import com.ghca.fastjson.JSONObject;
import com.chenyang.nse.bussiness.commmon.json.ErrCode;
import com.chenyang.nse.bussiness.common.PropertiesConstant;
import com.chenyang.nse.bussiness.dao.table.core.encryption.TCoreJiaMiJieMiJinDuDao;
import com.chenyang.nse.bussiness.entity.orm.table.core.TCoreDatasystem;
import com.chenyang.nse.bussiness.entity.orm.table.core.encryption.ColumnPrimaryKeyInfo;
import com.chenyang.nse.bussiness.entity.orm.table.core.encryption.TCoreJiaMiJieMiJinDu;
import com.chenyang.nse.bussiness.entity.vo.strategy.ColumnInfoVO;
import com.chenyang.nse.bussiness.exception.CustomException;
import com.chenyang.nse.bussiness.service.core.encryption.TCoreEncryptionService;
import com.chenyang.nse.bussiness.service.core.encryption.impl.TCoreEncryptionContext;
import com.chenyang.nse.bussiness.tools.dataprocess.ApplicationContextProvider;
import com.chenyang.nse.bussiness.tools.dataprocess.EncTaskStateContainer;
import com.chenyang.nse.bussiness.tools.dataprocess.EncTaskTracker;
import com.chenyang.nse.bussiness.tools.dataprocess.common.ProcessStatus;
import com.chenyang.nse.bussiness.tools.encryption.ElasticSearchEncDecProvider;
import com.chenyang.nse.bussiness.tools.es.ElasticSearchUtil;
import com.chenyang.nse.bussiness.tools.logger.LoggerBuilder;
import com.chenyang.nse.bussiness.tools.propertyutil.PropertyGhcaUtil;
import com.chenyang.nse.bussiness.tools.string.AesTool;
import com.chenyang.nse.bussiness.tools.utils.GHCAUtils;
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.frameworkset.elasticsearch.client.ClientInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

public class ElasticSearchTask implements Runnable {
   private static final Logger log = LoggerFactory.getLogger(ElasticSearchTask.class);
   private final String copySuffix;
   private final String encryptionlabel;
   private final String pk;
   private final String projectId;
   private final String dataSystemId;
   private final TCoreDatasystem tCoreDatasystem;
   private final String schema;
   private final String tableName;
   private final List<ColumnInfoVO> columnInfoList;
   private final List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumn;
   private final String[] primaryKeys;
   private final List<ColumnPrimaryKeyInfo> columnPrimaryKeyInfoList;
   private final List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumnList;
   private ClientInterface connect;
   private EncTaskStateContainer container;
   private int finishCount = 0;
   private int failCount = 0;
   private ch.qos.logback.classic.Logger encryptionlogger;
   private final String progressFlag;
   private final CountDownLatch countDownLatch;
   private final Map<String, Integer> columnFailCount;
   private boolean isReload;

   public ElasticSearchTask(String projectId, String dataSystemId, TCoreDatasystem tCoreDatasystem, String schema, String tableName, String[] primaryKeys, List<ColumnInfoVO> columnInfoList, List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumn, List<ColumnPrimaryKeyInfo> columnPrimaryKeyInfoList, List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumnList, boolean isReload, String progressFlag, CountDownLatch countDownLatch) {
      this.copySuffix = PropertyGhcaUtil.copySuffix;
      this.encryptionlabel = PropertyGhcaUtil.encryptionlabel;
      this.pk = PropertyGhcaUtil.ELASTIC_SEARCH_PK;
      this.projectId = projectId;
      this.dataSystemId = dataSystemId;
      this.tCoreDatasystem = tCoreDatasystem;
      this.schema = schema;
      this.tableName = tableName;
      this.columnInfoList = columnInfoList;
      this.allColumn = allColumn;
      this.primaryKeys = primaryKeys;
      this.columnPrimaryKeyInfoList = columnPrimaryKeyInfoList;
      this.allColumnList = allColumnList;
      this.isReload = isReload;
      this.progressFlag = progressFlag;
      this.countDownLatch = countDownLatch;
      this.columnFailCount = new HashMap();

      try {
         this.connect = GHCAUtils.getInstance().connectToES(tCoreDatasystem.getDbip(), tCoreDatasystem.getDbport(), tCoreDatasystem.getUsername(), AesTool.decrypt(tCoreDatasystem.getPassword(), "ghca"));
      } catch (Exception var15) {
         log.error("connect to es {}:{} error", tCoreDatasystem.getDbip(), tCoreDatasystem.getDbport());
         throw new CustomException(ErrCode.CONNECT_ERROR);
      }

      this.container = EncTaskTracker.instance().container();
      this.container.add(projectId, schema, tableName, columnInfoList);
      EncTaskTracker.instance().register(this.container);
   }

   public void run() {
      try {
         this.runTask();
      } finally {
         if (Objects.nonNull(this.connect)) {
            this.connect.getClient().close();
         }

         EncTaskTracker.instance().over(this.container.getTask());
         if (Objects.nonNull(this.countDownLatch)) {
            this.countDownLatch.countDown();
         }

      }

   }

   private void runTask() {
      StringBuilder stringBuilder = new StringBuilder();
      stringBuilder.append(System.getProperty("user.dir")).append(File.separator).append("encryprocesslog").append(File.separator).append(this.projectId).append(File.separator).append(this.dataSystemId);
      String loggerPath = stringBuilder.toString();
      String loggerName = this.schema + "." + this.tableName;
      this.encryptionlogger = (new LoggerBuilder()).getLogger(loggerPath, loggerName);
      Thread.currentThread().setName("pool-" + loggerName + "-thread");
      this.encryptionlogger.info("启动执行线程！！");
      if (CollectionUtils.isEmpty(this.columnInfoList)) {
         String content = "0".equals(this.progressFlag) ? "加密" : "解密";
         this.encryptionlogger.info("{}已执行完成，无需执行{}/未配置{}字段", new Object[]{content, content, content});
      } else {
         TCoreJiaMiJieMiJinDuDao tCoreJiaMiJieMiJinDuDao = (TCoreJiaMiJieMiJinDuDao)ApplicationContextProvider.getBean(TCoreJiaMiJieMiJinDuDao.class);
         String property = PropertiesConstant.properties.getProperty("esWaitTime", "2000");
         int esWaitTime = Integer.parseInt(property.trim());
         this.updateFlag(ProcessStatus.RUNNING);
         int total = Convert.toInt(this.connect.countAll(this.schema + "/" + this.tableName));
         List<TCoreJiaMiJieMiJinDu> tCoreJiaList = tCoreJiaMiJieMiJinDuDao.queryTCoreJiaMiJieMiJinDu(this.dataSystemId, this.projectId, this.schema, this.tableName, this.progressFlag);
         int tCoreJiaListSize = tCoreJiaList.size();
         String id;
         if (0 == tCoreJiaListSize) {
            TCoreJiaMiJieMiJinDu tCoreJiaMiJieMiJinDu = new TCoreJiaMiJieMiJinDu();
            tCoreJiaMiJieMiJinDu.setProjectid(this.projectId);
            tCoreJiaMiJieMiJinDu.setDatasystemid(this.dataSystemId);
            tCoreJiaMiJieMiJinDu.setSchemahh(this.schema);
            tCoreJiaMiJieMiJinDu.setTablename(this.tableName);
            tCoreJiaMiJieMiJinDu.setFlag(this.progressFlag);
            tCoreJiaMiJieMiJinDu.setTotalcounts(total);
            tCoreJiaMiJieMiJinDu.setStarttime(new Date());
            tCoreJiaMiJieMiJinDuDao.save(tCoreJiaMiJieMiJinDu);
            id = tCoreJiaMiJieMiJinDu.getId();
         } else {
            TCoreJiaMiJieMiJinDu tCoreJiaMiJieMiJinDu = (TCoreJiaMiJieMiJinDu)tCoreJiaList.get(tCoreJiaList.size() - 1);
            tCoreJiaMiJieMiJinDu.setTotalcounts(total);
            tCoreJiaMiJieMiJinDu.setStarttime(new Date());
            tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJieMiJinDu);
            id = tCoreJiaMiJieMiJinDu.getId();
         }

         if (0 == total) {
            this.finish(id);
         } else {
            int from = 0;
            int size = 10000;

            while(from <= total) {
               StringBuilder stringBuilderEntity = new StringBuilder();
               String result = ElasticSearchUtil.queryByPage(this.connect, this.schema, this.tableName, from, size);
               from += size;
               this.encryptDecrypt(result, stringBuilderEntity);
               ElasticSearchUtil.update(this.connect, stringBuilderEntity.toString());
            }

            this.finish(id);
         }
      }
   }

   private void encryptDecrypt(String result, StringBuilder stringBuilderEntity) {
      if (!StringUtils.isBlank(result)) {
         JSONObject jsonObject = JSONObject.parseObject(result);
         JSONObject jsonObjectHits = jsonObject.getJSONObject("hits");
         if (!Objects.isNull(jsonObjectHits)) {
            this.processEncDec(jsonObjectHits.getJSONArray("hits"), stringBuilderEntity);
         }
      }
   }

   private void finish(String id) {
      TCoreJiaMiJieMiJinDuDao tCoreJiaMiJieMiJinDuDao = (TCoreJiaMiJieMiJinDuDao)ApplicationContextProvider.getBean(TCoreJiaMiJieMiJinDuDao.class);
      TCoreJiaMiJieMiJinDu tCoreJiaMiJieMiJinDu = new TCoreJiaMiJieMiJinDu();
      tCoreJiaMiJieMiJinDu.setId(id);
      tCoreJiaMiJieMiJinDu.setState(0 == this.failCount ? "1" : "-1");
      tCoreJiaMiJieMiJinDu.setFailcounts(this.failCount);
      tCoreJiaMiJieMiJinDu.setFinishcounts(this.finishCount);
      tCoreJiaMiJieMiJinDu.setEndtime(new Date());
      tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJieMiJinDu);
      this.printLog();
      if (0 == this.failCount) {
         this.updateFlag(ProcessStatus.COMPLETE);
      } else {
         for(ColumnInfoVO columnInfoVO : this.columnInfoList) {
            Integer count = (Integer)this.columnFailCount.get(columnInfoVO.getColumnName());
            if (Objects.isNull(count)) {
               this.updateFlag(columnInfoVO.getColumnName(), ProcessStatus.COMPLETE);
            } else {
               this.updateFlag(columnInfoVO.getColumnName(), ProcessStatus.FAIL);
            }
         }
      }

   }

   private void processEncDec(JSONArray jsonArray, StringBuilder stringBuilderEntity) {
      for(int i =0; i < jsonArray.size(); i ++) {
         JSONObject jsonObject = jsonArray.getJSONObject(i);
         String id = jsonObject.getString("_id");
         JSONObject jsonObjectEntity = jsonObject.getJSONObject("_source");
         boolean resultFlag = this.processEncDec(jsonObjectEntity);
         if (resultFlag) {
            String data = ElasticSearchUtil.assembleData(this.schema, this.tableName, id, jsonObjectEntity.toString());
            stringBuilderEntity.append(data);
            ++this.finishCount;
         } else {
            ++this.failCount;
            this.encryptionlogger.warn("提交到" + this.finishCount + "行，执行异常!");
            this.encryptionlogger.warn("{}行加密数据提交失败!", this.finishCount);
         }
      }

   }

   private boolean processEncDec(JSONObject jsonObject) {
      boolean success = true;

      for(ColumnInfoVO columnInfoVO : this.columnInfoList) {
         String[] columnNameArr = columnInfoVO.getColumnName().split("\\.");

         try {
            this.processEncDec((JSONObject)jsonObject, columnInfoVO, columnNameArr, 0);
         } catch (Exception var7) {
            if (success) {
               success = false;
            }
         }
      }

      return success;
   }

   private void processEncDec(JSONObject jsonObject, ColumnInfoVO columnInfoVO, String[] columnNameArr, int index) {
      if (!Objects.isNull(jsonObject)) {
         if (columnNameArr.length != index + 1) {
            Object object = jsonObject.get(columnNameArr[index]);
            if (!Objects.isNull(object)) {
               if (object instanceof JSONObject) {
                  JSONObject var10001 = (JSONObject)object;
                  ++index;
                  this.processEncDec(var10001, columnInfoVO, columnNameArr, index);
               } else {
                  if (!(object instanceof JSONArray)) {
                     log.error("unsupported json structure, json = {}", jsonObject);
                     return;
                  }

                  JSONArray var16 = (JSONArray)object;
                  ++index;
                  this.processEncDec(var16, columnInfoVO, columnNameArr, index);
               }

            }
         } else {
            String key = columnNameArr[index];
            Object object = jsonObject.get(key);
            if (object instanceof String) {
               String beforeValue = object.toString();
               String afterValue = this.doMethod(columnInfoVO, beforeValue);
               jsonObject.put(key, afterValue);
            } else {
               if (!(object instanceof JSONArray)) {
                  log.error("unsupported json structure, json = {}", jsonObject);
                  return;
               }

               JSONArray jsonArray = (JSONArray)object;

               for(int i = 0; i < jsonArray.size(); ++i) {
                  String beforeValue = jsonArray.getString(i);
                  String afterValue = this.doMethod(columnInfoVO, beforeValue);
                  jsonArray.set(i, afterValue);
               }
            }

         }
      }
   }

   private void processEncDec(JSONArray jsonArray, ColumnInfoVO columnInfoVO, String[] columnNameArr, int index) {
      if (columnNameArr.length == index + 1) {
         for(int i = 0; i < jsonArray.size(); ++i) {
            Object object = jsonArray.get(i);
            if (object instanceof String) {
               String beforeValue = object.toString();
               String afterValue = this.doMethod(columnInfoVO, beforeValue);
               jsonArray.set(i, afterValue);
            } else {
               if (!(object instanceof JSONObject)) {
                  log.error("unsupported jsonArray structure, json = {}", jsonArray);
                  return;
               }

               String key = columnNameArr[index];
               JSONObject jsonObject = (JSONObject)object;
               Object objectTemp = jsonObject.get(key);
               if (!(objectTemp instanceof String)) {
                  log.error("unsupported jsonObject structure, json = {}", jsonArray);
                  return;
               }

               String beforeValue = objectTemp.toString();
               String afterValue = this.doMethod(columnInfoVO, beforeValue);
               jsonObject.put(key, afterValue);
            }
         }

      } else {
         for(Object object : jsonArray) {
            if (object instanceof JSONObject) {
               JSONObject var10001 = (JSONObject)object;
               ++index;
               this.processEncDec(var10001, columnInfoVO, columnNameArr, index);
            } else {
               if (!(object instanceof JSONArray)) {
                  log.error("unsupported json structure, json = {}", jsonArray);
                  return;
               }

               JSONArray var16 = (JSONArray)object;
               ++index;
               this.processEncDec(var16, columnInfoVO, columnNameArr, index);
            }
         }

      }
   }

   private String doMethod(ColumnInfoVO columnInfoVO, String value) {
      return "0".equals(this.progressFlag) ? this.doEncryptionMethod(columnInfoVO, value) : this.doDecryptionMethod(columnInfoVO, value);
   }

   private String doEncryptionMethod(ColumnInfoVO columnInfoVO, String value) {
      if (StringUtils.isBlank(columnInfoVO.getEncryptionType())) {
         log.warn("encryptionType is blank!");
         return value;
      } else {
         String columnType = columnInfoVO.getColumnType();
         String[] split = columnType.split(":");
         if ("text".equals(split[0])) {
            String analyze = ElasticSearchUtil.getAnalyze(this.connect, this.schema, value, split.length > 1 ? split[1] : null);
            JSONObject jsonObject = JSONObject.parseObject(analyze);
            JSONArray jsonArray = jsonObject.getJSONArray("tokens");
            List<String> valueList = (List)jsonArray.stream().map((j) -> ((JSONObject)j).getString("token")).collect(Collectors.toList());
            return this.encrypt(value, valueList, columnInfoVO);
         } else {
            return this.encrypt(value, columnInfoVO);
         }
      }
   }

   private String encrypt(String value, List<String> valueList, ColumnInfoVO columnInfoVO) {
      StringBuilder stringBuilder = new StringBuilder();

      try {
         for(String valueTemp : valueList) {
            valueTemp = ElasticSearchEncDecProvider.encrypt(columnInfoVO.getEncryptionType(), columnInfoVO.getSecretKey(), valueTemp, columnInfoVO.getIsLike(), columnInfoVO.getKeepfirst(), columnInfoVO.getEncdigit(), columnInfoVO.getTwoindex());
            stringBuilder.append(valueTemp).append(" ");
         }

         value = ElasticSearchEncDecProvider.encrypt(columnInfoVO.getEncryptionType(), columnInfoVO.getSecretKey(), value, columnInfoVO.getIsLike(), columnInfoVO.getKeepfirst(), columnInfoVO.getEncdigit(), columnInfoVO.getTwoindex());
         stringBuilder.append(value).append(" ");
      } catch (Exception e) {
         Integer count = (Integer)this.columnFailCount.get(columnInfoVO.getColumnName());
         this.columnFailCount.put(columnInfoVO.getColumnName(), Objects.isNull(count) ? 1 : Integer.valueOf(count + 1));
         this.encryptionlogger.error("encrypt error", e);
         throw new CustomException(ErrCode.DATA_PROCESS_ERROR);
      }

      return stringBuilder.substring(0, stringBuilder.length() - 1);
   }

   private String encrypt(String value, ColumnInfoVO columnInfoVO) {
      try {
         value = ElasticSearchEncDecProvider.encrypt(columnInfoVO.getEncryptionType(), columnInfoVO.getSecretKey(), value, columnInfoVO.getIsLike(), columnInfoVO.getKeepfirst(), columnInfoVO.getEncdigit(), columnInfoVO.getTwoindex());
         return value;
      } catch (Exception e) {
         Integer count = (Integer)this.columnFailCount.get(columnInfoVO.getColumnName());
         this.columnFailCount.put(columnInfoVO.getColumnName(), Objects.isNull(count) ? 1 : Integer.valueOf(count + 1));
         this.encryptionlogger.error("encrypt error", e);
         throw new CustomException(ErrCode.DATA_PROCESS_ERROR);
      }
   }

   private String doDecryptionMethod(ColumnInfoVO columnInfoVO, String value) {
      if (StringUtils.isBlank(columnInfoVO.getEncryptionType())) {
         log.warn("encryptionType is blank!");
         return value;
      } else {
         String columnType = columnInfoVO.getColumnType();
         String[] split = columnType.split(":");
         if ("text".equals(split[0])) {
            String[] valueArr = value.split(" ");
            return this.decrypt(valueArr, columnInfoVO);
         } else {
            return this.decrypt(value, columnInfoVO);
         }
      }
   }

   private String decrypt(String[] valueArr, ColumnInfoVO columnInfoVO) {
      try {
         String value = valueArr[valueArr.length - 1];
         value = ElasticSearchEncDecProvider.decrypt(columnInfoVO.getEncryptionType(), columnInfoVO.getSecretKey(), value, columnInfoVO.getIsLike(), columnInfoVO.getKeepfirst(), columnInfoVO.getEncdigit(), columnInfoVO.getTwoindex());
         return value;
      } catch (Exception e) {
         Integer count = (Integer)this.columnFailCount.get(columnInfoVO.getColumnName());
         this.columnFailCount.put(columnInfoVO.getColumnName(), Objects.isNull(count) ? 1 : Integer.valueOf(count + 1));
         this.encryptionlogger.error("decrypt error", e);
         throw new CustomException(ErrCode.DATA_PROCESS_ERROR);
      }
   }

   private String decrypt(String value, ColumnInfoVO columnInfoVO) {
      try {
         value = ElasticSearchEncDecProvider.decrypt(columnInfoVO.getEncryptionType(), columnInfoVO.getSecretKey(), value, columnInfoVO.getIsLike(), columnInfoVO.getKeepfirst(), columnInfoVO.getEncdigit(), columnInfoVO.getTwoindex());
         return value;
      } catch (Exception e) {
         Integer count = (Integer)this.columnFailCount.get(columnInfoVO.getColumnName());
         this.columnFailCount.put(columnInfoVO.getColumnName(), Objects.isNull(count) ? 1 : Integer.valueOf(count + 1));
         this.encryptionlogger.error("decrypt error", e);
         throw new CustomException(ErrCode.DATA_PROCESS_ERROR);
      }
   }

   private void updateFlag(ProcessStatus processStatus) {
      String flag = this.getFlag(processStatus);
      this.updateFlag(flag);
   }

   private String getFlag(ProcessStatus processStatus) {
      String flag;
      switch (processStatus) {
         case RUNNING:
            flag = "0".equals(this.progressFlag) ? "-1" : "-2";
            break;
         case COMPLETE:
            flag = "0".equals(this.progressFlag) ? "5" : "7";
            break;
         case FAIL:
            flag = "0".equals(this.progressFlag) ? "4" : "44";
            break;
         default:
            throw new CustomException(ErrCode.UNSUPPORTED_TYPE);
      }

      return flag;
   }

   private void updateFlag(String flag) {
      TCoreEncryptionService tCoreEncryptionService = (TCoreEncryptionService)ApplicationContextProvider.getBean(TCoreEncryptionContext.class);

      for(ColumnInfoVO columnInfoVO : this.columnInfoList) {
         tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, columnInfoVO.getColumnName(), flag);
      }

   }

   private void updateFlag(String columnName, ProcessStatus processStatus) {
      String flag = this.getFlag(processStatus);
      TCoreEncryptionService tCoreEncryptionService = (TCoreEncryptionService)ApplicationContextProvider.getBean(TCoreEncryptionContext.class);
      tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, columnName, flag);
   }

   private void printLog() {
      if ("0".equals(this.progressFlag)) {
         this.encryptionlogger.info("加密完成");
         this.encryptionlogger.info("加密完成总行数：{}，失败总行数：{}", this.finishCount, this.failCount);
      } else {
         this.encryptionlogger.info("解密完成");
         this.encryptionlogger.info("解密完成总行数：{}，失败总行数：{}", this.finishCount, this.failCount);
      }

   }
}
