package com.bonus.canteen.core.account.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.bonus.canteen.core.account.constants.AccTradeTypeEnum;
import com.bonus.canteen.core.account.constants.AccWalletIdEnum;
import com.bonus.canteen.core.account.domain.AccWalletInfo;
import com.bonus.canteen.core.account.domain.WalletUpdateDTO;
import com.bonus.canteen.core.account.domain.param.*;
import com.bonus.canteen.core.account.domain.vo.AccBatchOperationWallerErrorVO;
import com.bonus.canteen.core.account.domain.vo.AccBatchOperationWalletPreCheckVO;
import com.bonus.canteen.core.account.domain.vo.AccInfoDetailsVO;
import com.bonus.canteen.core.account.domain.vo.AccOperationListVO;
import com.bonus.canteen.core.account.service.AccSubService;
import com.bonus.canteen.core.account.service.IAccInfoService;
import com.bonus.canteen.core.account.service.IAccTradeService;
import com.bonus.canteen.core.account.service.IAccWalletInfoService;
import com.bonus.canteen.core.account.utils.AccRedisUtils;
import com.bonus.canteen.core.common.utils.MqUtil;
import com.bonus.canteen.core.pay.constants.PayChannelEnum;
import com.bonus.canteen.core.pay.constants.PayTypeEnum;
import com.bonus.canteen.core.user.domain.DeviceMqPersonalUpdateMessageDTO;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.houqin.mq.constant.LeMqConstant;
import com.bonus.common.houqin.utils.JacksonUtil;
import com.bonus.common.houqin.utils.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.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class AccSubServiceImpl implements AccSubService {
   private static final Logger log = LoggerFactory.getLogger(AccSubServiceImpl.class);
   @Autowired
   @Lazy
   private IAccInfoService accInfoService;
   @Autowired
   @Lazy
   private IAccWalletInfoService accWalletInfoService;
   @Autowired
   @Lazy
   IAccTradeService accTradeService;

   @Resource(
      name = "smartCanteenTaskExecutor"
   )
   @Lazy
   private AsyncTaskExecutor asyncTaskExecutor;

   @Transactional(
      rollbackFor = {Exception.class}
   )
   public void individualAccSubsidyAdd(AccSubsidyAddParam param) {
      AccInfoDetailsVO accInfoVO = this.accInfoService.queryAccInfoByUserId(param.getUserId());
      if(Objects.isNull(accInfoVO)) {
         throw new ServiceException("账户不存在");
      }
      accInfoService.checkAccStatus(accInfoVO);
      WalletUpdateDTO walletUpdateDTO = new WalletUpdateDTO();
      walletUpdateDTO.setAmount(param.getAmount());
      walletUpdateDTO.setUserId(param.getUserId());
      walletUpdateDTO.setAccWalletIdEnum(AccWalletIdEnum.SUBSIDY);
      walletUpdateDTO.setAccTradeTypeEnum(AccTradeTypeEnum.SUBSIDY);
      walletUpdateDTO.setPayChannelEnum(PayChannelEnum.GC_SYSTEM);
      walletUpdateDTO.setPayTypeEnum(PayTypeEnum.SUB_GRANT);
      accWalletInfoService.addAccWalletBalance(walletUpdateDTO);
      try {
         DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(Math.toIntExact(param.getUserId()),"update");
         String jsonString = JacksonUtil.writeValueAsString(bean);
         log.info("账户变动发送mq内容：{}", jsonString);
         MqUtil.pushToTenantAllDevice(bean, LeMqConstant.Topic.DEVICE_UPDATE_PERSONAL_CONFIG_V4);
      } catch (Exception e) {
         log.error("发送MQ消息失败", e);
      }
   }
   @Transactional(
      rollbackFor = {Exception.class}
   )
   public void individualAccSubsidyClear(AccSubsidyClearParam param) {
      AccInfoDetailsVO accInfoVO = this.accInfoService.queryAccInfoByUserId(param.getUserId());
      if(Objects.isNull(accInfoVO)) {
         throw new ServiceException("账户不存在");
      }
      accInfoService.checkAccStatus(accInfoVO);
      clearAllAccSubsidyHandler(accInfoVO, param);
      try {
         DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(Math.toIntExact(param.getUserId()),"update");
         String jsonString = JacksonUtil.writeValueAsString(bean);
         log.info("账户变动发送mq内容：{}", jsonString);
         MqUtil.pushToTenantAllDevice(bean, LeMqConstant.Topic.DEVICE_UPDATE_PERSONAL_CONFIG_V4);
      } catch (Exception e) {
         log.error("发送MQ消息失败", e);
      }
   }

//   private void clearAccSubsidyHandler(AccInfoDetailsVO accInfoVO, AccSubsidyClearParam param) {
//      AccWalletInfo walletInfo = accWalletInfoService.selectAccWalletInfoByUserIdAndWalletId(accInfoVO.getUserId(), AccWalletIdEnum.SUBSIDY.getKey());
//      if(Objects.isNull(walletInfo)) {
//         throw new ServiceException("补贴钱包不存在");
//      }
//      BigDecimal walletBal = walletInfo.getWalletBal();
//      if (walletBal.compareTo(BigDecimal.ZERO) <= 0) {
//         throw new ServiceException("补贴钱包余额必须大于0元");
//      } else {
//         BigDecimal clearAmount = this.calculateClearAmount(walletBal, param.getClearType(), param.getAmount());
//         if (clearAmount.compareTo(BigDecimal.ZERO) <= 0) {
//            throw new ServiceException("清空金额必须大于0元");
//         } else {
//            reduceAccWalletBalance(clearAmount, param.getUserId(), AccWalletIdEnum.SUBSIDY.getKey());
//         }
//      }
//   }

   private void clearAllAccSubsidyHandler(AccInfoDetailsVO accInfoVO, AccSubsidyClearParam param) {
      AccWalletInfo walletInfo = accWalletInfoService.selectAccWalletInfoByUserIdAndWalletId(accInfoVO.getUserId(), AccWalletIdEnum.SUBSIDY.getKey());
      if(Objects.isNull(walletInfo)) {
         throw new ServiceException("补贴钱包不存在");
      }
      WalletUpdateDTO walletUpdateDTO = new WalletUpdateDTO();
      walletUpdateDTO.setAmount(walletInfo.getWalletBal());
      walletUpdateDTO.setUserId(param.getUserId());
      walletUpdateDTO.setAccWalletIdEnum(AccWalletIdEnum.SUBSIDY);
      walletUpdateDTO.setAccTradeTypeEnum(AccTradeTypeEnum.CLEAR);
      walletUpdateDTO.setPayChannelEnum(PayChannelEnum.GC_SYSTEM);
      walletUpdateDTO.setPayTypeEnum(PayTypeEnum.SUB_GRANT);
      accWalletInfoService.clearAccWalletBalance(walletUpdateDTO);
   }

//   private BigDecimal calculateClearAmount(BigDecimal walletBal, Integer clearType, BigDecimal amount) {
//      BigDecimal clearAmount;
//      if (AccWalletClearTypeEnum.CLEAR_BY.getKey().equals(clearType)) {
//         clearAmount = amount.compareTo(walletBal) >= 0 ? BigDecimal.ZERO : walletBal.subtract(amount);
//      } else {
//         clearAmount = amount.compareTo(walletBal) >= 0 ? walletBal : amount;
//      }
//      return clearAmount.compareTo(BigDecimal.ZERO) <= 0 ? BigDecimal.ZERO : clearAmount;
//   }

   public void batchAccSubsidyClear(AccSubsidyBatchClearParam param) {
      log.info("批量清空补贴用户数量:{}", param.getUserIds().size());
      if(CollUtil.isEmpty(param.getUserIds())) {
         throw new ServiceException("批量清空补贴用户为空");
      }
      List<AccInfoDetailsVO> accInfoVOList = this.accInfoService.queryAccInfoByUserIds(param.getUserIds());
      log.info("批量清空补贴根据用户编号查询到{}人", accInfoVOList.size());
      if (CollUtil.isEmpty(accInfoVOList)) {
         throw new ServiceException("批量清空补贴查询到的用户为空");
      } else {
         this.asyncTaskExecutor.execute(() -> {
            this.batchAccSubsidyClearHandler(accInfoVOList, param.getClearType());
         });
      }
   }

   public void batchAccSubsidyAdd(AccSubsidyBatchAddParam param) {
      log.info("批量补贴用户数量:{}", param.getUserIds().size());
      if(CollUtil.isEmpty(param.getUserIds())) {
         throw new ServiceException("批量补贴用户为空");
      }
      List<AccInfoDetailsVO> accInfoVOList = this.accInfoService.queryAccInfoByUserIds(param.getUserIds());
      log.info("批量补贴根据用户编号查询到{}人", accInfoVOList.size());
      if (CollUtil.isEmpty(accInfoVOList)) {
         throw new ServiceException("批量补贴查询到的用户为空");
      } else {
         this.asyncTaskExecutor.execute(() -> {
            this.batchAccSubsidyAddHandler(accInfoVOList, param.getAmount());
         });
      }
   }

   private void batchAccSubsidyAddHandler(List<AccInfoDetailsVO> accInfoDetails, BigDecimal amount) {
      log.info("批量补贴开始:{}", accInfoDetails.size());
      AccRedisUtils.lockBatchUpdateAccWallet();
      try {
         accInfoDetails.forEach((accInfo) -> {
            try {
               AccInfoDetailsVO accInfoVO = this.accInfoService.queryAccInfoByUserId(accInfo.getUserId());
               if(Objects.isNull(accInfoVO)) {
                  throw new ServiceException(accInfo.getNickName() + "的账户不存在");
               }
               accInfoService.checkAccStatus(accInfoVO);
               WalletUpdateDTO walletUpdateDTO = new WalletUpdateDTO();
               walletUpdateDTO.setAmount(amount);
               walletUpdateDTO.setUserId(accInfo.getUserId());
               walletUpdateDTO.setAccWalletIdEnum(AccWalletIdEnum.SUBSIDY);
               walletUpdateDTO.setAccTradeTypeEnum(AccTradeTypeEnum.SUBSIDY);
               walletUpdateDTO.setPayChannelEnum(PayChannelEnum.GC_SYSTEM);
               walletUpdateDTO.setPayTypeEnum(PayTypeEnum.SUB_GRANT);
               accWalletInfoService.addAccWalletBalance(walletUpdateDTO);
               try {
                  DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(Math.toIntExact(accInfo.getUserId()),"update");
                  String jsonString = JacksonUtil.writeValueAsString(bean);
                  log.info("账户变动发送mq内容：{}", jsonString);
                  MqUtil.pushToTenantAllDevice(bean, LeMqConstant.Topic.DEVICE_UPDATE_PERSONAL_CONFIG_V4);
               } catch (Exception e) {
                  log.error("发送MQ消息失败", e);
               }
            } catch (Exception ex) {
               log.error("批量补贴充值异常, 用户id: {}, 补贴金额：{}", accInfo.getUserId(), amount, ex);
            }

         });
      } catch (Exception ex) {
         log.error("批量补贴系统异常", ex);
      } finally {
         AccRedisUtils.unlockBatchUpdateAccWallet();
      }

      log.info("批量补贴结束");
   }

   private void batchAccSubsidyClearHandler(List<AccInfoDetailsVO> accInfoDetails, Integer clearType) {
      log.info("批量清空补贴开始:{}", accInfoDetails.size());
      AccRedisUtils.lockBatchUpdateAccWallet();
      try {
         accInfoDetails.forEach((accInfo) -> {
            try {
               AccInfoDetailsVO accInfoVO = this.accInfoService.queryAccInfoByUserId(accInfo.getUserId());
               if(Objects.isNull(accInfoVO)) {
                  throw new ServiceException(accInfo.getNickName() + "的账户不存在");
               }
               accInfoService.checkAccStatus(accInfoVO);
               AccSubsidyClearParam param = new AccSubsidyClearParam();
               param.setUserId(accInfo.getUserId());
               param.setClearType(clearType);
               clearAllAccSubsidyHandler(accInfoVO, param);
               try {
                  DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(Math.toIntExact(accInfo.getUserId()),"update");
                  String jsonString = JacksonUtil.writeValueAsString(bean);
                  log.info("账户变动发送mq内容：{}", jsonString);
                  MqUtil.pushToTenantAllDevice(bean, LeMqConstant.Topic.DEVICE_UPDATE_PERSONAL_CONFIG_V4);
               } catch (Exception e) {
                  log.error("发送MQ消息失败", e);
               }
            } catch (Exception var10) {
               log.error("批量清空补贴充值异常", var10);
            }

         });
      } catch (Exception ex) {
         log.error("批量清空补贴系统异常", ex);
      } finally {
         AccRedisUtils.unlockBatchUpdateAccWallet();
      }

      log.info("批量清空补贴结束");
   }

//   public void reduceAccWalletBalance(BigDecimal amount, Long userId, Integer walletId) {
//      log.info("清空补贴入参: amount：{}， userId：{}，walletId：{}", amount, userId, walletId);
//      AccRedisUtils.lockUpdateAccWalletBalance(userId);
//      try {
//         WalletBalanceOperation operation = new WalletBalanceOperation();
//         operation.setAmount(amount);
//         operation.setUserId(userId);
//         operation.setWalletId(walletId);
//         operation.setOperationType(WalletBalanceOperationEnum.REDUCE_BAL.getKey());
//         operation.setTradeType(AccTradeTypeEnum.CLEAR.getKey());
//         operation.setPayChannel(PayChannelEnum.GW_SYSTEM.getKey());
//         operation.setPayType(PayTypeEnum.SUB_GRANT.getKey());
//         accWalletInfoService.acWalletBalanceOperation(operation);
//         log.info("清空补贴结束");
//      } finally {
//         AccRedisUtils.unlockUpdateAccWalletBalance(userId);
//      }
//   }

   public AccBatchOperationWalletPreCheckVO batchOperationWalletClearCheck(AccSubsidyBatchClearParam param) {
      log.info("批量清空校验操作人数：{}", param.getUserIds().size());
      List<AccInfoDetailsVO> accInfoVOList = accInfoService.queryAccInfoByUserIds(param.getUserIds());
      if (CollUtil.isEmpty(accInfoVOList)) {
         throw new ServiceException("批量补贴查询到的用户为空");
      }
      List<AccBatchOperationWallerErrorVO> errVOList = new ArrayList<>();
      List<Long> validUserList = new ArrayList<>();
      List<BigDecimal> amountList = new ArrayList<>();
      BigDecimal walletBal = null;
      for (AccInfoDetailsVO accInfo : accInfoVOList) {
         try {
            checkAccount(accInfo);
            AccWalletInfo walletInfo = accWalletInfoService.selectAccWalletInfoByUserIdAndWalletId(accInfo.getUserId(), AccWalletIdEnum.SUBSIDY.getKey());
            if(Objects.isNull(walletInfo)) {
               throw new ServiceException("补贴钱包不存在");
            }
            walletBal = walletInfo.getWalletBal();
//            if (walletBal.compareTo(BigDecimal.ZERO) <= 0) {
//               throw new ServiceException("补贴钱包余额必须大于0元");
//            } else {
//               BigDecimal clearAmount = this.calculateClearAmount(walletBal, param.getClearType(), param.getAmount());
//               if (clearAmount.compareTo(BigDecimal.ZERO) <= 0) {
//                  throw new ServiceException("清空金额必须大于0元");
//               }else{
//                  amountList.add(clearAmount);
//               }
//            }
            amountList.add(walletBal);
            validUserList.add(walletInfo.getUserId());
         } catch (Exception ex) {
            errVOList.add(createErrorVO(accInfo, walletBal, ex.getMessage()));
         }
      }
      BigDecimal validTotalAmount = amountList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
      AccBatchOperationWalletPreCheckVO result = new AccBatchOperationWalletPreCheckVO();
      result.setTotalUserSum(param.getUserIds().size());
      result.setValidCount(validUserList.size());
      result.setValidTotalAmount(validTotalAmount);
      result.setValidUserIdList(validUserList);
      result.setInvalidCount(errVOList.size());
      result.setErrVOList(errVOList);
      result.setRemark(param.getRemark());
      return result;
   }

   public AccBatchOperationWalletPreCheckVO batchOperationWalletAddCheck(AccSubsidyBatchAddParam param) {
      log.info("批量校验操作人数：{}", param.getUserIds().size());
      List<AccInfoDetailsVO> accInfoVOList = accInfoService.queryAccInfoByUserIds(param.getUserIds());
      if (CollUtil.isEmpty(accInfoVOList)) {
         throw new ServiceException("批量补贴查询到的用户为空");
      }
      List<AccBatchOperationWallerErrorVO> errVOList = new ArrayList<>();
      List<Long> sucVoList = new ArrayList<>();

      for (AccInfoDetailsVO accInfo : accInfoVOList) {
         try {
            checkAccount(accInfo);
            sucVoList.add(accInfo.getUserId());
         } catch (Exception ex) {
            errVOList.add(createErrorVO(accInfo, param.getAmount(), ex.getMessage()));
         }
      }
      BigDecimal validTotalAmount = param.getAmount().multiply(new BigDecimal(sucVoList.size()));
      AccBatchOperationWalletPreCheckVO result = new AccBatchOperationWalletPreCheckVO();
      result.setTotalUserSum(param.getUserIds().size());
      result.setValidCount(sucVoList.size());
      result.setValidTotalAmount(validTotalAmount);
      result.setValidUserIdList(sucVoList);
      result.setInvalidCount(errVOList.size());
      result.setErrVOList(errVOList);
      result.setAmount(param.getAmount());
      result.setRemark(param.getRemark());
      return result;
   }

   private void checkAccount(AccInfoDetailsVO accInfo) {
      AccInfoDetailsVO accInfoVO = accInfoService.queryAccInfoByUserId(accInfo.getUserId());
      accInfoService.checkAccStatus(accInfoVO);
   }

   private AccBatchOperationWallerErrorVO createErrorVO(AccInfoDetailsVO accInfo, BigDecimal amount, String errMsg) {
      AccBatchOperationWallerErrorVO errVO = new AccBatchOperationWallerErrorVO();
      errVO.setUserId(accInfo.getUserId());
      errVO.setNickName(accInfo.getNickName());
      errVO.setDeptFullName(accInfo.getDeptFullName());
      errVO.setUserType(accInfo.getUserType());
      errVO.setPhoneNumber(SM4EncryptUtils.sm4Decrypt(accInfo.getPhoneNumber()));
      errVO.setAmount(amount);
      errVO.setErrorMessage(errMsg);
      return errVO;
   }
   @Override
   public List<AccOperationListVO> queryAccSubOperationList(AccWalletOperationQueryParam param) {
      List<AccOperationListVO> operationListVOList = accTradeService.queryAccOperationList(param);
      if(CollUtil.isNotEmpty(operationListVOList)) {
         for(AccOperationListVO vo: operationListVOList) {
            vo.setPhoneNumber(SM4EncryptUtils.sm4Decrypt(vo.getPhoneNumber()));
         }
      }
      return operationListVOList ;
   }
}
