package com.red.circle.other.infra.database.rds.service.live.impl;

import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.other.infra.database.rds.dao.live.RoomContributionBalanceDAO;
import com.red.circle.other.infra.database.rds.entity.live.RoomContributionBalance;
import com.red.circle.other.infra.database.rds.service.live.RoomContributionBalanceService;
import java.math.BigDecimal;
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 pengliang
 * @since 2021-03-01
 */
@Slf4j
@Service
public class RoomContributionBalanceServiceImpl extends
    BaseServiceImpl<RoomContributionBalanceDAO, RoomContributionBalance> implements
    RoomContributionBalanceService {

  @Override
  public BigDecimal getTotalQuantityByUserId(Long userId) {
    return Optional.ofNullable(
            query().eq(RoomContributionBalance::getUserId, userId).last(PageConstant.LIMIT_ONE)
                .getOne())
        .map(RoomContributionBalance::getTotalQuantity)
        .orElse(BigDecimal.ZERO);
  }

  @Override
  public BigDecimal getTotalQuantityRoomId(Long roomId) {
    return Optional.ofNullable(getByRoomId(roomId))
        .map(RoomContributionBalance::getTotalQuantity)
        .orElse(BigDecimal.ZERO);
  }

  @Override
  public BigDecimal getBalanceQuantity(Long roomId) {
    return Optional.ofNullable(getByRoomId(roomId))
        .map(RoomContributionBalance::calculationBalance)
        .orElse(BigDecimal.ZERO);
  }

  @Override
  public RoomContributionBalance getByRoomId(Long roomId) {
    return query()
        .eq(RoomContributionBalance::getRoomId, roomId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();
  }

  @Override
  public void incrTotalQuantity(Long roomId, Long userId, BigDecimal quantity) {

    if (quantity.compareTo(BigDecimal.ZERO) <= 0) {
      return;
    }

    boolean isExists = roomExists(roomId);
    if (isExists) {
      incrTotal(roomId, quantity);
      return;
    }
    try {
      save(new RoomContributionBalance()
          .setRoomId(roomId)
          .setUserId(userId)
          .setTotalQuantity(quantity)
      );
    } catch (DuplicateKeyException ignore) {
      log.error("room.incrTotalQuantity DuplicateKey: {},{},{}", roomId, userId, quantity);
    }
  }

  private void incrTotal(Long roomId, BigDecimal quantity) {
    update().setSql("total_quantity=total_quantity+" + quantity)
        .eq(RoomContributionBalance::getRoomId, roomId)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public boolean incrWithdrawQuantity(Long roomId, BigDecimal quantity) {
    return update().setSql("withdraw_quantity=withdraw_quantity+" + quantity)
        .eq(RoomContributionBalance::getRoomId, roomId)
        .apply("total_quantity>=withdraw_quantity+" + quantity)
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public void correctionData(Long roomId, BigDecimal quantity) {

    if (Objects.isNull(roomId) || Objects.isNull(quantity) || quantity.longValue() <= 0) {
      return;
    }
    RoomContributionBalance balance = query().eq(RoomContributionBalance::getRoomId, roomId)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

    if (Objects.isNull(balance)) {
      return;
    }

    BigDecimal totalQuantity = balance.getTotalQuantity().compareTo(quantity) > 0
        ? balance.getTotalQuantity().subtract(quantity) : BigDecimal.ZERO;

    update().setSql("total_quantity=" + totalQuantity)
        .eq(RoomContributionBalance::getId, balance.getId())
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  @Override
  public void correctData(Long oldRoomId, Long newRoomId, BigDecimal val) {

    RoomContributionBalance oldRoom = query().eq(RoomContributionBalance::getRoomId, oldRoomId)
        .last(PageConstant.LIMIT_ONE).getOne();
    if (Objects.isNull(oldRoom)) {
      return;
    }

    RoomContributionBalance newRoom = query().eq(RoomContributionBalance::getRoomId, newRoomId)
        .last(PageConstant.LIMIT_ONE).getOne();
    if (Objects.isNull(newRoom)) {
      return;
    }

    String oldSql = String.format("total_quantity-%s", val);
    update().setSql(String.format("total_quantity=IF((%s) >= 0, %s, 0)", oldSql, oldSql))
        .eq(RoomContributionBalance::getId, oldRoom.getId())
        .last(PageConstant.LIMIT_ONE)
        .execute();

    update().setSql("total_quantity=total_quantity+" + val)
        .eq(RoomContributionBalance::getId, newRoom.getId())
        .last(PageConstant.LIMIT_ONE)
        .execute();
  }

  private Boolean roomExists(Long roomId) {
    return Optional.ofNullable(query().select(RoomContributionBalance::getId)
            .eq(RoomContributionBalance::getRoomId, roomId)
            .last(PageConstant.LIMIT_ONE)
            .getOne())
        .map(roomContributionBalance -> Objects.nonNull(roomContributionBalance.getId()))
        .orElse(Boolean.FALSE);
  }
}
