package com.bonus.core.customer.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.ObjectUtil;
import java.time.LocalDate;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.constant.DelFlagEnum;
import com.bonus.core.account.v4.api.CardInfoServiceV4Api;
import com.bonus.core.account.vo.AccCardBaseInfoVO;
import com.bonus.core.customer.constants.CustConstant;
import com.bonus.core.customer.constants.PersonalSexEnum;
import com.bonus.core.customer.constants.PersonalStatusEnum;
import com.bonus.core.customer.dto.CustAccExcelModel;
import com.bonus.core.customer.dto.CustInfoModel;
import com.bonus.core.customer.model.CustErrorToLead;
import com.bonus.core.customer.model.CustInfo;
import com.bonus.core.customer.po.QueryOrgsBasicInfoPO;
import com.bonus.core.customer.service.CustInfoCheckService;
import com.bonus.core.customer.service.CustInfoService;
import com.bonus.core.customer.service.CustOrgService;
import com.bonus.core.customer.service.CustPsnTypeService;
import com.bonus.core.customer.vo.OrgBasicInfoVO;
import com.bonus.core.customer.vo.PsnTypeBacisInfoVO;
import com.bonus.i18n.I18n;
import com.bonus.utils.RetCodeEnum;
import com.bonus.core.common.encrypt.SM4EncryptUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

@Service
public class CustInfoCheckServiceImpl implements CustInfoCheckService {
   private static final Logger log = LoggerFactory.getLogger(CustInfoCheckServiceImpl.class);
   @Autowired
   @Lazy
   private CustInfoService custInfoService;

   @Autowired
   @Lazy
   private CustOrgService custOrgService;

   @Autowired
   @Lazy
   private CustPsnTypeService custPsnTypeService;

   @Autowired
   @Lazy
   private CardInfoServiceV4Api cardInfoServiceV4Api;

   public void checkCustInfoOnlyAndSetRedundantData(CustInfoModel infoModel) {
      this.checkCustNumUnique(infoModel.getCustNum(), infoModel.getCustId());
      this.checkCusMobile(infoModel.getMobile(), infoModel.getCustId());
      this.checkOrgAndSetNumAndFullName(infoModel.getOrgId(), infoModel);
      this.checkPsnTypeAndSetPsnTypeName(infoModel.getPsnType(), infoModel);
      this.checkIdCardAndSetBirthday(infoModel.getIdCard(), infoModel);
      this.checkBirthday(infoModel.getBirthday());
      this.checkSex(infoModel.getSex());
      this.checkDBBankCardNum(infoModel);
   }

   protected void checkDBBankCardNum(CustInfoModel infoModel) {
      if (!CharSequenceUtil.isBlank(infoModel.getBankCardNum())) {

         long count = this.custInfoService.count(Wrappers.<CustInfo>lambdaQuery()
                 .eq(CustInfo::getBankCardNum, infoModel.getBankCardNum())
                 .eq(CustInfo::getCustState, PersonalStatusEnum.NORMAL.getKey())
                 .ne(ObjectUtil.isNotNull(infoModel.getCustId()), CustInfo::getCustId, infoModel.getCustId()));
         if (count > 0L) {
            throw new ServiceException(I18n.getMessage("customer.bankCard.repetition", new Object[0]));
         }
      }
   }

   public void checkCustomerExcelImportData(List<CustErrorToLead> saveErrorDataList, List<CustAccExcelModel> importDataList) {
      log.info("导入总数量={}", importDataList.size());
      this.checkDuplicateCustNum(saveErrorDataList, importDataList);
      Map<String, Long> custNumCustId = this.custInfoService.getAllNormalCustNumValueMap();
      Predicate<CustAccExcelModel> existNumPredicate = (excelModel) -> {
         return custNumCustId.containsKey(excelModel.getCustNum());
      };
      this.checkAndRemoveErrorData(saveErrorDataList, importDataList, CustAccExcelModel::getCustNum, existNumPredicate, custNumCustId.keySet(), "existsNum");
      this.checkDuplicateSericalNum(saveErrorDataList, importDataList);
      Map<String, String> serialNum_custNum = this.getSerialNumAndCustNumMap();
      serialNum_custNum.remove((Object)null);
      Predicate<CustAccExcelModel> existSerialNumPredicate = (excelModel) -> {
         return serialNum_custNum.containsKey(excelModel.getSerialNum());
      };
      this.checkAndRemoveErrorData(saveErrorDataList, importDataList, CustAccExcelModel::getSerialNum, existSerialNumPredicate, serialNum_custNum.keySet(), "existsSerialNum");
      this.checkPsnTypeRange(saveErrorDataList, importDataList);
      log.info("通过校验数量={}", importDataList.size());
   }

   protected void checkDuplicateSericalNum(List<CustErrorToLead> saveErrorDataList, List<CustAccExcelModel> importDataList) {
      Map<String, Integer> duplicateSerialNum = getDuplicateElements(importDataList.stream().filter((model) -> {
         return model.getSerialNum() != null;
      }).map(CustAccExcelModel::getSerialNum));
      Predicate<CustAccExcelModel> duplicateSerialNumPredicate = (excelModel) -> {
         return duplicateSerialNum.containsKey(excelModel.getSerialNum());
      };
      this.checkAndRemoveErrorData(saveErrorDataList, importDataList, CustAccExcelModel::getSerialNum, duplicateSerialNumPredicate, duplicateSerialNum.keySet(), "duplicateSerialNum");
   }

   public void checkUpdateCustomerExcelImportData(List<CustErrorToLead> saveErrorDataList, List<CustAccExcelModel> importDataList) {
      log.info("导入总数量={}", importDataList.size());
      this.checkDuplicateCustNum(saveErrorDataList, importDataList);
      this.checkDuplicateSericalNum(saveErrorDataList, importDataList);
      this.checkPsnTypeRange(saveErrorDataList, importDataList);
      log.info("通过校验数量={}", importDataList.size());
   }

   protected Map<String, String> getSerialNumAndCustNumMap() {
      List<AccCardBaseInfoVO> accCardBaseInfoVOS = this.cardInfoServiceV4Api.listAccCardBaseInfo();
      return (Map)(CollUtil.isEmpty(accCardBaseInfoVOS) ? new HashMap() : (Map)accCardBaseInfoVOS.stream().collect(HashMap::new, (map, item) -> {
         map.put(item.getSerialNum(), item.getCustNum());
      }, HashMap::putAll));
   }

   protected void checkDuplicateCustNum(List<CustErrorToLead> saveErrorDataList, List<CustAccExcelModel> importDataList) {
      Map<String, Integer> duplicateCustNum = getDuplicateElements(importDataList.stream().filter((model) -> {
         return model.getCustNum() != null;
      }).map(CustAccExcelModel::getCustNum));
      Predicate<CustAccExcelModel> duplicateNumPredicate = (excelModel) -> {
         return duplicateCustNum.containsKey(excelModel.getCustNum());
      };
      this.checkAndRemoveErrorData(saveErrorDataList, importDataList, CustAccExcelModel::getCustNum, duplicateNumPredicate, duplicateCustNum.keySet(), "duplicateNum");
   }

   protected void checkPsnTypeRange(List<CustErrorToLead> saveErrorDataList, List<CustAccExcelModel> importDataList) {
      Iterator<CustAccExcelModel> iterator = importDataList.iterator();

      while(iterator.hasNext()) {
         CustAccExcelModel excelModel = (CustAccExcelModel)iterator.next();
         if (ObjectUtil.isNotNull(excelModel.getPsnType()) && CustPsnTypeServiceImpl.notInPsnTypeRange(excelModel.getPsnType())) {
            CustInfoServiceImpl.assembleErrInfo(saveErrorDataList, excelModel, I18n.getMessage("customer.psnType.id.outOfRange", new Object[0]));
            iterator.remove();
         }
      }

   }

   protected void checkAndRemoveErrorData(List<CustErrorToLead> saveErrorDataList, List<CustAccExcelModel> importDataList, Function<CustAccExcelModel, ?> getColumn, Predicate<? super CustAccExcelModel> removePredicate, Set<String> predicateData, String checkType) {
      log.info("开始校验{},数量={}", checkType, importDataList.size());
      if (CollUtil.isNotEmpty(predicateData)) {
         List<CustAccExcelModel> duplicateNumData = (List)importDataList.stream().filter(removePredicate).collect(Collectors.toList());
         if (CollUtil.isNotEmpty(duplicateNumData)) {
            duplicateNumData.forEach((excelModel) -> {
               String var10002 = I18n.getMessage(getErrorMsgBycheckType(checkType), new Object[0]);
               CustInfoServiceImpl.assembleErrInfo(saveErrorDataList, excelModel, var10002 + String.valueOf(getColumn.apply(excelModel)));
            });
            importDataList.removeIf(removePredicate);
            log.info("校验结束{},数量={}", checkType, importDataList.size());
         }
      }

   }



   protected static String getErrorMsgBycheckType(String checkType) {
      switch (checkType) {
         case "duplicateNum":
            return "customer.excel.num.duplicate";
         case "existsNum":
            return "customer.excel.num.exists";
         case "duplicateSerialNum":
            return "customer.excel.serialNum.duplicate";
         case "existsSerialNum":
            return "customer.excel.serialNum.exists";
         default:
            return "customer.common.error";
      }
   }

   protected void checkCustNumUnique(String custNum, Long custId) {
      log.info("[校验人员编号唯一]入参：custId={},custNum={}", custId, custNum);
      if (!CharSequenceUtil.isBlank(custNum)) {
         if (this.custInfoService.existsNormalCustNum(custNum, custId)) {
            RetCodeEnum var10002 = RetCodeEnum.CUSTINFO_ALREADY_EXISTS;
            String var10003 = I18n.getMessage("customer.num.repetition", new Object[0]);
            throw new ServiceException( var10002+":"+var10003 + custNum);
         }
      }
   }

   protected void checkCusMobile(String mobile, Long custId) {
      log.info("[校验人员手机号唯一]入参：custId={},mobile={}", custId, mobile);
      if (!CharSequenceUtil.isBlank(mobile)) {
         if (this.custInfoService.existsNormalCustMobile(mobile, custId)) {
            RetCodeEnum var10002 = RetCodeEnum.CUSTINFO_ALREADY_EXISTS;
            String var10003 = I18n.getMessage("customer.mobile.exists", new Object[0]);
            throw new ServiceException( var10002+":"+var10003 + mobile);
         }
      }
   }

   protected void checkOrgAndSetNumAndFullName(Long orgId, CustInfoModel infoModel) {
      QueryOrgsBasicInfoPO queryOrgsBasicInfoPO = (new QueryOrgsBasicInfoPO()).setOrgIds(Collections.singleton(orgId)).setDelFlags(Collections.singleton(DelFlagEnum.DEL_FALSE.key()));
      if (ObjectUtil.isNull(orgId)) {
         queryOrgsBasicInfoPO.setSuperIds(Collections.singleton(CustConstant.DATA_DEFAULT_LONG));
      }

      List<OrgBasicInfoVO> orgBasicInfoVOList = this.custOrgService.listOrgBasicInfoByParams(queryOrgsBasicInfoPO);
      if (CollUtil.isEmpty(orgBasicInfoVOList)) {
         throw new ServiceException(RetCodeEnum.MKT_ERROR_PARAM+":"+ I18n.getMessage("customer.org.org.inexists", new Object[0]));
      } else {
         OrgBasicInfoVO orgBasicInfoVO = (OrgBasicInfoVO)orgBasicInfoVOList.get(0);
         infoModel.setOrgNum(orgBasicInfoVO.getOrgNum());
         infoModel.setOrgFullName(orgBasicInfoVO.getOrgFullName());
      }
   }

   protected void checkPsnTypeAndSetPsnTypeName(Integer psnType, CustInfoModel infoModel) {
      if (!ObjectUtil.isNull(psnType)) {
         PsnTypeBacisInfoVO psnTypeBasicInfoVO = this.custPsnTypeService.getPsnTypeBasicInfoVO(psnType);
         if (ObjectUtil.isNull(psnTypeBasicInfoVO)) {
            throw new ServiceException(I18n.getMessage("customer.psnType.inExists", new Object[0]));
         } else {
            infoModel.setPsnTypeName(psnTypeBasicInfoVO.getPsnTypeName());
         }
      }
   }

   protected void checkIdCardAndSetBirthday(String idCard, CustInfoModel infoModel) {
      if (!CharSequenceUtil.isBlank(idCard)) {
         if (!IdcardUtil.isValidCard(idCard)) {
            throw new ServiceException(I18n.getMessage("customer.idCard.invalid", new Object[0]));
         } else {
            long count = this.custInfoService.count(Wrappers.<CustInfo>lambdaQuery().eq(CustInfo::getIdCard, SM4EncryptUtils.sm4Encryptbyconfig(idCard))
                    .eq(CustInfo::getCustState, PersonalStatusEnum.NORMAL.getKey())
                    .ne(ObjectUtil.isNotNull(infoModel.getCustId()), CustInfo::getCustId, infoModel.getCustId()));
            if (count > 0L) {
               String var10002 = I18n.getMessage("customer.idCard.repetition", new Object[0]);
               throw new ServiceException(var10002 + idCard);
            } else {
               LocalDate birthday = LocalDate.parse(IdcardUtil.getBirthByIdCard(idCard), DatePattern.PURE_DATE_FORMATTER);
               infoModel.setBirthday(birthday);
            }
         }
      }
   }

   protected void checkBirthday(LocalDate birthday) {
      if (ObjectUtil.isNotNull(birthday) && birthday.isAfter(LocalDate.now())) {
         throw new ServiceException(I18n.getMessage("customer.birthday.invalid", new Object[0]));
      }
   }

   protected void checkSex(Integer sex) {
      if (ObjectUtil.isNotNull(sex) && !PersonalSexEnum.isContains(sex)) {
         throw new ServiceException(I18n.getMessage("customer.sex.invalid", new Object[0]));
      }
   }

   public static <T> Map<T, Integer> getDuplicateElements(Stream<T> stream) {
//      return (Map)((Map)stream.collect(Collectors.toMap((e) -> {
//         return e;
//      }, (e) -> {
//         return 1;
//      }, Integer::sum))).entrySet().stream().filter((entry) -> {
//         return (Integer)entry.getValue() > 1;
//      }).map(Map.Entry::getKey).collect(Collectors.toMap(Function.identity(), (t) -> {
//         return 1;
//      }));
      return stream.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
              .entrySet().stream()
              .filter(entry -> entry.getValue() > 1)
              .collect(Collectors.toMap(Map.Entry::getKey, entry -> 1));
   }

   // $FF: synthetic method
//   private static Object $deserializeLambda$(SerializedLambda lambda) {
//      switch (lambda.getImplMethodName()) {
//         case "getIdCard":
//            if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("com/baomidou/mybatisplus/core/toolkit/support/SFunction") && lambda.getFunctionalInterfaceMethodName().equals("apply") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("net/xnzn/core/customer/model/CustInfo") && lambda.getImplMethodSignature().equals("()Ljava/lang/String;")) {
//               return CustInfo::getIdCard;
//            }
//            break;
//         case "getCustId":
//            if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("com/baomidou/mybatisplus/core/toolkit/support/SFunction") && lambda.getFunctionalInterfaceMethodName().equals("apply") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("net/xnzn/core/customer/model/CustInfo") && lambda.getImplMethodSignature().equals("()Ljava/lang/Long;")) {
//               return CustInfo::getCustId;
//            }
//
//            if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("com/baomidou/mybatisplus/core/toolkit/support/SFunction") && lambda.getFunctionalInterfaceMethodName().equals("apply") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("net/xnzn/core/customer/model/CustInfo") && lambda.getImplMethodSignature().equals("()Ljava/lang/Long;")) {
//               return CustInfo::getCustId;
//            }
//            break;
//         case "getBankCardNum":
//            if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("com/baomidou/mybatisplus/core/toolkit/support/SFunction") && lambda.getFunctionalInterfaceMethodName().equals("apply") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("net/xnzn/core/customer/model/CustInfo") && lambda.getImplMethodSignature().equals("()Ljava/lang/String;")) {
//               return CustInfo::getBankCardNum;
//            }
//            break;
//         case "getCustState":
//            if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("com/baomidou/mybatisplus/core/toolkit/support/SFunction") && lambda.getFunctionalInterfaceMethodName().equals("apply") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("net/xnzn/core/customer/model/CustInfo") && lambda.getImplMethodSignature().equals("()Ljava/lang/Integer;")) {
//               return CustInfo::getCustState;
//            }
//
//            if (lambda.getImplMethodKind() == 5 && lambda.getFunctionalInterfaceClass().equals("com/baomidou/mybatisplus/core/toolkit/support/SFunction") && lambda.getFunctionalInterfaceMethodName().equals("apply") && lambda.getFunctionalInterfaceMethodSignature().equals("(Ljava/lang/Object;)Ljava/lang/Object;") && lambda.getImplClass().equals("net/xnzn/core/customer/model/CustInfo") && lambda.getImplMethodSignature().equals("()Ljava/lang/Integer;")) {
//               return CustInfo::getCustState;
//            }
//      }
//
//      throw new IllegalArgumentException("Invalid lambda deserialization");
//   }
}
