package com.engine.htsc.payment.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.EnumUtil;
import com.alibaba.fastjson.JSONObject;
import com.cloudstore.api.util.Util_ObjCopy;
import com.engine.core.impl.Service;
import com.engine.htsc.meeting.util.Common;
import com.engine.htsc.payment.enums.DispatchType;
import com.engine.htsc.payment.enums.Pay;
import com.engine.htsc.payment.log.PaymentLogger;
import com.engine.htsc.payment.log.PaymentLoggerFactory;
import com.engine.htsc.payment.model.SendAppendicesTask;
import com.engine.htsc.payment.model.SendPDFTask;
import com.engine.htsc.payment.model.WfParams;
import com.engine.htsc.payment.service.PaymentSyncService;
import com.engine.htsc.payment.util.AssertUtil;
import com.engine.htsc.payment.util.CommonAPI;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.engine.htsc.payment.enums.Pay.*;

public class PaymentSyncServiceImpl extends Service implements PaymentSyncService {

  private static final PaymentLogger logger = PaymentLoggerFactory.getLogger(PaymentSyncServiceImpl.class);

  @Override
  public void syncProcessPdfAndAllAppendix() throws InterruptedException, IOException, ClassNotFoundException {
//    Jedis jedis = RedisTools.GetJedis();
//    String rediskey = "syncProcessPdfAndAllAppendix";
//    int expireTime = Util.getIntValue(Util.null2String(new PropBean().getPropName("syncRedisExpiretime")));
//    boolean redisFlag = RedisTools.TryGetDistributedLock(jedis, rediskey, "lock", expireTime * 60000);

    //分支办结模板
    List<Map<String, String>> branchAllPrintMode = allPrintMode();
    logger.info(String.format("办结模板 branch-AllPrintMode: %s", JSONObject.toJSONString(branchAllPrintMode)));

    List<WfParams> wfParamsList = getAllWfParams();
    //深拷贝
    List<WfParams> toSyncProcessPdfBranchWfParams = Util_ObjCopy.deepCopy(wfParamsList);
    // 同步审批单,剥离setRequestInfo的逻辑,联查调度表
    List<Map<String, String>> table_requestMarks_pdf = getDispatch(DispatchType.PROCESS_PDF);
    List<WfParams> syncProcessPdf = toSyncProcessPdfBranchWfParams.stream().peek(wfParams -> {
      List<Map<String, String>> maps = buildRequestInfo(String.valueOf(wfParams.getWorkflowid()), table_requestMarks_pdf);
      wfParams.setRequestInfos(maps);
    }).collect(Collectors.toList());

    List<WfParams> toSyncAllAppendixBranchWfParams = Util_ObjCopy.deepCopy(wfParamsList);
    // 同步附件,剥离setAppendices的逻辑,联查调度表
    List<Map<String, String>> table_requestMarks_affix = getDispatch(DispatchType.PROCESS_AFFIX);
    List<WfParams> syncAppendices = toSyncAllAppendixBranchWfParams.stream().peek(wfParams -> {
      List<Map<String, String>> maps = buildAppendices(wfParams.getTableName(), table_requestMarks_affix);
      wfParams.setAppendices(maps);
    }).collect(Collectors.toList());


    List<SendPDFTask> taskSyncProcessPdf = syncProcessPdf(syncProcessPdf);
    logger.info(String.format("同步pdf数据信息 all taskSyncProcessPdf.size:%d", taskSyncProcessPdf.size()));
    logger.info(String.format("同步数据信息 Runtime.getRuntime().availableProcessors():%d",
        Runtime.getRuntime().availableProcessors()));
    ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors()); //可用核心数
    taskSyncProcessPdf.parallelStream().forEach(task -> {
      try {
        pool.submit(task).get();
      } catch (InterruptedException | ExecutionException e) {
        logger.error(String.format("分支同步数据信息 submit task error, \r\n task:%s",
            task.toString()), e);
      }
    });
    logger.info("同步数据信息 success, poll ready shutdown");

    List<SendAppendicesTask> taskSyncAppendices = syncAppendices(syncAppendices);
    logger.info(String.format("同步appendices数据信息 all taskSyncAppendices.size:%d", taskSyncAppendices.size()));
    logger.info(String.format("同步数据信息 Runtime.getRuntime().availableProcessors():%d",
        Runtime.getRuntime().availableProcessors()));
    taskSyncAppendices.parallelStream().forEach(task -> {
      try {
        pool.submit(task).get();
      } catch (InterruptedException | ExecutionException e) {
        logger.error(String.format("同步数据信息 submit task error, \r\n task:%s",
            task.toString()), e);
      }
    });
    logger.info("同步数据信息 success, poll ready shutdown");
    pool.shutdown();
    while (!pool.awaitTermination(2, TimeUnit.SECONDS)) {
      logger.info("同步数据信息 success, poll shutdown...");
    }

    FileUtil.clean(Common.OLD_SYNC_TEMP);
  }

  /**
   * 同步审批件
   *
   * @return
   * @throws InterruptedException
   */
  @Override
  public List<SendPDFTask> syncProcessPdf(List<WfParams> wfParams) throws InterruptedException {
    //去空
    wfParams = wfParams.stream()
        .filter(wfParam -> CollectionUtils.isNotEmpty(wfParam.getRequestInfos()))
        .collect(Collectors.toList());
    return wfParams.stream().map(wfParam -> {
      try {
        return new SendPDFTask(0, wfParam.getRequestInfos().size(), wfParam);
      } catch (Exception e) {
        logger.error(String.format("同步数据信息 build SendPDFTask error, " +
            "\r\n wfParams:%s", wfParam.toString()), e);
        return null;
      }
    }).filter(not(Objects::isNull)).collect(Collectors.toList());

  }

  @Override
  public List<SendAppendicesTask> syncAppendices(List<WfParams> wfParams) {
    //去空
    wfParams = wfParams.stream()
        .filter(wfParam -> CollectionUtils.isNotEmpty(wfParam.getAppendices()))
        .collect(Collectors.toList());
    return wfParams.stream().map(wfParam -> {
      try {
        return new SendAppendicesTask(0, wfParam.getAppendices().size(), wfParam);
      } catch (Exception e) {
        logger.error(String.format("同步数据信息 build SendPDFTask error, " +
            "\r\n wfParams:%s", wfParam.toString()), e);
        return null;
      }
    }).filter(not(Objects::isNull)).collect(Collectors.toList());
  }

  /**
   * 构造审批单,workflowid维度
   *
   * @param workflowid
   * @return
   */
  public List<Map<String, String>> buildRequestInfo(String workflowid, List<Map<String, String>> table_requestMarks) {
    if (AssertUtil.isEmpty(workflowid)) {
      return null;
    }
    // 调度表数据
    List<String> needSync_requestMarks = new ArrayList<>();
    table_requestMarks.forEach(e -> {
      String lcbh = e.get("lcbh");
      if (StringUtils.isNotEmpty(lcbh)) {
        needSync_requestMarks.add(lcbh);
      }
    });
    logger.info(String.format("审批件 needSync_requestMarks.size:%s", needSync_requestMarks.size()));

    List<Map<String, String>> all = CommonAPI
        .queryWithPlaceholder("workflow_requestbase",
            "requestid,requestname,requestmark,creater",
            "workflowid",
            workflowid);

    //从所有的数据中过滤出调度表数据
    return all.stream()
        .distinct()
        .filter(e -> needSync_requestMarks.contains(e.get("requestmark")))
        .collect(Collectors.toList());
  }

  /**
   * 构造附件
   *
   * @param tableName
   * @return
   */
  public List<Map<String, String>> buildAppendices(String tableName, List<Map<String, String>> table_requestMarks) {
    try {
      if (AssertUtil.isEmpty(tableName)) {
        return null;
      }
      //调度表
      String sql = "select lcbh from uf_dsrwdb where rwlx = ?";
      logger.info(String.format("sql:%s ?:%s", sql, DispatchType.PROCESS_AFFIX.getType()));
      // 调度表数据
      List<String> needSync_requestMarks = new ArrayList<>();
      table_requestMarks.forEach(e -> {
        String lcbh = e.get("lcbh");
        if (StringUtils.isNotEmpty(lcbh)) {
          needSync_requestMarks.add(lcbh);
        }
      });
      logger.info(String.format("附件 needSync_requestMarks.size:%s", needSync_requestMarks.size()));

      //所有附件字段
      String billid = tableName.substring(tableName.lastIndexOf("_")).replace("_", "-");
      sql = "select wm_concat_old(FIELDNAME) field from workflow_billfield b where b.fieldhtmltype=6 and b.billid=?";
      String field = CommonAPI.querySingleFieldWithPlaceholder(sql, "field", billid);
      if (AssertUtil.isEmpty(field)) {
        return null;
      }
      List<Map<String, String>> fjResults = CommonAPI
          .query(tableName, "lcbh,ngr," + field, null);
      List<Map<String, String>> requestMarkWithFj = new ArrayList<>();
      //附件数据汇总
      fjResults.forEach(result -> {
        String lcbh = result.get("lcbh");
        //调度表包含
        if (needSync_requestMarks.contains(lcbh)) {
          String ngr = result.get("ngr");
          result.remove("lcbh");
          result.remove("ngr");
          String fjs = result.values().stream()
              .filter(AssertUtil::isNotEmpty)
              .collect(Collectors.joining(","));
          Map<String, String> res = new HashMap<>();
          res.put("requestmark", lcbh);
          res.put("creater", ngr);
          res.put("appendices", fjs);
          //附件不为空
          if (!"".equals(fjs)) {
            requestMarkWithFj.add(res);
          }
        }
      });
      return requestMarkWithFj;
    } catch (Exception e) {
      logger.error(String.format("获取 tablename:%s 流程附件", tableName));
      return null;
    }

  }


  // ==== ignore below ====
  public String getPrintModeByEndUnNumber(String endUnNumber) {
    String sql = "    select t2.unnumber,t1.modeid from workflow_printset t1\n" +
        "    left join workflow_nodebase t2\n" +
        "    on t1.nodeid = t2.id\n" +
        "    where type =1\n" +
        "    and printenable =1\n" +
        "    and t2.nodename = '办结'\n" +
        "    and t1.isactive = 1\n" +
        "    and t1.orderid = 0\n" +
        "    and t2.unnumber = ?";
    return CommonAPI.querySingleFieldWithPlaceholder(sql, "MODEID", endUnNumber);
  }

  public List<Map<String, String>> allPrintMode() {
    List branchEndNode = EnumUtil.getFieldValues(Pay.Branch.class, "endUnNumber");
    List headEndNode = EnumUtil.getFieldValues(Pay.Head.class, "endUnNumber");
    List subEndNode = EnumUtil.getFieldValues(Pay.Sub.class, "endUnNumber");
    branchEndNode.addAll(headEndNode);
    branchEndNode.addAll(subEndNode);
    String sql = "    SELECT t2.UNNUMBER,t1.MODEID FROM workflow_printset t1\n" +
        "    LEFT JOIN workflow_nodebase t2\n" +
        "    ON t1.nodeid = t2.id\n" +
        "    WHERE type =1\n" +
        "    AND printenable =1\n" +
        "    AND t2.NODENAME = '办结'\n" +
        "    AND t1.ISACTIVE = 1\n" +
        "    AND t1.orderid = 0\n" +
        "    AND t2.UNNUMBER in ('" + String.join("','", branchEndNode) + "')";
    return CommonAPI.query(sql);
  }


  public List<Map<String, String>> getDispatch(DispatchType type) {
    String sql = "select lcbh from uf_dsrwdb where rwlx = ?";
    logger.info(String.format("getDispatch sql:%s ?:%s", sql, type.getType()));
    return CommonAPI
        .queryWithPlaceholder("uf_dsrwdb", "lcbh", "rwlx",
            type.getType());
  }

  public List<WfParams> getAllWfParams() {
    List<WfParams> wfParams = new ArrayList<>();
    List<WfParams> branchWfParams = Arrays.stream(Branch.values()).map(e -> new WfParams
        .Builder(e.getWfUnNumber())
        .setModeid(getPrintModeByEndUnNumber(e.getEndUnNumber()))
        .build())
        .collect(Collectors.toList());
    logger.info(String.format("同步数据信息 branch-WfParams: %s", branchWfParams.toString()));
    List<WfParams> headWfParams = Arrays.stream(Head.values()).map(e -> new WfParams
        .Builder(e.getWfUnNumber())
        .setModeid(getPrintModeByEndUnNumber(e.getEndUnNumber()))
        .build())
        .collect(Collectors.toList());
    logger.info(String.format("同步数据信息 headWfParams: %s", headWfParams.toString()));
    List<WfParams> subWfParams = Arrays.stream(Sub.values()).map(e -> new WfParams
        .Builder(e.getWfUnNumber())
        .setModeid(getPrintModeByEndUnNumber(e.getEndUnNumber()))
        .build())
        .collect(Collectors.toList());
    logger.info(String.format("同步数据信息 subWfParams: %s", subWfParams.toString()));

    wfParams.addAll(branchWfParams);
    wfParams.addAll(headWfParams);
    wfParams.addAll(subWfParams);
    return wfParams;
  }

  private static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
  }


}
