package com.red.circle.console.app.scheduler;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.redis.annotation.TaskCacheLock;
import com.red.circle.console.infra.database.mongo.count.dto.CurrencyAnalyzeItem;
import com.red.circle.console.infra.database.mongo.count.dto.CurrencyAnalyzeUser;
import com.red.circle.console.infra.database.mongo.count.dto.CurrencyAnalyzeUserItem;
import com.red.circle.console.infra.database.mongo.count.dto.GoldRunningTypeCount;
import com.red.circle.console.infra.database.mongo.count.dto.query.GoldAnalyzeQuery;
import com.red.circle.console.infra.database.mongo.count.entity.CountGoldOriginDailyDetails;
import com.red.circle.console.infra.database.mongo.count.entity.CountUserDailyRechargeRank;
import com.red.circle.console.infra.database.mongo.count.entity.CurrencyAnalyzeUserLog;
import com.red.circle.console.infra.database.mongo.count.entity.CurrencyAnalyzeUserLogItem;
import com.red.circle.console.infra.database.mongo.count.service.CountGoldOriginDailyDetailsService;
import com.red.circle.console.infra.database.mongo.count.service.CountUserDailyRechargeRankService;
import com.red.circle.console.infra.database.mongo.count.service.CurrencyAnalyzeUserLogService;
import com.red.circle.console.infra.database.mongo.count.service.UserGoldRunningWaterService;
import com.red.circle.console.infra.database.rds.entity.count.StatisticsDailyCurrencyCountPlatform;
import com.red.circle.console.infra.database.rds.service.count.StatisticsDailyCurrencyCountPlatformService;
import com.red.circle.order.inner.endpoint.InAppPurchaseDetailsClient;
import com.red.circle.order.inner.model.dto.inapp.CountUserInAppPurchaseDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.collection.CollectorsUtils;
import com.red.circle.tool.core.date.LocalDateTimeUtils;
import com.red.circle.tool.core.date.LocalDateUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 统计平台货币流通情况.
 *
 * @author pengliang on 2022/8/2
 */
@Slf4j
@Component
@AllArgsConstructor
public class CountDailyCurrencyTask {

  private final UserGoldRunningWaterService userGoldRunningWaterService;
  private final InAppPurchaseDetailsClient inAppPurchaseDetailsService;
  private final CurrencyAnalyzeUserLogService currencyAnalyzeUserLogService;
  private final CountUserDailyRechargeRankService countUserDailyRechargeRankService;
  private final CountGoldOriginDailyDetailsService countGoldOriginDailyDetailsService;
  private final StatisticsDailyCurrencyCountPlatformService statisticsDailyCurrencyCountPlatformService;

  /**
   * 每天8点执行,统计货币情况.
   */
  @Scheduled(cron = "0 0 8 * * ?", zone = "Asia/Shanghai")
  @TaskCacheLock(key = "COUNT_DAILY_CURRENCY", expireSecond = 60 * 60)
  public void countDailyCurrency() {
    long startTime = System.currentTimeMillis();
    log.info("exec count_daily_currency start");
    LocalDate bizDate = LocalDate.now().minusDays(1);

    List<GoldRunningTypeCount> goldRunningTypeCounts = userGoldRunningWaterService.countSysOriginGroupType(
        bizDate, LocalDate.now());

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

    statisticsDailyCurrencyCountPlatformService.saveBatch(
        goldRunningTypeCounts.stream().map(count -> {
          var currencyCountPlatform = new StatisticsDailyCurrencyCountPlatform()
              .setDateNumber(
                  Integer.valueOf(LocalDateUtils.format(bizDate, "yyyyMMdd")))
              .setSysOrigin(count.getSysOrigin())
              .setCurrencyType("GOLD")
              .setType(count.getType())
              .setQuantity(count.getQuantity())
              .setStatisticsTime(TimestampUtils.nowMinusDays(1));
          currencyCountPlatform.setCreateTime(TimestampUtils.now());
          currencyCountPlatform.setUpdateTime(TimestampUtils.now());
          return currencyCountPlatform;
        }).collect(Collectors.toList()));
    log.info("exec count_daily_currency end with {}",System.currentTimeMillis()-startTime);
  }


  /**
   * 每天8点执行，统计金币详情.
   */
  @Scheduled(cron = "0 0 8 * * ?", zone = "Asia/Shanghai")
  @TaskCacheLock(key = "COUNT_DAILY_GOLD_DETAILS", expireSecond = 60 * 60)
  public void countDailyGoldDetails() {
    long startTime = System.currentTimeMillis();
    log.info("exec count_daily_gold_details start");
    LocalDateTime bizDate = LocalDateTime.now().minusDays(1);
    Integer bizDateNumber = Integer.valueOf(LocalDateTimeUtils.format(bizDate, "yyyyMMdd"));
    LocalDateTime endTime = LocalDateTime.now();

    SysOriginPlatformEnum.getVoiceSystems()
        .forEach(sysOrigin -> {
          saveGoldOriginDailyDetails(bizDate, bizDateNumber, endTime, sysOrigin, 0);
          saveGoldOriginDailyDetails(bizDate, bizDateNumber, endTime, sysOrigin, 1);
        });
    log.info("exec count_daily_gold_details end with {}",System.currentTimeMillis()-startTime);
  }

  /**
   * 每天8点执行，统计金币收支用户每日top榜单.
   */
  @Scheduled(cron = "0 0 8 * * ?", zone = "Asia/Shanghai")
  @TaskCacheLock(key = "COUNT_GOLD_RUNNING_WATER_TOP_SIZE", expireSecond = 60 * 60)
  public void countGoldRunningWaterTopSize() {
    long startTime = System.currentTimeMillis();
    log.info("exec count_gold_running_water_top_size start");
    LocalDateTime bizDate = LocalDateTime.now().minusDays(1);
    LocalDateTime endTime = LocalDateTime.now();
    SysOriginPlatformEnum.getVoiceSystems()
        .forEach(sysOrigin -> saveCurrencyAnalyzeUser(sysOrigin, bizDate, endTime));
    log.info("exec count_gold_running_water_top_size end with {}",System.currentTimeMillis()-startTime);
  }


  /**
   * 每天8点执行，统计充值top排行榜.
   */
  @Scheduled(cron = "0 0 8 * * ?", zone = "Asia/Shanghai")
  @TaskCacheLock(key = "COUNT_DAILY_RECHARGE_RANK", expireSecond = 60 * 60)
  public void countDailyRechargeRank() {
    long startTime = System.currentTimeMillis();
    log.info("exec count_daily_recharge_rank start");
    LocalDateTime bizDate = LocalDateTime.now().minusDays(1);
    LocalDateTime endTime = LocalDateTime.now();
    SysOriginPlatformEnum.getVoiceSystems()
        .forEach(sysOrigin -> saveCountDailyRechargeRank(sysOrigin, bizDate, endTime));
    log.info("exec count_daily_recharge_rank end with {}",System.currentTimeMillis()-startTime);
  }

  private void saveCountDailyRechargeRank(SysOriginPlatformEnum sysOrigin, LocalDateTime start,
      LocalDateTime endTime) {
    List<CountUserInAppPurchaseDTO> inAppPurchases = inAppPurchaseDetailsService.countUserInAppPurchase(
        sysOrigin.name(), start, endTime, 50).getBody();
    if (CollectionUtils.isEmpty(inAppPurchases)) {
      return;
    }

    countUserDailyRechargeRankService.addBatch(
        inAppPurchases.stream().map(count ->new CountUserDailyRechargeRank()
                .setId(IdWorker.getIdStr())
                .setSysOrigin(sysOrigin.name())
                .setUserId(count.getUserId())
                .setQuantity(count.getAmount())
                .setDateNumber(Integer.valueOf(LocalDateTimeUtils.format(start, "yyyyMMdd")))
                .setCreateTime(TimestampUtils.now()))
            .collect(Collectors.toList())
    );
  }

  private void saveCurrencyAnalyzeUser(SysOriginPlatformEnum sysOrigin, LocalDateTime start,
      LocalDateTime end) {
    List<CurrencyAnalyzeUser> currencyAnalyzeUsers = userGoldRunningWaterService.countCurrencyAnalyzeUser(
        sysOrigin.name(), start, end, List.of("HKYS_GAME"), 50);
    if (CollectionUtils.isEmpty(currencyAnalyzeUsers)) {
      return;
    }

    currencyAnalyzeUserLogService.addBatch(currencyAnalyzeUsers.stream()
        .map(analyze ->new CurrencyAnalyzeUserLog()
            .setId(IdWorker.getIdStr())
            .setUserId(analyze.getUserId())
            .setSysOrigin(sysOrigin.name())
            .setQuantity(getTotalQuantity(analyze))
            .setDateNumber(Integer.valueOf(LocalDateTimeUtils.format(start, "yyyyMMdd")))
            .setItems(Optional.of(analyze.getItems())
                .map(items -> items.stream().map(item ->new CurrencyAnalyzeUserLogItem()
                    .setQuantity(item.getQuantity())
                    .setOriginName(item.getOriginName())
                    .setOrigin(item.getOrigin())
                    .setPercentage(item.getPercentage())
                    .setType(item.getType())).collect(Collectors.toList()))
                .orElseGet(Lists::newArrayList))
            .setGroupTypeQuantity(toGroupTypeQuantity(analyze))
            .setCreateTime(TimestampUtils.now()))
        .collect(Collectors.toList()));
  }

  private BigDecimal getTotalQuantity(CurrencyAnalyzeUser analyzeUser) {
    if (CollectionUtils.isEmpty(analyzeUser.getItems())) {
      return BigDecimal.ZERO;
    }
    return analyzeUser.getItems().stream().map(CurrencyAnalyzeUserItem::getQuantity)
        .filter(Objects::nonNull)
        .reduce(BigDecimal.ZERO, BigDecimal::add)
        .setScale(0, RoundingMode.DOWN);
  }

  private Map<Integer, BigDecimal> toGroupTypeQuantity(CurrencyAnalyzeUser analyzeUser) {
    if (CollectionUtils.isEmpty(analyzeUser.getItems())) {
      return Maps.newHashMap();
    }
    return analyzeUser.getItems().stream()
        .collect(Collectors.groupingBy(CurrencyAnalyzeUserItem::getType,
            CollectorsUtils.summingBigDecimal(CurrencyAnalyzeUserItem::getQuantity)));
  }

  private void saveGoldOriginDailyDetails(LocalDateTime bizDate,
      Integer bizDateNumber,
      LocalDateTime endTime,
      SysOriginPlatformEnum sysOrigin,
      Integer type) {
    GoldAnalyzeQuery query = new GoldAnalyzeQuery();
    query.setSysOrigin(sysOrigin.name());
    query.setType(type);
    query.setStartTime(bizDate);
    query.setEndTime(endTime);
    List<CurrencyAnalyzeItem> currencyAnalyzeItems = userGoldRunningWaterService.listGoldAnalyzeItem(
        query);
    if (CollectionUtils.isNotEmpty(currencyAnalyzeItems)) {
      countGoldOriginDailyDetailsService.addBatch(
          currencyAnalyzeItems.stream().map(item ->new CountGoldOriginDailyDetails()
                  .setId(IdWorker.getIdStr())
                  .setSysOrigin(sysOrigin.name())
                  .setDateNumber(bizDateNumber)
                  .setType(type)
                  .setQuantity(item.getValue())
                  .setOrigin(item.getOrigin())
                  .setOriginName(item.getOriginName())
                  .setPercentage(item.getPercentage())
                  .setCreateTime(TimestampUtils.now())
                  .setExpiredTime(TimestampUtils.nowPlusYear(1)))
              .collect(Collectors.toList())
      );
    }
  }

}
