package com.red.circle.other.infra.database.mongo.service.activity.impl;

import com.google.common.collect.Lists;
import com.mongodb.client.result.UpdateResult;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.mongodb.page.MongoPageHelper;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.other.infra.database.mongo.entity.activity.RoomContributionActivityCount;
import com.red.circle.other.infra.database.mongo.service.activity.RoomContributionActivityCountService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.LocalDateUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.text.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

/**
 * 房间贡献活动.
 *
 * @author pengshigang on 2022/10/26
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RoomContributionActivityCountServiceImpl implements
    RoomContributionActivityCountService {

  private final MongoTemplate mongoTemplate;
  private final MongoPageHelper mongoPageHelper;
  private static final BigDecimal integralMin = BigDecimal.valueOf(100000);
  private static final BigDecimal otherIntegralMin = BigDecimal.valueOf(150000);

  private Criteria getUniquenessCriteria(Long roomId, Integer dateNumber) {
    return Criteria.where("id").is(getUniqueId(roomId, dateNumber));
  }

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

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

    Integer dateNumber = ZonedDateTimeAsiaRiyadhUtils.getThisMonday();

    if (mongoTemplate.exists(Query.query(getUniquenessCriteria(roomId, dateNumber)),
        RoomContributionActivityCount.class)) {

      mongoTemplate.updateFirst(Query.query(getUniquenessCriteria(roomId, dateNumber)),
          new Update()
              .inc("contributionValue", quantity),
          RoomContributionActivityCount.class);
      return;
    }
    addData(roomId, quantity, dateNumber);
  }

  @Override
  public List<RoomContributionActivityCount> listRoomContribution(Long roomId,
      SysOriginPlatformEnum sysOrigin) {

    return mongoTemplate
        .find(Query.query(Criteria.where("roomId").is(roomId)
                .and("received").is(Boolean.FALSE)
                .and("dateNumber").lt(ZonedDateTimeAsiaRiyadhUtils.getThisMonday())
                .and("contributionValue").gte(
                    Objects.equals(sysOrigin, SysOriginPlatformEnum.TARAB) ? otherIntegralMin
                        : integralMin)),
            RoomContributionActivityCount.class);
  }

  @Override
  public RoomContributionActivityCount getThisWeekData(Long roomId) {
    return mongoTemplate
        .findOne(Query.query(Criteria.where("roomId").is(roomId)
                .and("dateNumber").is(ZonedDateTimeAsiaRiyadhUtils.getThisMonday())),
            RoomContributionActivityCount.class);
  }


  @Override
  public void updateReceivedByRoomId(Long roomId, BigDecimal ratio) {

    // 将之前的数据全部修改成已领取状态
    mongoTemplate.updateMulti(Query.query(Criteria.where("roomId").is(roomId)
            .and("received").is(Boolean.FALSE)
            .and("dateNumber").lt(LocalDateUtils.nowToInteger())),
        new Update().set("received", Boolean.TRUE)
            .set("balance", 0)
            .set("ratio", ratio),
        RoomContributionActivityCount.class);
  }

  @Override
  public void updateReceivedByList(List<ImmutablePair<String, BigDecimal>> list, BigDecimal ratio) {

    if (CollectionUtils.isEmpty(list)) {
      return;
    }

    list.forEach(data -> {

      if (StringUtils.isBlank(data.getLeft()) || Objects.isNull(data.getRight())) {
        return;
      }

      // 将之前的数据全部修改成已领取状态
      mongoTemplate.updateFirst(Query.query(Criteria.where("id").is(data.getLeft())
              .and("received").is(Boolean.FALSE)
              .and("dateNumber").lt(LocalDateUtils.nowToInteger())),
          new Update().set("received", Boolean.TRUE)
              .set("balance", data.getRight())
              .set("ratio", ratio),
          RoomContributionActivityCount.class);
    });
  }

  @Override
  public BigDecimal getAvailableAmount(SysOriginPlatformEnum sysOrigin,String regionCode, List<BigDecimal> integrals,
      BigDecimal ratio) {

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

    BigDecimal availableAmount = BigDecimal.ZERO;
    for (BigDecimal integral : integrals) {
      availableAmount = availableAmount.add(calculateAvailableAmount(sysOrigin,regionCode, ratio, integral));
    }
    return availableAmount;
  }

  @Override
  public List<ImmutablePair<String, BigDecimal>> listAvailableAmount(
      SysOriginPlatformEnum sysOrigin,String userRegionCode, List<ImmutablePair<String, BigDecimal>> integrals,
      BigDecimal ratio) {

    if (CollectionUtils.isEmpty(integrals)) {
      return Lists.newArrayList();
    }

    return integrals.stream().map(integral -> ImmutablePair
            .of(integral.getLeft(), calculateAvailableAmount(sysOrigin,userRegionCode, ratio, integral.getRight())))
        .collect(Collectors.toList());

  }

  private BigDecimal calculateAvailableAmount(SysOriginPlatformEnum sysOrigin,String regionCode, BigDecimal ratio,
      BigDecimal integral) {

    if (BigDecimal.ZERO.compareTo(integral) >= 0) {
      return BigDecimal.ZERO;
    }

    for (BigDecimal ruleValue : getRuleValues(sysOrigin,regionCode)) {

      if (integral.compareTo(ruleValue) >= 0) {
//        if(Objects.equals(regionCode,"PH")){
//          return integral.multiply(ratio).setScale(0, RoundingMode.DOWN);
//        }
        return ruleValue.multiply(ratio).setScale(0, RoundingMode.DOWN);
      }
    }
    return BigDecimal.ZERO;
  }

  private static List<BigDecimal> getRuleValues(SysOriginPlatformEnum sysOrigin,String regionCode) {

    if (Objects.equals(sysOrigin, SysOriginPlatformEnum.YOLO)) {

      return Stream.of(integralMin,
                      BigDecimal.valueOf(100000),
                      BigDecimal.valueOf(200000),
                      BigDecimal.valueOf(300000),
                      BigDecimal.valueOf(500000),
                      BigDecimal.valueOf(700000),
                      BigDecimal.valueOf(1000000),
                      BigDecimal.valueOf(1500000),
                      BigDecimal.valueOf(2000000),
                      BigDecimal.valueOf(2500000),
                      BigDecimal.valueOf(3000000),
                      BigDecimal.valueOf(3500000),
                      BigDecimal.valueOf(4000000),
                      BigDecimal.valueOf(4500000),
                      BigDecimal.valueOf(5000000),
                      BigDecimal.valueOf(6000000),
                      BigDecimal.valueOf(8000000),
                      BigDecimal.valueOf(10000000),
                      BigDecimal.valueOf(12000000),
                      BigDecimal.valueOf(15000000),
                      BigDecimal.valueOf(18000000),
                      BigDecimal.valueOf(20000000),
                      BigDecimal.valueOf(30000000)).sorted(Comparator.reverseOrder())
              .collect(Collectors.toList());
    }
    List<BigDecimal> list = Stream.of(otherIntegralMin,
            BigDecimal.valueOf(300000),
            BigDecimal.valueOf(500000),
            BigDecimal.valueOf(700000),
            BigDecimal.valueOf(1000000),
            BigDecimal.valueOf(1500000),
            BigDecimal.valueOf(2000000),
            BigDecimal.valueOf(2500000),
            BigDecimal.valueOf(3000000),
            BigDecimal.valueOf(3500000),
            BigDecimal.valueOf(4000000),
            BigDecimal.valueOf(4500000),
            BigDecimal.valueOf(5000000),
            BigDecimal.valueOf(6000000),
            BigDecimal.valueOf(8000000),
            BigDecimal.valueOf(10000000),
            BigDecimal.valueOf(12000000),
            BigDecimal.valueOf(15000000),
            BigDecimal.valueOf(18000000),
            BigDecimal.valueOf(20000000),
            BigDecimal.valueOf(30000000)).sorted(Comparator.reverseOrder())
        .collect(Collectors.toList());
    return list;

  }


  /**
   * 添加
   */
  private void addData(Long roomId, BigDecimal value, Integer dateNumber) {

    RoomContributionActivityCount count = new RoomContributionActivityCount();
    count.setId(getUniqueId(roomId, dateNumber));
    count.setRoomId(roomId);
    count.setContributionValue(value);
    count.setDateNumber(dateNumber);
    count.setCreateTime(TimestampUtils.now());
    count.setExpiredTime(TimestampUtils.nowPlusDays(180));
    count.setReceived(Boolean.FALSE);
    mongoTemplate.save(count);
  }

  private String getUniqueId(Long roomId, Integer dateNumber) {
    return roomId + "_" + dateNumber;
  }

  @Override
  public PageResult<RoomContributionActivityCount> pageReceived(long pageNumber, long pageSize,
      Long roomId) {

    Criteria criteria = Criteria.where("received").is(Boolean.TRUE);
    if (Objects.nonNull(roomId)) {
      criteria.and("roomId").is(roomId);
    }

    return mongoPageHelper
        .query(new Query(criteria), RoomContributionActivityCount.class, (int) pageSize,
            (int) pageNumber, Sort.Order.desc("createTime"));
  }

  @Override
  public List<RoomContributionActivityCount> listRoomContributionByPH(Long roomId,BigDecimal number) {

    return mongoTemplate
        .find(Query.query(Criteria.where("roomId").is(roomId)
                .and("received").is(Boolean.FALSE)
                .and("dateNumber").lt(ZonedDateTimeAsiaRiyadhUtils.getThisMonday())
                .and("contributionValue").gte(number)),
            RoomContributionActivityCount.class);
  }

    @Override
    public boolean updateRewardCoinsSent(String id) {
        Query query = new Query(Criteria.where("_id").is(id));
        Update update = new Update().set("rewardCoinsSent", true);
        UpdateResult result = mongoTemplate.updateFirst(query, update, RoomContributionActivityCount.class);

        // 检查是否有文档被修改
        return result.getModifiedCount() > 0;
    }

    @Override
  public List<RoomContributionActivityCount> getAllRoomsLastWeekData() {
    // 获取上周的 dateNumber
    Integer lastWeekDateNumber = ZonedDateTimeAsiaRiyadhUtils.getThisMonday() - 7;
    
    Criteria criteria = Criteria.where("dateNumber").is(lastWeekDateNumber)
        .and("received").is(Boolean.FALSE);
    
    return mongoTemplate.find(Query.query(criteria), RoomContributionActivityCount.class);
  }

  @Override
  public Optional<RoomContributionActivityCount> findByRoomIdAndDateNumber(Long roomId, Integer dateNumber) {
    Criteria criteria = Criteria.where("roomId").is(roomId)
        .and("dateNumber").is(dateNumber);
    
    RoomContributionActivityCount result = mongoTemplate.findOne(
        Query.query(criteria), 
        RoomContributionActivityCount.class
    );
    
    return Optional.ofNullable(result);
  }

}
