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

import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.component.redis.annotation.TaskCacheLock;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.DateUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.wallet.domain.wallet.WalletErrorLogReason;
import com.red.circle.wallet.infra.database.cache.service.WalletGoldCacheService;
import com.red.circle.wallet.infra.database.rds.entity.WalletExceptionLog;
import com.red.circle.wallet.infra.database.rds.entity.WalletGoldAssetRecord;
import com.red.circle.wallet.infra.database.rds.entity.WalletRemarks;
import com.red.circle.wallet.infra.database.rds.service.WalletExceptionLogService;
import com.red.circle.wallet.infra.database.rds.service.WalletGoldAssetRecordService;
import com.red.circle.wallet.infra.database.rds.service.WalletGoldBalanceService;
import com.red.circle.wallet.infra.database.rds.service.WalletRemarksService;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 检查钱包，金币错误.
 *
 * @author pengliang on 2023/7/17
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CheckWalletGoldErrorTask {

  private final WalletRemarksService walletRemarksService;
  private final WalletGoldCacheService walletGoldCacheService;
  private final WalletGoldBalanceService walletGoldBalanceService;
  private final WalletExceptionLogService walletExceptionLogService;
  private final WalletGoldAssetRecordService walletGoldAssetRecordService;

  /**
   * 3分钟检查一次.
   */
  @Scheduled(cron = "0 */3 * * * ?")
  @TaskCacheLock(key = "CHECK_WALLET_GOLD_EXECUTE", expireSecond = 60 * 3)
  public void execute() {
    int processSize = 10000;
    // 处理缓存中记录
    executeProcess(walletGoldCacheService.popErrorLog(processSize));
    // 处理db中记录
    executeProcess(walletExceptionLogService.listPending(processSize));
  }

  private void executeProcess(List<WalletExceptionLog> exceptionLogs) {
    Map<String, List<WalletExceptionLog>> reasonGroup = toMapWalletExceptionLog(exceptionLogs);
    if (CollectionUtils.isEmpty(reasonGroup)) {
      return;
    }
    reasonGroup.forEach((key, val) -> process(WalletErrorLogReason.valueOf(key), val));
  }

  private Map<String, List<WalletExceptionLog>> toMapWalletExceptionLog(
      List<WalletExceptionLog> exceptionLogs) {
    if (CollectionUtils.isEmpty(exceptionLogs)) {
      return null;
    }
    return exceptionLogs.stream()
        .collect(Collectors.groupingBy(WalletExceptionLog::getReason));
  }

  private void process(WalletErrorLogReason reason, List<WalletExceptionLog> valueLogs) {
    if (CollectionUtils.isEmpty(valueLogs)) {
      return;
    }

    // 金币刷新
    if (Objects.equals(reason, WalletErrorLogReason.REST_BALANCE)) {
      processBalance(valueLogs);
      return;
    }

    // 保存资产流水
    if (Objects.equals(reason, WalletErrorLogReason.SAVE_ASSET_RECORD)) {
      processAssetRecord(valueLogs);
      return;
    }

    // 保存备注
    if (Objects.equals(reason, WalletErrorLogReason.SAVE_REMARK)) {
      processRemarks(valueLogs);
    }
  }

  private void processAssetRecord(List<WalletExceptionLog> valueLogs) {
    List<WalletGoldAssetRecord> goldAssetRecords = toWalletGoldAssetRecords(valueLogs);

    if (CollectionUtils.isNotEmpty(goldAssetRecords)) {
      List<List<WalletGoldAssetRecord>> walletRemarkPartitions = CollectionUtils.partition(
          goldAssetRecords,
          500);

      for (List<WalletGoldAssetRecord> remarks : walletRemarkPartitions) {
        walletGoldAssetRecordService.saveBatch(remarks);
        removeExecLogs(valueLogs);
      }

    }
  }

  private void removeExecLogs(List<WalletExceptionLog> valueLogs) {
    walletExceptionLogService.removeByIds(
        valueLogs.stream().map(WalletExceptionLog::getId).toList());
  }

  private List<WalletGoldAssetRecord> toWalletGoldAssetRecords(
      List<WalletExceptionLog> valueLogs) {
    return valueLogs.stream().map(this::toWalletGoldAssetRecord).toList();
  }

  private void processRemarks(List<WalletExceptionLog> valueLogs) {
    List<WalletRemarks> walletRemarks = toWalletRemarks(valueLogs);
    if (CollectionUtils.isNotEmpty(walletRemarks)) {
      List<List<WalletRemarks>> walletRemarkPartition = CollectionUtils.partition(walletRemarks,
          500);
      for (List<WalletRemarks> remarks : walletRemarkPartition) {
        // 过滤兼容错误数据
        List<WalletRemarks> saveRemarks = remarks.stream()
            .filter(item -> StringUtils.isNotBlank(item.getBizId())).toList();
        if (CollectionUtils.isEmpty(saveRemarks)) {
          continue;
        }
        walletRemarksService.saveBatch(saveRemarks);
        removeExecLogs(valueLogs);
      }
    }
  }

  private List<WalletRemarks> toWalletRemarks(List<WalletExceptionLog> valueLogs) {
    return valueLogs.stream().map(this::toWalletRemarks).toList();
  }

  private void processBalance(List<WalletExceptionLog> valueLogs) {
    for (WalletExceptionLog valueLog : valueLogs) {
      if (checkTypeIncome(valueLog.getReceiptType())) {
        walletGoldBalanceService.incrIncome(valueLog.getUserId(),
            valueLog.getPennyAmount());
      }

      if (checkExpenditure(valueLog.getReceiptType())) {
        walletGoldBalanceService.incrExpenditure(valueLog.getUserId(),
            valueLog.getPennyBalance());
      }
      walletExceptionLogService.removeById(valueLog.getId());
    }
  }

  private WalletRemarks toWalletRemarks(WalletExceptionLog receipt) {
    WalletRemarks remarks = new WalletRemarks()
        .setId(receipt.getId())
        .setBizId(Objects.toString(receipt.getAssetRecordId()))
        .setBizType(receipt.getBizType())
        .setContent(receipt.getRemark());
    remarks.setCreateTime(receipt.getCreateTime());
    remarks.setUpdateTime(DateUtils.now());
    return remarks;
  }

  private WalletGoldAssetRecord toWalletGoldAssetRecord(WalletExceptionLog receipt) {
    WalletGoldAssetRecord assetRecord = new WalletGoldAssetRecord()
        .setId(receipt.getAssetRecordId())
        .setSysOrigin(receipt.getSysOrigin())
        .setUserId(receipt.getUserId())
        .setPennyAmount(receipt.getPennyAmount())
        .setPennyBalance(receipt.getPennyBalance())
        .setType(receipt.getReceiptType())
        .setEventType(receipt.getEventType())
        .setEventDesc(receipt.getEventDesc())
        .setEventId(receipt.getEventId())
        .setOpUserType(receipt.getOpUserType());
    assetRecord.setCreateTime(receipt.getCreateTime());
    assetRecord.setUpdateTime(DateUtils.now());
    return assetRecord;
  }

  private boolean checkTypeIncome(Integer receiptType) {
    return Objects.equals(receiptType, ReceiptType.INCOME.getType());
  }

  private boolean checkExpenditure(Integer receiptType) {
    return Objects.equals(receiptType, ReceiptType.EXPENDITURE.getType());
  }


}
