package com.red.circle.order.infra.database.rds.service.count.impl;

import com.google.api.client.util.Maps;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.framework.mybatis.service.impl.BaseServiceImpl;
import com.red.circle.order.infra.database.rds.dao.count.UserMonthlyRechargeDAO;
import com.red.circle.order.infra.database.rds.entity.count.UserMonthlyRecharge;
import com.red.circle.order.infra.database.rds.service.count.UserMonthlyRechargeService;
import com.red.circle.order.inner.model.dto.MonthlyRechargeSummaryVO;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.LocalDateUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 累计用户每月充值
 * </p>
 *
 * @author pengshigang
 * @since 2021-06-19
 */
@Service
@RequiredArgsConstructor
public class UserMonthlyRechargeServiceImpl extends
    BaseServiceImpl<UserMonthlyRechargeDAO, UserMonthlyRecharge> implements
    UserMonthlyRechargeService {

    private final UserMonthlyRechargeDAO userMonthlyRechargeDAO;

  @Override
  public void incrThisMonthAmount(Long userId, BigDecimal amount, MonthlyRechargeType type) {
    UserMonthlyRecharge userMonthlyRecharge = query()
        .eq(UserMonthlyRecharge::getUserId, userId)
        .eq(UserMonthlyRecharge::getRechargeDate, nowThisMonth())
        .eq(UserMonthlyRecharge::getType, type)
        .last(PageConstant.LIMIT_ONE)
        .getOne();
    if (Objects.isNull(userMonthlyRecharge)) {
      save(new UserMonthlyRecharge()
          .setUserId(userId)
          .setAmount(amount)
          .setType(type.name())
          .setRechargeDate(nowThisMonth())
      );
      return;
    }
    update()
        .setSql("amount=amount+" + amount)
        .eq(UserMonthlyRecharge::getId, userMonthlyRecharge.getId())
        .execute();
  }

  @Override
  public void decrThisMonthAmount(Long userId, BigDecimal amount, MonthlyRechargeType type) {

    UserMonthlyRecharge userMonthlyRecharge = query()
        .eq(UserMonthlyRecharge::getUserId, userId)
        .eq(UserMonthlyRecharge::getRechargeDate, nowThisMonth())
        .eq(UserMonthlyRecharge::getType, type)
        .last(PageConstant.LIMIT_ONE)
        .getOne();

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

    update()
        .setSql("amount=amount-" + amount)
        .eq(UserMonthlyRecharge::getId, userMonthlyRecharge.getId())
        .last(PageConstant.LIMIT_ONE)
        .execute();

  }

  @Override
  public BigDecimal getThisMonthTotalPersonalRecharge(Long userId) {
    if (Objects.isNull(userId)) {
      return BigDecimal.ZERO;
    }

    List<MonthlyRechargeType> types = MonthlyRechargeType.getPersonalRecharge();
    if (CollectionUtils.isEmpty(types)) {
      return BigDecimal.ZERO;
    }
    List<UserMonthlyRecharge> userMonthlyRecharges = query()
        .eq(UserMonthlyRecharge::getUserId, userId)
        .eq(UserMonthlyRecharge::getRechargeDate, nowThisMonth())
        .in(UserMonthlyRecharge::getType, types)
        .last(PageConstant.formatLimit(types.size()))
        .list();

    if (CollectionUtils.isEmpty(userMonthlyRecharges)) {
      return BigDecimal.ZERO;
    }

    return userMonthlyRecharges.stream().map(UserMonthlyRecharge::getAmount)
        .reduce(BigDecimal.ZERO, BigDecimal::add)
        .setScale(2, RoundingMode.DOWN);
  }


  @Override
  public BigDecimal getTotalRechargeByUserId(Long userId) {
    return Optional.ofNullable(query().select(UserMonthlyRecharge::getAmount)
            .eq(UserMonthlyRecharge::getUserId, userId)
            .ge(UserMonthlyRecharge::getCreateTime, "2021-04-01")
            .last(PageConstant.formatLimit(1000))
            .list()).map(list -> list.stream().map(UserMonthlyRecharge::getAmount)
            .reduce(BigDecimal.ZERO, BigDecimal::add))
        .orElse(BigDecimal.ZERO);
  }

  @Override
  public Map<Long, List<UserMonthlyRecharge>> mapGroupByUserId(Set<Long> userIds,
      Integer rechargeDate) {

    if (CollectionUtils.isEmpty(userIds) || Objects.isNull(rechargeDate)) {
      return Maps.newHashMap();
    }

    return Optional.ofNullable(query()
            .in(UserMonthlyRecharge::getUserId, userIds)
            .eq(UserMonthlyRecharge::getRechargeDate, rechargeDate)
            .orderByAsc(UserMonthlyRecharge::getType)
            .list())
        .map(userTotalRecharges -> userTotalRecharges.stream()
            .collect(Collectors.groupingBy(UserMonthlyRecharge::getUserId)))
        .orElseGet(Maps::newHashMap);
  }

  @Override
  public Map<Integer, List<UserMonthlyRecharge>> mapGroupByRechargeDates(Long userId,
      Set<Integer> rechargeDates) {
    if (Objects.isNull(userId) || CollectionUtils.isEmpty(rechargeDates)) {
      return Maps.newHashMap();
    }
    return Optional.ofNullable(query()
            .eq(UserMonthlyRecharge::getUserId, userId)
            .in(UserMonthlyRecharge::getRechargeDate, rechargeDates)
            .orderByAsc(UserMonthlyRecharge::getType)
            .list())
        .map(userTotalRecharges -> userTotalRecharges.stream()
            .collect(Collectors.groupingBy(UserMonthlyRecharge::getRechargeDate)))
        .orElseGet(Maps::newHashMap);
  }

  @Override
  public List<UserMonthlyRecharge> listThisMonth(Long userId) {
    return query()
        .eq(UserMonthlyRecharge::getUserId, userId)
        .eq(UserMonthlyRecharge::getRechargeDate, nowThisMonth())
        .orderByAsc(UserMonthlyRecharge::getType)
        .list();
  }

    @Override
    public List<UserMonthlyRecharge> listThisMonthByIds(List<Long> userIds) {
        return query()
                .in(UserMonthlyRecharge::getUserId, userIds)
                .eq(UserMonthlyRecharge::getRechargeDate, nowThisMonth())
                .orderByAsc(UserMonthlyRecharge::getType)
                .list();
    }

    @Override
    public List<UserMonthlyRecharge> listLastMonthByIds(List<Long> userIds) {
        return query()
                .in(UserMonthlyRecharge::getUserId, userIds)
                .eq(UserMonthlyRecharge::getRechargeDate, lastMonth())
                .orderByAsc(UserMonthlyRecharge::getType)
                .list();
    }

    @Override
    public List<MonthlyRechargeSummaryVO> getMonthlyRechargeSummary(Set<Long> userIds) {
        return userMonthlyRechargeDAO.getMonthlyTotalRecharge(userIds);
    }

  private int nowThisMonth() {
    return LocalDateUtils.nowThisMonthToInteger();
  }

    private String lastMonth() {
      return LocalDate.now().minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM"));
    }

}
