package com.qs.mp.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.qs.mp.common.core.domain.AjaxResult;
import com.qs.mp.common.core.redis.DistributedLocker;
import com.qs.mp.common.enums.CoinLogTypeEnum;
import com.qs.mp.common.exception.ServiceException;
import com.qs.mp.framework.redis.RedisLockKey;
import com.qs.mp.user.domain.UserCoin;
import com.qs.mp.user.domain.UserCoinLog;
import com.qs.mp.user.domain.dto.CoinTransferParamDTO;
import com.qs.mp.user.domain.param.CoinTransferParam;
import com.qs.mp.user.mapper.UserCoinMapper;
import com.qs.mp.user.service.IUserCoinLogService;
import com.qs.mp.user.service.IUserCoinService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * <p>
 * 用户代币 服务实现类
 * </p>
 *
 * @author quanshu
 * @since 2022-03-07
 */
@Service
public class UserCoinServiceImpl extends ServiceImpl<UserCoinMapper, UserCoin> implements IUserCoinService {

  @Autowired
  private IUserCoinLogService userCoinLogService;

  @Autowired
  private DistributedLocker distributedLocker;

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void produce(Long userId, Integer logCoin, String bizId, CoinLogTypeEnum coinLogTypeEnum) {
    UserCoin userCoin = getById(userId);
    if (null == userCoin) {
      userCoin = new UserCoin();
      userCoin.setUserId(userId);
      userCoin.setCoin(logCoin);
      save(userCoin);
    } else {
      boolean rtn = update(new LambdaUpdateWrapper<UserCoin>().set(UserCoin::getCoin, userCoin.getCoin() + logCoin)
          .eq(UserCoin::getUserId, userId).eq(UserCoin::getCoin, userCoin.getCoin()));
      Assert.isTrue(rtn, "更新盲豆余额失败。userId:" + userId);
      userCoin.setCoin(userCoin.getCoin() + logCoin);
    }

    UserCoinLog userCoinLog = new UserCoinLog();
    userCoinLog.setUserId(userId);
    userCoinLog.setType(coinLogTypeEnum);
    userCoinLog.setMoney(userCoin.getCoin());
    userCoinLog.setLogMoney(logCoin);
    userCoinLog.setIncomeExpense(CoinLogTypeEnum.INCOME);
    userCoinLog.setLogText(coinLogTypeEnum.getDesc());
    userCoinLog.setBizTime(new Date());
    userCoinLog.setRefId(bizId);
    userCoinLogService.save(userCoinLog);
  }

  @Override
  @Transactional
  public void consume(Long userId, Integer logCoin, String bizId) {
    UserCoin userCoin = getById(userId);

    boolean rtn = update(new LambdaUpdateWrapper<UserCoin>().set(UserCoin::getCoin, userCoin.getCoin() - logCoin)
          .eq(UserCoin::getUserId, userId).eq(UserCoin::getCoin, userCoin.getCoin()));
    Assert.isTrue(rtn, "更新盲豆余额失败。userId:" + userId);

    UserCoinLog userCoinLog = new UserCoinLog();
    userCoinLog.setUserId(userId);
    userCoinLog.setType(CoinLogTypeEnum.EXCHANGE);
    userCoinLog.setMoney(userCoin.getCoin() - logCoin);
    userCoinLog.setLogMoney(0 - logCoin);
    userCoinLog.setIncomeExpense(CoinLogTypeEnum.EXPENSES);
    userCoinLog.setLogText("兑换商品");
    userCoinLog.setBizTime(new Date());
    userCoinLog.setRefId(bizId);
    userCoinLogService.save(userCoinLog);
  }

  @Override
  @Transactional
  public boolean transfer(CoinTransferParamDTO coinTransferParamDTO) {
    String LockKey = RedisLockKey.build(RedisLockKey.USER_COIN_TRANSFER_LOCK, coinTransferParamDTO.getFromUserId());
    if (!distributedLocker.tryLock(LockKey)) {
      throw new ServiceException("系统繁忙，请稍后再试");
    }
    try {
      // 获取转赠方拥有盲豆数量
      UserCoin userCoin = getById(coinTransferParamDTO.getFromUserId());
      if (userCoin.getCoin() < (coinTransferParamDTO.getRealTransferCoin() + coinTransferParamDTO.getDeductTransferCoin())) {
        throw new ServiceException("您的盲豆数量不足，不能转赠");
      }
      // 扣除转赠方盲豆
      boolean rtn1 = update(new LambdaUpdateWrapper<UserCoin>().set(UserCoin::getCoin, userCoin.getCoin() - (coinTransferParamDTO.getRealTransferCoin() + coinTransferParamDTO.getDeductTransferCoin()))
              .eq(UserCoin::getUserId, coinTransferParamDTO.getFromUserId()));
      Assert.isTrue(rtn1, "扣除转赠方盲豆失败, userId:" + coinTransferParamDTO.getFromUserId());
      // 增加受赠方盲豆
      UserCoin toUserCoin = getById(coinTransferParamDTO.getToUserId());
      if (toUserCoin == null) {
        // 受赠方从未获得过盲豆
        UserCoin toUserNoCoin = new UserCoin();
        toUserNoCoin.setCoin(coinTransferParamDTO.getRealTransferCoin());
        toUserNoCoin.setUserId(coinTransferParamDTO.getToUserId());
        toUserNoCoin.setCreatedTime(new Date());
        toUserNoCoin.setUpdatedTime(new Date());
        boolean rtn2 = save(toUserNoCoin);
        Assert.isTrue(rtn2, "从未获得过盲豆的用户转赠盲豆失败, userId:" + coinTransferParamDTO.getToUserId());
      } else {
        boolean rtn3 = update(new LambdaUpdateWrapper<UserCoin>().set(UserCoin::getCoin, toUserCoin.getCoin() + coinTransferParamDTO.getRealTransferCoin())
                .eq(UserCoin::getUserId, coinTransferParamDTO.getToUserId()));
        Assert.isTrue(rtn3, "增加受赠方盲豆失败，userId:" + coinTransferParamDTO.getToUserId());
      }

      // 盲豆转赠日志
      List<UserCoinLog> userCoinLogList = new ArrayList<>();
      UserCoinLog userCoinFromLog = new UserCoinLog();
      userCoinFromLog.setUserId(coinTransferParamDTO.getFromUserId());
      userCoinFromLog.setType(CoinLogTypeEnum.TRANSFER);
      userCoinFromLog.setMoney(userCoin.getCoin() - (coinTransferParamDTO.getRealTransferCoin() + coinTransferParamDTO.getDeductTransferCoin()));
      userCoinFromLog.setLogMoney(-(coinTransferParamDTO.getRealTransferCoin() + coinTransferParamDTO.getDeductTransferCoin()));
      userCoinFromLog.setIncomeExpense(CoinLogTypeEnum.EXPENSES);
      userCoinFromLog.setLogText("盲豆转赠：(" + coinTransferParamDTO.getToPhone() + ")" + "｜转赠折损：" + coinTransferParamDTO.getDeductTransferCoin() + "盲豆");
      userCoinFromLog.setBizTime(new Date());
      userCoinFromLog.setCreatedTime(new Date());
      userCoinFromLog.setUpdatedTime(new Date());
      userCoinLogList.add(userCoinFromLog);

      UserCoinLog userCoinToLog = new UserCoinLog();
      userCoinToLog.setUserId(coinTransferParamDTO.getToUserId());
      userCoinToLog.setType(CoinLogTypeEnum.TRANSFER);
      userCoinToLog.setMoney(toUserCoin == null ? coinTransferParamDTO.getRealTransferCoin() : toUserCoin.getCoin() + coinTransferParamDTO.getRealTransferCoin());
      userCoinToLog.setLogMoney(coinTransferParamDTO.getRealTransferCoin());
      userCoinToLog.setIncomeExpense(CoinLogTypeEnum.INCOME);
      userCoinToLog.setLogText("盲豆转赠：(" + coinTransferParamDTO.getFromPhone() + ")");
      userCoinToLog.setBizTime(new Date());
      userCoinToLog.setCreatedTime(new Date());
      userCoinToLog.setUpdatedTime(new Date());
      userCoinLogList.add(userCoinToLog);
      userCoinLogService.saveBatch(userCoinLogList);
    } finally {
      distributedLocker.unlock(LockKey);
    }
    return true;
  }
}
