package com.red.circle.wallet.app.command.bank.query;

import com.google.common.collect.Lists;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.parse.DataTypeUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.wallet.app.convertor.UserBankAppConvertor;
import com.red.circle.wallet.app.dto.clientobject.UserBankWaterCO;
import com.red.circle.wallet.app.dto.clientobject.UserBankWithdrawMoneyApplyCO;
import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankRunningWater;
import com.red.circle.wallet.infra.database.mongo.entity.bank.UserBankWithdrawGoldApply;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankRunningWaterService;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankWithdrawGoldApplyService;
import com.red.circle.wallet.infra.database.mongo.service.bank.UserBankWithdrawMoneyApplyService;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * @author pengliang on 2023/2/20
 */
@Component
@RequiredArgsConstructor
public class UserBankWaterQryExe {

  private final UserProfileClient userProfileClient;
  private final UserBankAppConvertor userBankAppConvertor;
  private final UserBankRunningWaterService userBankRunningWaterService;
  private final UserBankWithdrawGoldApplyService userBankWithdrawGoldApplyService;
  private final UserBankWithdrawMoneyApplyService userBankWithdrawMoneyApplyService;

  public List<UserBankWaterCO> execute(AppExtCommand cmd) {

    List<UserBankRunningWater> waters = userBankRunningWaterService.listLast(
        cmd.requiredReqUserId(), PageConstant.MAX_LIMIT_SIZE);

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

    Map<Long, UserBankWithdrawGoldApply> goldApplyMap = userBankWithdrawGoldApplyService.mapByIds(
        waters.stream().filter(apply -> Objects.equals(apply.getEvent(),
                UserBankWaterEvent.EXCHANGE_GOLD_COINS.name()))
            .map(UserBankRunningWater::getTrackId)
            .filter(StringUtils::isNotBlank)
            .map(DataTypeUtils::toLong)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet())
    );

    Map<Long, UserBankWithdrawMoneyApplyCO> moneyApplyMap = userBankAppConvertor.toMapUserBankWithdrawMoneyApplyCO(
        userBankWithdrawMoneyApplyService.mapByIds(
            waters.stream().filter(apply -> Objects.equals(apply.getEvent(),
                    UserBankWaterEvent.WITHDRAW.name()))
                .map(UserBankRunningWater::getTrackId)
                .filter(StringUtils::isNotBlank)
                .map(DataTypeUtils::toLong)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet())
        )
    );

    Map<Long, UserProfileDTO> userProfileMap = ResponseAssert.requiredSuccess(
        userProfileClient.mapByUserIds(
            waters.stream().filter(
                    apply -> Objects.equals(apply.getEvent(), UserBankWaterEvent.TRANSFER.name())
                        || Objects.equals(apply.getEvent(), UserBankWaterEvent.RECEIVE_TRANSFER.name()))
                .map(UserBankRunningWater::getTrackId)
                .filter(StringUtils::isNotBlank)
                .map(DataTypeUtils::toLong)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet())
        )
    );

    return waters.stream().map(water -> {

          Object details = null;
          if (Objects.equals(water.getEvent(),
              UserBankWaterEvent.EXCHANGE_GOLD_COINS.name())) {
            details = goldApplyMap.get(DataTypeUtils.toLong(water.getTrackId()));
          }

          if (Objects.equals(water.getEvent(), UserBankWaterEvent.WITHDRAW.name())) {
            details = moneyApplyMap.get(DataTypeUtils.toLong(water.getTrackId()));
          }

          if (Objects.equals(water.getEvent(), UserBankWaterEvent.TRANSFER.name()) ||
              Objects.equals(water.getEvent(), UserBankWaterEvent.RECEIVE_TRANSFER.name())) {
            details = userProfileMap.get(DataTypeUtils.toLong(water.getTrackId()));
          }

          return new UserBankWaterCO()
              .setId(water.getId())
              .setType(water.getType())
              .setAmount(water.getAmount())
              .setServiceCharge(water.getServiceCharge())
              .setActualAmount(water.getActualAmount())
              .setEvent(water.getEvent())
              .setEventDescribe(water.getEventDescribe())
              .setBalance(water.getBalance())
              .setTrackId(water.getTrackId())
              .setDetails(details)
              .setCreateTime(water.getCreateTime());
        })
        .collect(Collectors.toList());
  }

}
