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

import ch.qos.logback.classic.Logger;
import com.chenyang.nse.bussiness.bean.Column;
import com.chenyang.nse.bussiness.config.PropertiesLoaderUtils;
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.service.core.encryption.TCoreEncryptionService;
import com.chenyang.nse.bussiness.service.core.encryption.impl.TCoreEncryptionContext;
import com.chenyang.nse.bussiness.tools.encryption.EncProvider;
import com.chenyang.nse.bussiness.tools.encryption.EncUtils;
import com.chenyang.nse.bussiness.tools.jdbc.MongodbTool;
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.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import dm.jdbc.util.StringUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.CollectionUtils;

public class MongoDBLoadingTask implements Runnable {
   private String copySuffix = "";
   private String encryptionlabel = "";
   private String encryptionlikeopen = "";
   private String mongodbpk = "";
   protected static final String COMPLETE = "5";
   protected static final String RUNNING = "-1";
   protected static final String FAIL = "4";
   private int jiamiTasktotalCounts = 0;
   private int jiamiTaskFinishCounts = 0;
   private int failCount = 0;
   private String dataSystemId;
   private String projectId;
   private String tableName;
   private String columnName;
   private String columnType;
   private String columnSize;
   private TCoreDatasystem tCoreDatasystem;
   private String schema;
   private String[] primaryKeys;
   private List<ColumnInfoVO> columnInfoList;
   private List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumn;
   private List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumnList;
   List<ColumnPrimaryKeyInfo> columnPrimaryKeyInfoList;
   HashSet<ColumnEncryption> columnSet = null;
   Properties props = new Properties();
   private JdbcTemplate jdbcTemplate;
   private MongoClient connect;
   private boolean isKeepReadData = true;
   private int startRow = 0;
   private int maxPackageCount = 100000;
   private boolean isReload;
   EncTaskStateContainer container;
   private static String COLUMN_ALL_ENCRYPTED = "该列不存在明文数据，无需处理";
   private static String CHECK_COLUMN = "checkColumn";

   public MongoDBLoadingTask(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) {
      this.copySuffix = PropertyGhcaUtil.copySuffix;
      this.encryptionlikeopen = PropertyGhcaUtil.encryptionlikeopen;
      this.encryptionlabel = PropertyGhcaUtil.encryptionlabel;
      this.mongodbpk = PropertyGhcaUtil.mongodbpk;
      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;

      try {
         this.connect = MongodbTool.connect(tCoreDatasystem.getDbservername(), tCoreDatasystem.getUsername(), AesTool.decrypt(tCoreDatasystem.getPassword(), "ghca"), tCoreDatasystem.getDbip(), Integer.parseInt(tCoreDatasystem.getDbport()), true);
      } catch (Exception e) {
         e.printStackTrace();
      }

      this.jdbcTemplate = (JdbcTemplate)ApplicationContextProvider.getBean(JdbcTemplate.class);

      try {
         this.props = PropertiesLoaderUtils.loadAllProperties("config.properties");
      } catch (IOException e) {
         e.printStackTrace();
      }

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

   public void run() {
      try {
         this.runTask();
      } finally {
         EncTaskTracker.instance().over(this.container.getTask());
      }

   }

   public Map<String, Object> runTask() {
      String loggerPath = System.getProperty("user.dir") + File.separator + "encryprocesslog" + File.separator + this.projectId + File.separator + this.dataSystemId;
      Logger encryptionlogger = (new LoggerBuilder()).getLogger(loggerPath, this.schema + "." + this.tableName);
      Thread.currentThread().setName("pool-" + this.tableName + "-thread");
      encryptionlogger.info("启动执行线程！！");
      TCoreJiaMiJieMiJinDuDao tCoreJiaMiJieMiJinDuDao = (TCoreJiaMiJieMiJinDuDao)ApplicationContextProvider.getBean(TCoreJiaMiJieMiJinDuDao.class);
      TCoreEncryptionService tCoreEncryptionService = (TCoreEncryptionService)ApplicationContextProvider.getBean(TCoreEncryptionContext.class);
      HashMap<String, Object> resultMap = new HashMap();
      boolean encFlag = false;
      Boolean doresult = true;
      if (this.isReload) {
         resultMap = this.checkColumnAllEncrypted(this.connect, resultMap, ((ColumnInfoVO)this.columnInfoList.get(0)).getColumnName());
         if (!CollectionUtils.isEmpty(resultMap)) {
            return resultMap;
         }
      }

      try {
         for(ColumnInfoVO columnInfoVO : this.columnInfoList) {
            tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, columnInfoVO.getColumnName(), "-1");
            EncryptProcessUtil.backUpOneCopyColumnMongoDB(this.connect, this.schema, this.tableName, columnInfoVO, this.copySuffix);
         }
      } catch (Exception throwables) {
         encryptionlogger.error("备份原列数据失败：" + throwables.getMessage());

         for(int i = 0; i < this.columnInfoList.size(); ++i) {
            String columnName = ((ColumnInfoVO)this.columnInfoList.get(i)).getColumnName();
            tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, columnName, "4");
         }

         TCoreJiaMiJieMiJinDu tCoreJiaMiJieMiJinDu = new TCoreJiaMiJieMiJinDu();
         tCoreJiaMiJieMiJinDu.setProjectid(this.projectId);
         tCoreJiaMiJieMiJinDu.setDatasystemid(this.dataSystemId);
         tCoreJiaMiJieMiJinDu.setSchemahh(this.schema);
         tCoreJiaMiJieMiJinDu.setTablename(this.tableName);
         tCoreJiaMiJieMiJinDu.setFlag("4");
         tCoreJiaMiJieMiJinDuDao.save(tCoreJiaMiJieMiJinDu);
         return resultMap;
      }

      TCoreJiaMiJieMiJinDu tCoreJiaMiJieMiJin1 = new TCoreJiaMiJieMiJinDu();
      List<TCoreJiaMiJieMiJinDu> tCoreJiaMiJieMiJinList1 = tCoreJiaMiJieMiJinDuDao.queryTCoreJiaMiJieMiJinDu(this.dataSystemId, this.projectId, this.schema, this.tableName, "1");
      int cpp1 = tCoreJiaMiJieMiJinList1.size();
      if (cpp1 > 0) {
         for(int npp1 = 0; npp1 < cpp1; ++npp1) {
            tCoreJiaMiJieMiJin1 = (TCoreJiaMiJieMiJinDu)tCoreJiaMiJieMiJinList1.get(npp1);
         }
      }

      if (null != tCoreJiaMiJieMiJin1) {
         tCoreJiaMiJieMiJinDuDao.remove(tCoreJiaMiJieMiJin1);
      }

      List<TCoreJiaMiJieMiJinDu> tCoreJiaList = tCoreJiaMiJieMiJinDuDao.queryTCoreJiaMiJieMiJinDu(this.dataSystemId, this.projectId, this.schema, this.tableName, "0");
      int cpppppp = tCoreJiaList.size();
      if (cpppppp == 0) {
         TCoreJiaMiJieMiJinDu tCoreJiaMiJieMiJinDu = new TCoreJiaMiJieMiJinDu();
         tCoreJiaMiJieMiJinDu.setProjectid(this.projectId);
         tCoreJiaMiJieMiJinDu.setDatasystemid(this.dataSystemId);
         tCoreJiaMiJieMiJinDu.setSchemahh(this.schema);
         tCoreJiaMiJieMiJinDu.setTablename(this.tableName);
         tCoreJiaMiJieMiJinDu.setFlag("0");
         tCoreJiaMiJieMiJinDuDao.save(tCoreJiaMiJieMiJinDu);
      }

      MongoDatabase db1 = this.connect.getDatabase(this.schema);
      MongoCollection<Document> collection1 = db1.getCollection(this.tableName);
      FindIterable<Document> findCount = collection1.find();
      MongoCursor<Document> cursorCount = findCount.iterator();

      while(cursorCount.hasNext()) {
         ++this.jiamiTasktotalCounts;
         cursorCount.next();
      }

      TCoreJiaMiJieMiJinDu tCoreJiaMiJieMiJin = new TCoreJiaMiJieMiJinDu();
      List<TCoreJiaMiJieMiJinDu> tCoreJiaMiJieMiJinList = tCoreJiaMiJieMiJinDuDao.queryTCoreJiaMiJieMiJinDu(this.dataSystemId, this.projectId, this.schema, this.tableName, "0");
      int cpp = tCoreJiaMiJieMiJinList.size();
      if (cpp > 0) {
         for(int npp = 0; npp < cpp; ++npp) {
            tCoreJiaMiJieMiJin = (TCoreJiaMiJieMiJinDu)tCoreJiaMiJieMiJinList.get(npp);
         }
      }

      if (null != tCoreJiaMiJieMiJin) {
         tCoreJiaMiJieMiJin.setTotalcounts(this.jiamiTasktotalCounts);
         tCoreJiaMiJieMiJin.setStarttime(new Date());
         tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJieMiJin);
      }

      for(ColumnInfoVO columnInfo : this.columnInfoList) {
         try {
            this.jiamiTaskFinishCounts = 0;
            MongoDatabase db = this.connect.getDatabase(this.schema);
            MongoCollection<Document> collection = db.getCollection(this.tableName);
            String pattern = "^(?!.*" + this.encryptionlabel + ").*$";
            Bson regex = Filters.regex(columnInfo.getColumnName(), pattern);
            Bson sort = Filters.eq(this.mongodbpk, 1);
            int count = 0;
            int max = 100000;

            while(true) {
               FindIterable<Document> find = collection.find(regex).sort(sort).skip(count).limit(max);
               MongoCursor<Document> cursor = find.iterator();
               List<Document> results = new ArrayList();

               while(cursor.hasNext()) {
                  results.add(cursor.next());
               }

               if (results.size() == 0) {
                  break;
               }

               for(Document document : results) {
                  Object pk = document.get(this.mongodbpk);
                  String value = MongodbTool.getDocumentValue(columnInfo.getColumnName(), document);
                  Bson eq = Filters.eq(this.mongodbpk, pk);
                  String encryptionType = columnInfo.getEncryptionType();
                  String secretKey = columnInfo.getSecretKey();
                  String isLike = columnInfo.getIsLike();
                  String keepfirst = columnInfo.getKeepfirst();
                  String encdigit = columnInfo.getEncdigit();
                  String twoindex = columnInfo.getTwoindex();
                  String newValue = this.doEncryptionMethod(encryptionType, secretKey, value, isLike, keepfirst, encdigit, twoindex);
                  collection.updateMany(eq, new Document("$set", new Document(columnInfo.getColumnName(), newValue)));
                  ++this.jiamiTaskFinishCounts;
               }

               count += max;
            }
         } catch (Exception t) {
            encFlag = true;
            encryptionlogger.error(t.getMessage());
            encryptionlogger.warn("提交到" + this.jiamiTaskFinishCounts + "行，执行异常!");
            encryptionlogger.warn("{}行加密数据提交失败!", this.jiamiTaskFinishCounts);
            tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, this.columnName, "4");
            if (null != tCoreJiaMiJieMiJin) {
               tCoreJiaMiJieMiJin.setExceptionstring(t.toString());
               tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJieMiJin);
            }

            doresult = false;
            TCoreJiaMiJieMiJinDu tCoreJiaMiJie = new TCoreJiaMiJieMiJinDu();
            List<TCoreJiaMiJieMiJinDu> tCoreJiaMiJieList = tCoreJiaMiJieMiJinDuDao.queryTCoreJiaMiJieMiJinDu(this.dataSystemId, this.projectId, this.schema, this.tableName, "0");
            int cpplll = tCoreJiaMiJieList.size();
            if (cpplll > 0) {
               for(int npplll = 0; npplll < cpplll; ++npplll) {
                  tCoreJiaMiJie = (TCoreJiaMiJieMiJinDu)tCoreJiaMiJieList.get(npplll);
               }
            }

            tCoreJiaMiJie.setState("-1");
            tCoreJiaMiJieMiJin.setFinishcounts(this.jiamiTaskFinishCounts);
            this.failCount += this.jiamiTaskFinishCounts;
            tCoreJiaMiJieMiJin.setFailcounts(this.failCount);
            tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJie);
            this.jiamiTaskFinishCounts = 0;
         }
      }

      if (!encFlag) {
         encryptionlogger.info("加密完成！");
         encryptionlogger.info("加密完成总行数：" + this.jiamiTaskFinishCounts);
         if (null != tCoreJiaMiJieMiJin && doresult) {
            tCoreJiaMiJieMiJin.setFinishcounts(this.jiamiTaskFinishCounts);
            tCoreJiaMiJieMiJin.setState("1");
            tCoreJiaMiJieMiJin.setEndtime(new Date());
            tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJieMiJin);
         }

         for(ColumnInfoVO columnInfoVO : this.columnInfoList) {
            String check = this.checkEncryption(this.connect, columnInfoVO, encryptionlogger);
            if (!"ok".equals(check)) {
               encryptionlogger.error("正常信息：字段" + this.columnName + "加密校验失败");
               tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, this.columnName, "4");
            } else {
               encryptionlogger.info("正常信息：字段" + this.columnName + "加密校验成功");
               String columnName = columnInfoVO.getColumnName();
               tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, columnName, "5");
            }
         }
      }

      return resultMap;
   }

   private HashMap<String, Object> checkColumnAllEncrypted(MongoClient connect, HashMap<String, Object> resultMap, String columnName) {
      MongoDatabase db = connect.getDatabase(this.schema);
      MongoCollection<Document> collection = db.getCollection(this.tableName);
      String pattern = "^(?!.*" + this.encryptionlabel + ").*$";
      Bson regex = Filters.regex(columnName, pattern);
      FindIterable<Document> find = collection.find(regex);
      MongoCursor<Document> cursor = find.iterator();
      List<Document> results = new ArrayList();

      while(cursor.hasNext()) {
         results.add(cursor.next());
      }

      if (results.size() == 0) {
         resultMap.put(CHECK_COLUMN, COLUMN_ALL_ENCRYPTED);
      }

      return resultMap;
   }

   private String doEncryptionMethod(String encryptionType, String secretKey, String value, String isLike, String keepfirst, String encdigit, String twoindex) throws Exception {
      if (null != encryptionType && !"".equals(encryptionType)) {
         value = EncProvider.encryptEcb(encryptionType, secretKey, value, isLike, keepfirst, encdigit, twoindex);
      }

      return value;
   }

   private String checkEncryption(MongoClient connect, ColumnInfoVO columnInfoVO, Logger encryptionlogger) {
      try {
         encryptionlogger.info("正常信息：开始校验");
         List<Column> columnList = MongodbTool.findAllColumns(connect, this.schema, this.tableName);
         List<String> stringList = (List)columnList.stream().map(Column::getColumnName).collect(Collectors.toList());
         if (!stringList.contains(columnInfoVO + this.copySuffix)) {
            return "ok";
         }

         int checkNum = Integer.valueOf(this.props.getProperty("checkNum"));
         TCoreEncryptionService tCoreEncryptionService = (TCoreEncryptionService)ApplicationContextProvider.getBean(TCoreEncryptionContext.class);
         encryptionlogger.info("正常信息：校验连接创建成功");
         String columnName = columnInfoVO.getColumnName();
         MongoDatabase db = connect.getDatabase(this.schema);
         MongoCollection<Document> collection = db.getCollection(this.tableName);
         Bson nq = Filters.ne(columnName + this.copySuffix, null);
         FindIterable<Document> find = collection.find(nq).limit(checkNum);
         MongoCursor<Document> cursor = find.iterator();
         List<Document> results = new ArrayList();

         while(cursor.hasNext()) {
            results.add(cursor.next());
         }

         for(Document document : results) {
            String copyValue = MongodbTool.getDocumentValue(columnName, document);
            if (null == copyValue) {
               StringBuilder mg = new StringBuilder();
               mg.append("数据源：");
               mg.append(this.schema);
               mg.append("表：");
               mg.append(this.tableName);
               mg.append("列：");
               mg.append(columnName);
               mg.append("加密后的数据为空，加密失败，准备回滚。");
               encryptionlogger.info("异常信息：加密非空校验：加密后的数据为空，加密失败，准备回滚。");
               return mg.toString();
            }
         }

         encryptionlogger.info("正常信息：加密非空校验通过");
         encryptionlogger.info("正常信息：解密校验开始");
         Bson nq1 = Filters.ne(columnName, null);
         FindIterable<Document> find1 = collection.find(nq1).limit(checkNum);
         MongoCursor<Document> cursor1 = find1.iterator();
         List<Document> results1 = new ArrayList();

         while(cursor1.hasNext()) {
            results1.add(cursor1.next());
         }

         for(Document document : results1) {
            String value = MongodbTool.getDocumentValue(columnName, document);
            if (StringUtil.isNotEmpty(value)) {
               String copyValue = MongodbTool.getDocumentValue(columnName + this.copySuffix, document);
               String secretKeyId = columnInfoVO.getSecretKeyId();
               Map<String, String> encryptionInfo = tCoreEncryptionService.queryEncryptionInfo(secretKeyId);
               if (null != encryptionInfo.get("encryptionType") && !"".equals(encryptionInfo.get("encryptionType"))) {
                  if (!value.contains(this.props.getProperty("encryption.label"))) {
                     StringBuilder mg111 = new StringBuilder();
                     mg111.append("数据源：");
                     mg111.append(this.schema);
                     mg111.append("表：");
                     mg111.append(this.tableName);
                     mg111.append("列：");
                     mg111.append(columnName);
                     mg111.append("加密后的数据解密后与元数据不一致，准备回滚。");
                     encryptionlogger.info("异常信息：加密后的数据解密后与元数据不一致，准备回滚。");
                     return mg111.toString();
                  }

                  Boolean isEq = EncUtils.compareStringEq(copyValue, EncProvider.decryptEcb((String)encryptionInfo.get("encryptionType"), (String)encryptionInfo.get("secretkey"), value, columnInfoVO.getIsLike(), columnInfoVO.getKeepfirst(), columnInfoVO.getEncdigit(), columnInfoVO.getTwoindex()));
                  if (!isEq) {
                     StringBuilder mg111 = new StringBuilder();
                     mg111.append("数据源：");
                     mg111.append(this.schema);
                     mg111.append("表：");
                     mg111.append(this.tableName);
                     mg111.append("列：");
                     mg111.append(columnName);
                     mg111.append("加密后的数据解密后与元数据不一致，准备回滚。");
                     encryptionlogger.info("异常信息：加密后的数据解密后与元数据不一致，准备回滚。");
                     return mg111.toString();
                  }
               }
            }
         }

         encryptionlogger.info("正常信息：解密校验通过");
      } catch (Exception e) {
         e.printStackTrace();
         encryptionlogger.info("结束校验");
         return "e1";
      }

      encryptionlogger.info("正常信息：校验结束");
      return "ok";
   }
}
