
package com.tmerclub.cloud.user.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.tmerclub.cloud.api.coupon.bo.CouponGiveBO;
import com.tmerclub.cloud.api.coupon.bo.CouponIdAndCountBO;
import com.tmerclub.cloud.api.coupon.feign.CouponFeignClient;
import com.tmerclub.cloud.api.coupon.vo.CouponDataVO;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
import com.tmerclub.cloud.api.platform.feign.ConfigFeignClient;
import com.tmerclub.cloud.api.user.bo.RechargeNotifyBO;
import com.tmerclub.cloud.common.cache.constant.CacheNames;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.util.PageUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.PriceUtil;
import com.tmerclub.cloud.common.util.PrincipalUtil;
import com.tmerclub.cloud.user.bo.mongo.MongoUserBalanceLogBO;
import com.tmerclub.cloud.user.bo.mongo.MongoUserScoreLogBO;
import com.tmerclub.cloud.user.constant.*;
import com.tmerclub.cloud.user.dto.RechargeCouponDTO;
import com.tmerclub.cloud.user.dto.UserAdminDTO;
import com.tmerclub.cloud.user.dto.UserRechargeDTO;
import com.tmerclub.cloud.user.mapper.UserGrowthLogMapper;
import com.tmerclub.cloud.user.mapper.UserRechargeMapper;
import com.tmerclub.cloud.user.model.UserExtension;
import com.tmerclub.cloud.user.model.UserGrowthLog;
import com.tmerclub.cloud.user.model.UserRecharge;
import com.tmerclub.cloud.user.service.*;
import com.tmerclub.cloud.user.vo.UserRechargeVO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 余额充值级别表
 *
 * @author tmerclub
 * @date 2021-04-27 15:51:36
 */
@Service
public class UserRechargeServiceImpl implements UserRechargeService {

    @Autowired
    private UserRechargeMapper userRechargeMapper;
    @Autowired
    private UserExtensionService userExtensionService;
    @Autowired
    private UserBalanceLogService userBalanceLogService;
    @DubboReference
    private CouponFeignClient couponFeignClient;
    @Autowired
    private UserRechargeCouponService userRechargeCouponService;
    @Autowired
    private RocketMQTemplate couponGiveTemplate;
    @Autowired
    private UserGrowthLogMapper userGrowthLogMapper;
    @Autowired
    private UserScoreLogService userScoreLogService;
    @DubboReference
    private ConfigFeignClient configFeignClient;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private RocketMQTemplate addUserBalanceLogTemplate;
    @Autowired
    private RocketMQTemplate addUserScoreLogTemplate;

    @Override
    public PageVO<UserRecharge> page(PageDTO pageDTO) {
        return PageUtil.doPage(pageDTO, () -> userRechargeMapper.list());
    }

    @Override
    @Cacheable(cacheNames = CacheNames.USER_RECHARGE_INFO, key = "#rechargeId")
    public UserRechargeVO getRechargeInfo(Long rechargeId) {
        return userRechargeMapper.getRechargeInfo(rechargeId);
    }

    @Override
    @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_LIST, allEntries = true)
    public void save(UserRecharge userRecharge) {
        userRechargeMapper.save(userRecharge);
    }

    @Override
    public void update(UserRecharge userRecharge) {
        userRechargeMapper.update(userRecharge);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteById(Long rechargeId) {
        userRechargeMapper.deleteById(rechargeId);
        userRechargeCouponService.deleteById(rechargeId);
    }

    @Override
    @Cacheable(cacheNames = CacheNames.USER_RECHARGE_LIST)
    public List<UserRechargeVO> list() {
        List<UserRechargeVO> list = userRechargeMapper.list();
        List<Long> couponIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (UserRechargeVO userRechargeVO : list) {
            List<CouponDataVO> couponList = userRechargeVO.getCouponList();
            if (CollUtil.isNotEmpty(couponList)) {
                couponIds.addAll(couponList.stream().map(CouponDataVO::getCouponId).collect(Collectors.toList()));
            }
        }
        if (CollUtil.isNotEmpty(couponIds)) {
            ServerResponseEntity<List<CouponDataVO>> couponEntity = couponFeignClient.getCouponListByCouponIds(couponIds);
            if (!couponEntity.isSuccess()) {
                throw new LuckException(couponEntity.getMsg());
            }
            List<CouponDataVO> couponDataVOList = couponEntity.getData();
            Map<Long, String> couponMap = couponDataVOList.stream().collect(Collectors.toMap(CouponDataVO::getCouponId, CouponDataVO::getCouponName));
            for (UserRechargeVO userRechargeVO : list) {
                userRechargeVO.setCouponTotalNum(userRechargeVO.getCouponList().size());
                for (CouponDataVO couponDataVO : userRechargeVO.getCouponList()) {
                    Long couponId = couponDataVO.getCouponId();
                    if (couponMap.containsKey(couponId)) {
                        couponDataVO.setCouponName(couponMap.get(couponId));
                    }
                }
            }
        }
        return list;
    }

    @Override
    @Caching(evict = {
            @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_LIST, allEntries = true),
            @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_INFO, key = "#rechargeId")})
    public void removeCacheByRechargeId(Long rechargeId) {
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean batchUpdateUserBalance(UserAdminDTO userAdminDTO) {
        Integer paySysType = configFeignClient.getPaySysType().getData();
        if (Objects.equals(paySysType, PaySysType.ALLINPAY.value())) {
            throw new LuckException("通联支付不支持手动修改余额");
        }
        BigDecimal balance = userAdminDTO.getBalanceValue();
        if (!PrincipalUtil.isMaximumOfTwoDecimal(balance.toString())) {
            throw new LuckException("最多是保留两位小数的数值");
        }
        // 将元转为分
        long balanceValue = PriceUtil.toLongCent(balance);
        List<Long> userIds = userAdminDTO.getUserIds();
        if (Objects.equals(0L, balanceValue) || CollUtil.isEmpty(userIds)) {
            return false;
        }
        DateTime now = DateUtil.date();
        // 批量给用户充值
        List<UserExtension> extensions = userExtensionService.getByUserIdsAndLevelType(userIds);
        List<Long> userIdsUpdate = extensions.stream().map(UserExtension::getUserId).collect(Collectors.toList());
        List<MongoUserBalanceLogBO> userBalanceLogs = new ArrayList<>(Constant.INITIAL_CAPACITY);
        List<Long> userBalanceLogIds = segmentManager.listSegmentId(DistributedIdKey.tmerclub_USER_BALANCE_LOG, extensions.size());
        int index = 0;
        for (UserExtension extension : extensions) {
            if (Objects.isNull(extension)) {
                continue;
            }
            // 用户改变的余额数值
            long changeBalance = balanceValue;
            long totalBalance = Objects.nonNull(extension.getActualBalance()) ? extension.getActualBalance() : 0L;
            int compare = Long.compare(PriceUtil.MAX_CENT - extension.getBalance(), balanceValue);
            // 用户余额的balance的最大值为Long.MAX_VALUE，当用户余额 + 新增余额达到这个值的时候，用户将不能在继续充值了
            if (compare < 0 && userIds.size() == 1) {
                throw new LuckException("当前用户余额已达到最大限额，剩余可充值余额为：" + PriceUtil.toDecimalPrice(PriceUtil.MAX_CENT - extension.getBalance()));
            } else if (compare < 0 && userIds.size() > 1) {
                continue;
            }
            long userBalance = totalBalance + balanceValue;
            if (userBalance < 0) {
                //修改后的金额 < 0 ，那么减少的金额就是用户原本的余额
                changeBalance = totalBalance;
            } else if (userBalance > Constant.MAX_USER_BALANCE) {
                changeBalance = Constant.MAX_USER_BALANCE - totalBalance;
            }
            // 添加日志
            MongoUserBalanceLogBO userBalanceLog = new MongoUserBalanceLogBO();
            userBalanceLog.setBalanceLogId(userBalanceLogIds.get(index));
            userBalanceLog.setUserId(extension.getUserId());
            userBalanceLog.setChangeBalance(Math.abs(changeBalance));
            userBalanceLog.setPaySysType(PaySysType.DEFAULT.value());
            userBalanceLog.setIoType(balanceValue > 0 ? RechargeIoTypeEnum.INCOME.value() : RechargeIoTypeEnum.EXPENDITURE.value());
            userBalanceLog.setType(RechargeTypeEnum.SYSTEM.value());
            userBalanceLogs.add(userBalanceLog);
            index++;
        }
        if (CollUtil.isEmpty(userIdsUpdate)) {
            return false;
        }
        userExtensionService.updateBatchUserBalanceByUserIds(userIdsUpdate, balanceValue, now);
        userBalanceLogService.saveBatchToMongodb(userBalanceLogs);
        return true;
    }

    @Override
    public UserRechargeVO getRechargeByRechargeId(Long rechargeId, Integer putOnStatus) {
        if (Objects.isNull(userRechargeMapper.getByRechargeId(rechargeId))) {
            throw new LuckException("数据状态错误，请刷新重试");
        }
        UserRechargeVO userRechargeVO = getRechargeInfo(rechargeId);
        List<CouponDataVO> couponDataList = userRechargeVO.getCouponList();
        if (CollUtil.isEmpty(couponDataList)) {
            return userRechargeVO;
        }
        List<Long> couponIds = couponDataList.stream().map(CouponDataVO::getCouponId).collect(Collectors.toList());
        // 查询优惠券信息
        ServerResponseEntity<List<CouponDataVO>> couponResponse = couponFeignClient.getCouponListByCouponIdsAndPutOnStatus(couponIds, putOnStatus);
        if (!Objects.equals(couponResponse.getCode(), ResponseEnum.OK.value())) {
            throw new LuckException(couponResponse.getMsg());
        }
        List<CouponDataVO> couponDataVOList = couponResponse.getData();
        if (CollUtil.isEmpty(couponDataVOList)) {
            return userRechargeVO;
        }
        Map<Long, CouponDataVO> couponDataMap = couponDataList.stream().collect(Collectors.toMap(CouponDataVO::getCouponId, (k) -> k));
        for (CouponDataVO couponDataVO : couponDataVOList) {
            CouponDataVO dataVO = couponDataMap.get(couponDataVO.getCouponId());
            couponDataVO.setCouponNum(dataVO.getCouponNum());
        }
        userRechargeVO.setCouponList(couponDataVOList);
        return userRechargeVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = CacheNames.USER_RECHARGE_LIST, allEntries = true)
    public void saveRecharge(UserRechargeDTO userRechargeDTO) {
        UserRecharge userRecharge = BeanUtil.map(userRechargeDTO, UserRecharge.class);
        userRechargeMapper.save(userRecharge);
        if (CollUtil.isEmpty(userRechargeDTO.getCouponList())) {
            return;
        }
        userRechargeCouponService.insertBatch(userRecharge.getRechargeId(), userRechargeDTO.getCouponList());
    }

    @Override
    public void updateByRechargeId(UserRechargeDTO userRechargeDTO) {
        if (Objects.isNull(userRechargeMapper.getByRechargeId(userRechargeDTO.getRechargeId()))) {
            throw new LuckException("数据状态错误，请刷新重试");
        }
        UserRecharge userRecharge = BeanUtil.map(userRechargeDTO, UserRecharge.class);
        userRechargeMapper.update(userRecharge);
        UserRechargeVO userRechargeDb = getRechargeByRechargeId(userRecharge.getRechargeId(), null);
        Map<Long, CouponDataVO> mapByCouponId = userRechargeDb.getCouponList().stream().collect(Collectors.toMap(CouponDataVO::getCouponId, c -> c));
        List<RechargeCouponDTO> addCoupon = Lists.newArrayList();
        if (CollUtil.isEmpty(userRechargeDTO.getCouponList()) && CollUtil.isNotEmpty(userRechargeDb.getCouponList())) {
            // 之前赠送优惠券，更新为不赠送优惠券
            userRechargeCouponService.deleteById(userRecharge.getRechargeId());
        } else if (CollUtil.isEmpty(userRechargeDb.getCouponList()) && CollUtil.isNotEmpty(userRechargeDTO.getCouponList())) {
            // 之前不赠送优惠券，更新为赠送优惠券
            userRechargeCouponService.insertBatch(userRechargeDTO.getRechargeId(), userRechargeDTO.getCouponList());
        } else {
            //筛选需要删除的优惠券.
            Set<Long> couponIdList = mapByCouponId.keySet();
            List<RechargeCouponDTO> updateList = Lists.newArrayList();

            for (RechargeCouponDTO couponDto : userRechargeDTO.getCouponList()) {
                if (!couponIdList.contains(couponDto.getCouponId())) {
                    addCoupon.add(couponDto);
                    continue;
                }
                CouponDataVO couponDb = mapByCouponId.get(couponDto.getCouponId());
                if (Objects.equals(couponDto.getCouponNum(), couponDb.getCouponNum())) {
                    couponIdList.remove(couponDto.getCouponId());
                    continue;
                }
                updateList.add(couponDto);
                couponIdList.remove(couponDto.getCouponId());
            }
            if (CollUtil.isNotEmpty(couponIdList)) {
                userRechargeCouponService.removeByRechargeIdAndCouponId(userRechargeDTO.getRechargeId(), couponIdList);
            }
            if (CollUtil.isNotEmpty(addCoupon)) {
                userRechargeCouponService.insertBatch(userRechargeDTO.getRechargeId(), addCoupon);
            }
            if (CollUtil.isNotEmpty(updateList)) {
                userRechargeCouponService.updateBatchByCoupons(userRechargeDTO.getRechargeId(), updateList);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rechargeSuccess(RechargeNotifyBO message) {
        Long balanceLogId = message.getBalanceLogId();
        MongoUserBalanceLogBO userBalanceLogDb = userBalanceLogService.getByBalanceLogId(balanceLogId);
        if (Objects.equals(userBalanceLogDb.getIsPayed(), 1)) {
            return;
        }
        Long rechargeId = userBalanceLogDb.getRechargeId();
        UserRechargeVO userRechargeVO = null;
        if (Objects.nonNull(rechargeId)) {
            userRechargeVO = getRechargeInfo(rechargeId);
        }
        // 更新支付状态
        userBalanceLogService.updateToSuccess(balanceLogId, message.getPayId(), message.getPayType());
        List<CouponDataVO> couponGiveList = null;
        if (userRechargeVO != null
                && Objects.equals(userBalanceLogDb.getPaySysType(), PaySysType.DEFAULT.value())) {
            // 只有默认支付才能享受充值优惠，增加成长值、积分、余额
            userExtensionService.updateByUserRecharge(userRechargeVO, userBalanceLogDb);
            couponGiveList = userRechargeVO.getCouponList();
            addPreLog(userBalanceLogDb, userRechargeVO);
        } else {
            // 更新用户余额
            userExtensionService.addByUserBalanceLog(userBalanceLogDb);
        }

        // 没有赠送优惠券，则返回
        if (CollectionUtil.isEmpty(couponGiveList)) {
            return;
        }
        // 处理赠送优惠券
        CouponGiveBO couponGiveBO = new CouponGiveBO();

        List<CouponIdAndCountBO> couponInfos = new ArrayList<>(Constant.INITIAL_CAPACITY);


        for (CouponDataVO couponDataVO : couponGiveList) {
            CouponIdAndCountBO couponIdAndCountBO = new CouponIdAndCountBO();
            couponIdAndCountBO.setCouponId(couponDataVO.getCouponId());
            couponIdAndCountBO.setCount(couponDataVO.getCouponNum());
            couponInfos.add(couponIdAndCountBO);
        }
        couponGiveBO.setUserId(userBalanceLogDb.getUserId());
        couponGiveBO.setBizId(userBalanceLogDb.getBalanceLogId());
        // 充值送优惠券
        couponGiveBO.setBizType(0);
        couponGiveBO.setCouponInfos(couponInfos);
        // 发送消息，赠送优惠券
        SendStatus sendStatus = couponGiveTemplate.syncSend(RocketMqConstant.COUPON_GIVE_TOPIC, new GenericMessage<>(couponGiveBO)).getSendStatus();
        if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，因为订单回调会有多次，几乎不可能每次都无法发送出去，发的出去无所谓因为接口是幂等的
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    /**
     * 添加余额充值赠送的余额、积分、成长值记录
     *
     * @param userBalanceLogDb
     * @param userRechargeVO
     */
    private void addPreLog(MongoUserBalanceLogBO userBalanceLogDb, UserRechargeVO userRechargeVO) {
        // 添加成长值赠送记录
        if (userRechargeVO.getPresGrowth() > 0) {
            UserGrowthLog userGrowthLog = new UserGrowthLog();
            userGrowthLog.setUserId(userBalanceLogDb.getUserId());
            userGrowthLog.setSource(GrowthLogSourceEnum.BALANCE.value());
            userGrowthLog.setBizId(userBalanceLogDb.getBalanceLogId());
            userGrowthLog.setChangeGrowth(userRechargeVO.getPresGrowth());
            userGrowthLog.setRemarks("余额充值赠送的成长值");
            userGrowthLogMapper.save(userGrowthLog);
        }
        // 添加余额赠送记录
        if (userRechargeVO.getPresAmount() > 0) {
            MongoUserBalanceLogBO userBalanceLog = new MongoUserBalanceLogBO();
            userBalanceLog.setBalanceLogId(segmentManager.getSegmentId(DistributedIdKey.tmerclub_USER_BALANCE_LOG));
            userBalanceLog.setUserId(userBalanceLogDb.getUserId());
            userBalanceLog.setChangeBalance(userRechargeVO.getPresAmount());
            userBalanceLog.setIoType(RechargeIoTypeEnum.INCOME.value());
            userBalanceLog.setType(RechargeTypeEnum.PRESENT.value());
            userBalanceLog.setPaySysType(PaySysType.DEFAULT.value());
            SendStatus sendStatus = addUserBalanceLogTemplate.syncSend(RocketMqConstant.ADD_USER_BALANCE_LOG_TOPIC, new GenericMessage<>(Collections.singletonList(userBalanceLog))).getSendStatus();
            if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
                // 消息发不出去就抛异常，发的出去无所谓
                throw new LuckException(ResponseEnum.EXCEPTION);
            }
        }
        // 添加积分赠送记录
        if (userRechargeVO.getPresScore() > 0) {
            MongoUserScoreLogBO userScoreLog = new MongoUserScoreLogBO();
            userScoreLog.setLogId(segmentManager.getSegmentId(DistributedIdKey.tmerclub_USER_SCORE_LOG));
            userScoreLog.setUserId(userBalanceLogDb.getUserId());
            userScoreLog.setSource(ScoreLogTypeEnum.BALANCE.value());
            userScoreLog.setScore(userRechargeVO.getPresScore());
            userScoreLog.setBizId(userBalanceLogDb.getBalanceLogId());
            userScoreLog.setIoType(ScoreIoTypeEnum.INCOME.value());
            SendStatus sendStatus = addUserScoreLogTemplate.syncSend(RocketMqConstant.ADD_USER_SCORE_LOG_TOPIC, new GenericMessage<>(Collections.singletonList(userScoreLog))).getSendStatus();
            if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
                // 消息发不出去就抛异常，发的出去无所谓
                throw new LuckException(ResponseEnum.EXCEPTION);
            }
        }
    }

}
