package com.red.circle.wallet.infra.database.rds.service.bean.impl;

import com.red.circle.common.business.dto.cmd.PageUserIdCmd;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.wallet.infra.database.rds.dao.BeanBalanceDAO;
import com.red.circle.wallet.infra.database.rds.entity.bean.BeanBalance;
import com.red.circle.wallet.infra.database.rds.service.bean.BeanBalanceService;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 用户豆子余额 服务实现类.
 * </p>
 *
 * @author pengshigang
 * @since 2021-10-14
 */
@Slf4j
@Service
public class BeanBalanceServiceImpl extends BaseServiceImpl<BeanBalanceDAO, BeanBalance> implements
    BeanBalanceService {

  @Override
  public boolean incr(String sysOrigin, Long userId, BigDecimal quantity) {
    BeanBalance balance = getById(userId);

    if (Objects.isNull(balance)) {
      try {
        return save(new BeanBalance()
            .setSysOrigin(sysOrigin)
            .setUserId(userId)
            .setEarnPoints(quantity)
        );
      } catch (DuplicateKeyException ignore) {
        // ignore
      }
    }

    return update().set(BeanBalance::getUpdateTime, TimestampUtils.now())
        .setSql("earn_points=earn_points+" + quantity)
        .eq(BeanBalance::getUserId, userId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }


  @Override
  public boolean decr(String sysOrigin, Long userId, BigDecimal quantity) {
    return update().set(BeanBalance::getUpdateTime, TimestampUtils.now())
        .setSql("consumption_points=consumption_points+" + quantity)
        .apply("earn_points>=consumption_points+" + quantity)
        .eq(BeanBalance::getUserId, userId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public BigDecimal getBalance(Long userId) {
    return Optional.ofNullable(getById(userId))
        .map(balance -> balance.getEarnPoints()
            .subtract(balance.getConsumptionPoints())
            .setScale(2, RoundingMode.DOWN))
        .orElse(BigDecimal.ZERO);
  }

  @Override
  public boolean isBalanceSufficient(Long userId, BigDecimal consumeCandy) {
    return Optional.ofNullable(getBalance(userId))
        .map(beanBalance -> ArithmeticUtils.gte(beanBalance, consumeCandy))
        .orElse(Boolean.FALSE);
  }

  @Override
  public PageResult<BeanBalance> pageBeanBalance(PageUserIdCmd cmd) {
    return query()
        .eq(Objects.nonNull(cmd.getUserId()), BeanBalance::getUserId, cmd.getUserId())
        .page(cmd.getPageQuery());
  }
}
