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.service.core.strategy.MaksingAppDataRuleService;
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.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.jdbc.core.JdbcTemplate;

public class MongoDBRemoveLoadingBatchTask implements Runnable {
   private String copySuffix = "";
   private String cipherSuffix = "";
   private String mongodbpk = "";
   protected static final String COMPLETE = "7";
   protected static final String RUNNING = "-2";
   protected static final String FAIL = "44";
   private int jiamiTasktotalCounts = 0;
   private int jiamiTaskFinishCounts = 0;
   private int failCount = 0;
   private boolean isKeepReadData = true;
   private int startRow = 0;
   private int maxPackageCount = 100000;
   private String dataSystemId;
   private String projectId;
   private JdbcTemplate jdbcTemplate;
   private String tableName;
   private String columnName;
   private String columnType;
   private String columnSize;
   private MongoClient connect;
   private TCoreDatasystem tCoreDatasystem;
   private String schema;
   private String[] primaryKeys;
   private List<ColumnInfoVO> columnInfoList;
   private List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumnList;
   List<ColumnPrimaryKeyInfo> columnPrimaryKeyInfoList;
   HashSet<ColumnEncryption> columnSet = null;
   Properties props = new Properties();
   CountDownLatch countDownLatch;
   EncTaskStateContainer container;

   public MongoDBRemoveLoadingBatchTask(String projectId, String dataSystemId, TCoreDatasystem tCoreDatasystem, String schema, String tableName, String[] primaryKeys, List<ColumnInfoVO> columnInfoList, CountDownLatch countDownLatch, List<ColumnPrimaryKeyInfo> columnPrimaryKeyInfoList, List<com.chenyang.nse.bussiness.entity.vo.maskingtask.ColumnInfoVO> allColumnList) {
      this.copySuffix = PropertyGhcaUtil.copySuffix;
      this.cipherSuffix = PropertyGhcaUtil.cipherSuffix;
      this.mongodbpk = PropertyGhcaUtil.mongodbpk;
      this.projectId = projectId;
      this.dataSystemId = dataSystemId;
      this.tCoreDatasystem = tCoreDatasystem;
      this.schema = schema;
      this.tableName = tableName;
      this.columnInfoList = columnInfoList;
      this.primaryKeys = primaryKeys;

      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.columnPrimaryKeyInfoList = columnPrimaryKeyInfoList;
      this.countDownLatch = countDownLatch;
      this.allColumnList = allColumnList;

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

      this.jdbcTemplate = (JdbcTemplate)ApplicationContextProvider.getBean(JdbcTemplate.class);
      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 void runTask() {
      this.countDownLatch.countDown();
      new ArrayList();
      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");
      new Properties();

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

      encryptionlogger.info("执行批量解密任务开始start>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
      encryptionlogger.info("执行批量解密任务开始start↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓");
      encryptionlogger.info("启动执行线程！！");
      TCoreJiaMiJieMiJinDuDao tCoreJiaMiJieMiJinDuDao = (TCoreJiaMiJieMiJinDuDao)ApplicationContextProvider.getBean(TCoreJiaMiJieMiJinDuDao.class);
      TCoreEncryptionService tCoreEncryptionService = (TCoreEncryptionService)ApplicationContextProvider.getBean(TCoreEncryptionContext.class);
      MaksingAppDataRuleService maksingAppDataRuleService = (MaksingAppDataRuleService)ApplicationContextProvider.getBean(MaksingAppDataRuleService.class);
      Boolean doresult = true;
      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();
      }

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

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

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

      for(ColumnInfoVO columnInfo : this.columnInfoList) {
         this.columnName = columnInfo.getColumnName();
         boolean encFlag = false;
         tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, columnInfo.getColumnName(), "-2");

         try {
            this.jiamiTaskFinishCounts = 0;
            MongoDatabase db = this.connect.getDatabase(this.schema);
            MongoCollection<Document> collection = db.getCollection(this.tableName);
            Bson sort = Filters.eq(this.mongodbpk, 1);
            int count = 0;
            int max = 100000;

            while(true) {
               FindIterable<Document> find = collection.find().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.doDecryptionMethod(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, "44");
            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, "1");
            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);
            tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJie);
            this.jiamiTaskFinishCounts = 0;
            continue;
         }

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

            String check = this.checkDecryption(this.connect, columnInfo, encryptionlogger);
            if (!"ok".equals(check)) {
               encryptionlogger.error("字段" + this.columnName + "解密校验失败");
               this.updateFieldFlag(columnInfo.getTcoreencryptionfieldId(), "44");
            } else {
               encryptionlogger.info("字段" + this.columnName + "解密校验成功");
               tCoreEncryptionService.updateFlag(this.projectId, this.dataSystemId, this.schema, this.tableName, this.columnName, "7");
               if (null != tCoreJiaMiJieMiJin && doresult) {
                  tCoreJiaMiJieMiJin.setFinishcounts(this.jiamiTaskFinishCounts);
                  tCoreJiaMiJieMiJin.setState("1");
                  tCoreJiaMiJieMiJin.setEndtime(new Date());
                  tCoreJiaMiJieMiJinDuDao.update(tCoreJiaMiJieMiJin);
               }

               encryptionlogger.info("字段" + this.columnName + "解密完成");
            }
         }
      }

   }

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

      return value;
   }

   private String checkDecryption(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, 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 + this.copySuffix, 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 copy = MongodbTool.getDocumentValue(columnName + this.copySuffix, document);
               String secretKeyId = columnInfoVO.getSecretKeyId();
               Map<String, String> encryptionInfo = tCoreEncryptionService.queryEncryptionInfo(secretKeyId);
               if (copy.contains(this.props.getProperty("encryption.label"))) {
                  if (null != encryptionInfo.get("encryptionType") && !"".equals(encryptionInfo.get("encryptionType"))) {
                     Boolean isEq = EncUtils.compareStringEq(value, EncProvider.decryptEcb((String)encryptionInfo.get("encryptionType"), (String)encryptionInfo.get("secretkey"), copy, 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("解密后的数据加密后与元数据不一致，准备回滚。");
                        return mg111.toString();
                     }
                  }
               } else if (null != encryptionInfo.get("encryptionType") && !"".equals(encryptionInfo.get("encryptionType"))) {
                  Boolean isEq = EncUtils.compareStringEq(value, copy);
                  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";
   }

   private int updateFieldFlag(String id, String flag) {
      int num = this.jdbcTemplate.update("UPDATE t_core_encryption_field SET flag = ? WHERE id = ?", new Object[]{flag, id});
      return num;
   }
}
