package com.red.circle.console.app.service.app.user;

import com.alibaba.nacos.shaded.com.google.common.collect.Lists;
import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.DiamondOrigin;
import com.red.circle.console.app.convertor.WalletAppConvertor;
import com.red.circle.console.app.dto.clienobject.user.UserGoldBalanceCO;
import com.red.circle.console.app.dto.clienobject.user.UserGoldRunningWaterCO;
import com.red.circle.console.app.dto.clienobject.user.UserGoldRunningWaterClsTmpCO;
import com.red.circle.console.infra.database.rds.service.admin.UserService;
import com.red.circle.console.inner.error.ConsoleErrorCode;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.mybatis.constant.PageConstant;
import com.red.circle.order.inner.endpoint.UserRechargeCountClient;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.inner.endpoint.activity.CumulativeRechargeClient;
import com.red.circle.other.inner.endpoint.sys.AnchorSalaryExchangeGoldClient;
import com.red.circle.other.inner.endpoint.user.user.UserLevelClient;
import com.red.circle.other.inner.model.cmd.sys.SysAnchorSalaryExchangeGoldCmd;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.other.inner.endpoint.user.user.UserSvipClient;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.wallet.inner.endpoint.wallet.WalletDiamondClient;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.error.WalletErrorCode;
import com.red.circle.wallet.inner.model.cmd.DeductGoldCmd;
import com.red.circle.wallet.inner.model.cmd.DiamondCmd;
import com.red.circle.wallet.inner.model.cmd.DiamondReceiptCmd;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.cmd.SendGoldCmd;
import com.red.circle.wallet.inner.model.cmd.UserGoldRunningWaterBackQryCmd;
import com.red.circle.wallet.inner.model.dto.UserGoldRunningWaterClsHistoryDTO;
import com.red.circle.wallet.inner.model.dto.UserGoldRunningWaterHistoryDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * 用户钱包 服务实现.
 *
 * @author pengliang on 2022/7/21
 */
@Service
@RequiredArgsConstructor
public class UserWalletServiceImpl implements UserWalletService {

  private final UserService userService;
  private final UserSvipClient userSvipClient;
  private final WalletGoldClient walletGoldClient;
  private final UserProfileClient userProfileClient;
  private final UserLevelClient userLevelClient;
  private final WalletAppConvertor walletAppConvertor;
  private final WalletDiamondClient walletDiamondClient;
  private final UserRechargeCountClient userRechargeCountClient;
  private final CumulativeRechargeClient cumulativeRechargeClient;
  private final AnchorSalaryExchangeGoldClient anchorSalaryExchangeGoldClient;

  @Override
  public BigDecimal getGoldBalance(Long userId) {
    return ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userId)).getDollarAmount();
  }

  @Override
  public List<UserGoldRunningWaterCO> listGoldRunningWater(UserGoldRunningWaterBackQryCmd query) {
    if (Objects.isNull(query.getLimit())) {
      query.setLimit(PageConstant.DEFAULT_LIMIT_SIZE);
    }

    if (Objects.isNull(query.getStartTime()) || Objects.isNull(query.getEndTime())) {
      query.setStartTime(TimestampUtils.nowPlusDays(7).getTime());
      query.setEndTime(TimestampUtils.now().getTime());
    }

    List<UserGoldRunningWaterHistoryDTO> runningWaters = walletGoldClient.listWater(query)
        .getBody();

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

    Map<Long, UserProfileDTO> userProfileMap = ResponseAssert.requiredSuccess(
        userProfileClient.mapByUserIds(
            runningWaters.stream().map(UserGoldRunningWaterHistoryDTO::getUserId)
                .collect(Collectors.toSet())));
/*
    Map<Long, User> userMap = userService.mapBackUser(
        runningWaters.stream().map(UserGoldRunningWaterTmpDTO::getBackOperationUser)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet()));*/

    return runningWaters.stream()
        .map(runningWater -> {

          UserProfileDTO userProfile = userProfileMap.get(runningWater.
              getUserId());

          if (Objects.isNull(userProfile)) {
            return null;
          }

          return new UserGoldRunningWaterCO()
              .setUserProfile(userProfileMap.get(runningWater.getUserId()))
              .setRunningWater(walletAppConvertor.toUserGoldRunningWaterDTO(runningWater));
              /*.backOperationName(Optional.ofNullable(runningWater.getBackOperationUser())
                  .map(backUid -> Optional.of(userMap.get(backUid))
                      .map(User::getNickname)
                      .orElse(""))
                  .orElse(""))*/
        })
        .filter(Objects::nonNull)
        .collect(Collectors.toList());
  }

  @Override
  public UserGoldRunningWaterClsTmpCO getGoldRunningWaterCls(UserGoldRunningWaterBackQryCmd query) {
    if (Objects.isNull(query.getLimit())) {
      query.setLimit(PageConstant.DEFAULT_LIMIT_SIZE);
    }

    if (Objects.isNull(query.getStartTime()) || Objects.isNull(query.getEndTime())) {
      query.setStartTime(TimestampUtils.nowPlusDays(7).getTime());
      query.setEndTime(TimestampUtils.now().getTime());
    }

    UserGoldRunningWaterClsHistoryDTO clsWater = walletGoldClient.getClsSearchWater(query)
        .getBody();

    if (Objects.isNull(clsWater) || CollectionUtils.isEmpty(clsWater.getWaters())) {
      return null;
    }

    List<UserGoldRunningWaterHistoryDTO> clsWaterWaters = clsWater.getWaters();

    Map<Long, UserProfileDTO> userProfileMap = userProfileClient.mapByUserIds(
        clsWaterWaters.stream().map(UserGoldRunningWaterHistoryDTO::getUserId)
            .collect(Collectors.toSet())
    ).getBody();

    Map<Long, String> opsUserNicknameMap = userService.mapBackUserNickname(
        clsWaterWaters.stream().map(UserGoldRunningWaterHistoryDTO::getBackOperationUser)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet())
    );

    return new UserGoldRunningWaterClsTmpCO()
        .setContext(clsWater.getContext())
        .setListOver(clsWater.getListOver())
        .setRequestId(clsWater.getRequestId())
        .setErrorCode(clsWater.getErrorCode())
        .setWaters(clsWaterWaters.stream()
            .map(runningWater -> {

              UserProfileDTO userProfile = userProfileMap.get(runningWater.
                  getUserId());

              if (Objects.isNull(userProfile)) {
                return null;
              }

              return new UserGoldRunningWaterCO()
                  .setUserProfile(userProfileMap.get(runningWater.getUserId()))
                  .setRunningWater(walletAppConvertor.toUserGoldRunningWaterDTO(runningWater))
                  .setBackOperationName(Optional.ofNullable(runningWater.getBackOperationUser())
                      .map(opsUserNicknameMap::get).orElse("")
                  );
            })
            .filter(Objects::nonNull)
            .toList()
        );
  }

  @Override
  public void sendGold(SendGoldCmd param) {
    ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
        ArithmeticUtils.gtZero(param.getQuantity()));
    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(param.getUserId()));
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);

    if (StringUtils.isNotBlank(param.getTrackId())) {
      ResponseAssert.isFalse(ConsoleErrorCode.PROCESSED,
          ResponseAssert.requiredSuccess(walletGoldClient.existsEventId(param.getTrackId())));
    }

    // 金币折算工资
    if (param.checkSalary() && ArithmeticUtils.gtZero(param.getUsdQuantity())) {
      anchorSalaryExchangeGoldClient.add(new SysAnchorSalaryExchangeGoldCmd()
          .setCandyNumber(param.getQuantity())
          .setUsdAmount(param.getUsdQuantity())
          .setSysOrigin(userProfile.getSysOriginChild())
          .setUserId(userProfile.getId())
      );

      //将工资的价值美金 算入至累计充值
//      userRechargeCountClient.count(userProfile.getId(), param.getUsdQuantity(),
//          MonthlyRechargeType.SALARY_EXCHANGE);

      // 累计充值活动 - 每日累计充值活动抽奖
      cumulativeRechargeClient.incrTodayRechargeScore(userProfile.getId(),
          param.getUsdQuantity().doubleValue());

      // SVip积分累计.
      userSvipClient.incrIntegral(param.getUserId(), param.getQuantity().longValue());

    } else if (param.getType() != null && param.getType() == 4) {
      // 充值类型计入财富榜
      userLevelClient.incrWealthExp(userProfile.getId(), param.getQuantity());
    }

    GoldOrigin candyOriginEnum =
        param.checkSalary() ? GoldOrigin.WAGES : GoldOrigin.REWARD_COINS;
    walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .opsIncome()
        .userId(userProfile.getId())
        .eventId(StringUtils.isBlankOrElse(param.getTrackId(), "-"))
        .sysOrigin(userProfile.getOriginSys())
        .origin(candyOriginEnum)
        .amount(param.getQuantity())
        .remark(param.getRemarks())
        .closeDelayAsset()
        .opUserId(param.getOperationUserId())
        .build());
  }

  @Override
  public void deductGold(DeductGoldCmd param) {
    ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
        ArithmeticUtils.gtZero(param.getQuantity()));
    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(param.getUserId()));
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);

    walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .opsExpenditure()
        .userId(userProfile.getId())
        .sysOrigin(userProfile.getOriginSys())
        .eventId("BACK-" + param.getOperationUserId())
        .amount(param.getQuantity())
        .origin(GoldOrigin.DEDUCT_COINS)
        .remark(param.getRemarks())
        .closeDelayAsset()
        .opUserId(param.getOperationUserId())
        .build());
  }

  @Override
  public List<UserGoldBalanceCO> listGoldBalance() {
    return null;

  }

  @Override
  public void sendDiamond(DiamondCmd cmd) {
    ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
        ArithmeticUtils.gtZero(cmd.getQuantity()));
    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(cmd.getUserId()));
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);

    walletDiamondClient.changeBalance(
        new DiamondReceiptCmd()
            .setConsumeId(IdWorkerUtils.getIdStr())
            .setSysOrigin(SysOriginPlatformEnum.valueOf(userProfile.getOriginSys()))
            .setTrackId(cmd.getOperationUserId())
            .setUserId(userProfile.getId())
            .setOriginUserId(userProfile.getId())
            .setOrigin(DiamondOrigin.REWARD_DIAMOND)
            .setCustomizeOriginDesc(cmd.getRemarks())
            .setType(ReceiptType.INCOME)
            .setAmount(cmd.getQuantity())
            .setCreateTime(TimestampUtils.now()));

  }

  @Override
  public void deductDiamond(DiamondCmd cmd) {

    ResponseAssert.isTrue(WalletErrorCode.REQUIRED_GT_ZERO,
        ArithmeticUtils.gtZero(cmd.getQuantity()));
    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(cmd.getUserId()));
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);

    walletDiamondClient.changeBalance(
        new DiamondReceiptCmd()
            .setConsumeId(IdWorkerUtils.getIdStr())
            .setSysOrigin(SysOriginPlatformEnum.valueOf(userProfile.getOriginSys()))
            .setTrackId(cmd.getOperationUserId())
            .setUserId(userProfile.getId())
            .setOriginUserId(userProfile.getId())
            .setOrigin(DiamondOrigin.DEDUCT_DIAMOND)
            .setCustomizeOriginDesc(cmd.getRemarks())
            .setType(ReceiptType.EXPENDITURE)
            .setAmount(cmd.getQuantity())
            .setCreateTime(TimestampUtils.now()));

  }
}
