package com.red.circle.other.app.listener.team;

import com.google.common.base.Throwables;
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.PropsActivityRewardEnum;
import com.red.circle.component.mq.MessageEventProcess;
import com.red.circle.component.mq.MessageEventProcessDescribe;
import com.red.circle.component.mq.config.RocketMqMessageListener;
import com.red.circle.component.mq.service.Action;
import com.red.circle.component.mq.service.ConsumerMessage;
import com.red.circle.component.mq.service.MessageListener;
import com.red.circle.mq.business.model.event.team.TeamSalaryRevokeEvent;
import com.red.circle.mq.rocket.business.streams.TeamSalaryRevokeSink;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.TeamSalaryPaymentDetails;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMemberTarget;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamPolicyManager;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamSalaryPaymentService;
import com.red.circle.other.infra.database.rds.service.badge.BadgeBackpackService;
import com.red.circle.other.infra.database.rds.service.gift.GiftBackpackService;
import com.red.circle.other.infra.database.rds.service.props.FragmentsBackpackService;
import com.red.circle.other.infra.database.rds.service.props.PropsBackpackService;
import com.red.circle.other.inner.model.dto.activity.props.ActivityRewardPropsDTO;
import com.red.circle.other.inner.model.dto.agency.agency.TeamSalaryPaymentRemark;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.DateFormatConstant;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.date.ZonedDateTimeUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.parse.DataTypeUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.inner.endpoint.bank.UserBankBalanceClient;
import com.red.circle.wallet.inner.endpoint.bank.UserBankRunningWaterClient;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.RecoveryGoldReceiptCmd;
import com.red.circle.wallet.inner.model.cmd.UserBankBalanceDecrCmd;
import com.red.circle.wallet.inner.model.dto.UserBankRunningWaterDTO;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 将用户关联的团队工资追回.
 *
 * @author pengshigang on 2023/4/17
 */
@Slf4j
@RocketMqMessageListener(groupId = TeamSalaryRevokeSink.INPUT, tag = TeamSalaryRevokeSink.TAG)
@RequiredArgsConstructor
public class TeamSalaryRevokeListener implements MessageListener {

  private final String tag = "将用户关联的团队工资追回";
  private final WalletGoldClient walletGoldClient;
  private final GiftBackpackService giftBackpackService;
  private final MessageEventProcess messageEventProcess;
  private final PropsBackpackService propsBackpackService;
  private final BadgeBackpackService badgeBackpackService;
  private final UserBankBalanceClient userBankBalanceClient;
  private final TeamMemberTargetService teamMemberTargetService;
  private final FragmentsBackpackService fragmentsBackpackService;
  private final TeamSalaryPaymentService teamSalaryPaymentService;
  private final UserBankRunningWaterClient userBankRunningWaterClient;

  @Override
  public Action consume(ConsumerMessage message) {
    return messageEventProcess.consume(
        MessageEventProcessDescribe.builder()
            .logTag(tag)
            .consumeMsgTimeoutMinute(30)
            .repeatConsumeMinute(10)
            .repeatConsumeTag("TeamSalaryRevoke")
            .checkConsumeTag(TeamSalaryRevokeSink.TAG)
            .message(message)
            .build(),
        TeamSalaryRevokeEvent.class,
        eventBody -> {

          if (Objects.isNull(eventBody.getPurchaseDate()) || CollectionUtils
              .isEmpty(eventBody.getAnchorMap())) {
            log.error("[{}]参数不存在:{}", tag, JacksonUtils.toJson(eventBody));
            return;
          }

          if (!Objects.equals(eventBody.getSysOrigin(), SysOriginPlatformEnum.ASWAT.name())) {
            log.error("[{}]系统参数支持ASWAT:{}", tag, JacksonUtils.toJson(eventBody));
            return;
          }

          business(eventBody);
        });
  }

  private void business(TeamSalaryRevokeEvent event) {

    // 订单支付时的年月
    Integer purchaseDate = getPurchaseDate(event);

    // 订单支付时间是否为当前时间,是则无需做主播目标相减操作,因为退款时本月主播目标在发通知之前就已经减掉了.
    boolean notSubtract = isNotSubtract(purchaseDate);

    event.getAnchorMap().forEach((userId, deductTarget) -> {

      TeamSalaryPayment payment = teamSalaryPaymentService.getOneById(userId + "_" + purchaseDate);
      if (Objects.isNull(payment)) {
        return;
      }

      List<TeamSalaryPaymentDetails> salarySendDetails = payment.getSalarySendDetails();
      if (CollectionUtils.isEmpty(salarySendDetails)) {
        addRemark(payment.getId(), "没有发送工资记录明细,退款失败");
        return;
      }

      Map<String, TeamPolicyManager> mapPolicy = payment.getPolicyMap();
      if (CollectionUtils.isEmpty(mapPolicy)) {
        addRemark(payment.getId(), "没有政策,退款失败");
        return;
      }

      // 处理退款追回工资,道具业务
      AtomicReference<Boolean> isUpdateState = new AtomicReference<>(Boolean.FALSE);
      salarySendDetails.forEach(details -> {

        if (Boolean.TRUE.equals(details.getRecycled())) {
          return;
        }

        isUpdateState.set(Boolean.TRUE);

        TeamPolicyManager policyManager = mapPolicy.get(details.getTeamRegionId());
        if (Objects.isNull(policyManager)) {
          return;
        }

        TeamMemberTarget target = getTeamMemberTarget(details.getMemberTargetId());
        if (Objects.isNull(target) || CollectionUtils.isEmpty(target.getDailyTargets())) {
          return;
        }

        // 将memberTarget 中的目标数量循环减掉退款数量, 原deductTarget不变，每次克隆一个deductTarget进去
        deductOriginalTarget(new AtomicReference<>(notSubtract ? 0 : deductTarget), target);

        // 根据团队政策 + 团队成员账单
        TeamBillMemberTarget billMemberTarget = TeamBillCycleUtils
            .calculatorPolicyTarget(target, policyManager);

        // 如果扣除退款目标后计算出来的等级依旧大于或等于发工资的等级,则不做处理
        if (Objects.nonNull(billMemberTarget) && Objects.nonNull(billMemberTarget.getLevel())
            && billMemberTarget.getLevel() >= details.getPolicyLevel()) {
          return;
        }

        // 退款后掉级，获得需要被扣除银行账户金额的用户ID与道具追回
        Long revokeUserId = getUserIdAndRevokeProps(details);
        if (Objects.isNull(revokeUserId)) {
          return;
        }

        // 银行账户支出
        userBankBalanceClient.decr(new UserBankBalanceDecrCmd()
            .setUserId(revokeUserId)
            .setAmount(PennyAmount.ofDollar(details.getSalary()))
        );
        if (CollectionUtils.isEmpty(details.getRemarks())) {
          details.setRemarks(List.of(new TeamSalaryPaymentRemark()
              .setCreateTime(TimestampUtils.now())
              .setRemark("扣款成功")
          ));
        } else {

          List<TeamSalaryPaymentRemark> remarkList = details.getRemarks();
          remarkList.add(new TeamSalaryPaymentRemark()
              .setCreateTime(TimestampUtils.now())
              .setRemark("扣款成功")
          );
          details.setRemarks(remarkList);
        }

        details.setRecycled(Boolean.TRUE);
        details.setUpdateTime(TimestampUtils.now());
        details.setRefundUserId(event.getRefundUserId());
        // 添加流水
        addUserBankRunningWater(details, revokeUserId);

      });

      // 修改状态
      if (Boolean.TRUE.equals(isUpdateState.get())) {
        teamSalaryPaymentService.updateDetails(payment.getId(), salarySendDetails);
      }

    });

  }

  private boolean isNotSubtract(Integer purchaseDate) {

    return Objects.equals(purchaseDate, Integer.parseInt(ZonedDateTimeAsiaRiyadhUtils.nowFormat(
        DateFormatConstant.yyyyMM)));
  }

  private int getPurchaseDate(TeamSalaryRevokeEvent event) {
    return Integer.parseInt(ZonedDateTimeUtils
        .format(ZonedDateTimeAsiaRiyadhUtils.convert(event.getPurchaseDate().toLocalDateTime()),
            DateFormatConstant.yyyyMM));
  }

  /**
   * 添加流水.
   */
  private void addUserBankRunningWater(TeamSalaryPaymentDetails details, Long userId) {

    UserBankWaterEvent event = UserBankWaterEvent.SYSTEM_DEDUCT;
    // 是否将主播工资发给代理银行账户(true 是)
    if (Boolean.TRUE.equals(details.getAnchor()) && Boolean.TRUE
        .equals(details.getHostSalaryToAgent())) {
      event = UserBankWaterEvent.SYSTEM_AUTOMATIC_DEDUCT_SALARY_AGENT_MEMBER;
    }
    // 是代理
    else if (Boolean.FALSE.equals(details.getAnchor())) {
      event = UserBankWaterEvent.SYSTEM_AUTOMATIC_DEDUCT_SALARY_AGENT;
    }
    // 是主播
    else if (Boolean.TRUE.equals(details.getAnchor())) {
      event = UserBankWaterEvent.SYSTEM_AUTOMATIC_DEDUCT_SALARY;
    }

    try {

      userBankRunningWaterClient.add(new UserBankRunningWaterDTO()
          .setId(IdWorkerUtils.getId())
          .setUserId(userId)
          .setSysOrigin(details.getSysOrigin())
          .setType(ReceiptType.EXPENDITURE.getType())
          .setAmount(details.getSalary())
          .setEvent(event.name())
          .setEventDescribe(event.getDescribe())
          .setTrackId(details.getId())
          .setRemark("")
          .setBalance(userBankBalanceClient.getBalance(userId).getBody().getAccurateBalance())
          .setCreateTime(TimestampUtils.now())
          .setCreateUser(0L)
          .setCreateUserOrigin(0)
      );

    } catch (Exception e) {
      //消费失败
      log.error("[将用户关联的团队工资追回] 插入单条银行流水记录失败:{},userId:{}",
          Throwables.getStackTraceAsString(e), userId);
    }

  }

  /**
   * 获得需要被扣掉金额的用户ID.
   */
  private Long getUserIdAndRevokeProps(TeamSalaryPaymentDetails details) {

    // 是否将主播工资发给代理银行账户(true 是)
    if (Boolean.TRUE.equals(details.getAnchor()) && Boolean.TRUE
        .equals(details.getHostSalaryToAgent())) {

      revokeProps(details);
      return details.getTeamOwnId();

    }
    // 是代理
    else if (Boolean.FALSE.equals(details.getAnchor())) {

      return details.getTeamOwnId();

    }
    // 是主播
    else if (Boolean.TRUE.equals(details.getAnchor())) {

      revokeProps(details);
      return details.getTeamMemberId();
    }

    return null;
  }

  /**
   * 回收道具.
   */
  private void revokeProps(TeamSalaryPaymentDetails details) {

    if (Objects.isNull(details.getPolicy())) {
      return;
    }

    List<ActivityRewardPropsDTO> rewardProps = details.getPolicy().getPropsRewards();
    if (CollectionUtils.isEmpty(rewardProps)) {
      return;
    }

    details.getPolicy().getPropsRewards().forEach(props -> {

      if (PropsActivityRewardEnum.PROPS.eq(props.getType())) {

        // TODO VIP 的附赠道具没有回收，无法确定赠送的道具是新的还是送的。
        propsBackpackService.reduceDaysAndUnUse(details.getTeamMemberId(),
            Set.of(DataTypeUtils.toLong(props.getContent())),
            props.getQuantity()
        );
        return;
      }

      if (PropsActivityRewardEnum.FRAGMENTS.eq(props.getType())) {

        fragmentsBackpackService.decrFragments(details.getTeamMemberId(),
            Set.of(DataTypeUtils.toLong(props.getContent())), props.getQuantity());
        return;
      }

      if (PropsActivityRewardEnum.GOLD.eq(props.getType())) {
        walletGoldClient.recovery(new RecoveryGoldReceiptCmd()
            .setUserId(details.getTeamMemberId())
            .setAmount(PennyAmount.ofDollar(ArithmeticUtils.toBigDecimal(props.getContent())))
            .setSysOrigin(details.getSysOrigin())
            .setEventType("PROPS_RECOVERY_GOLD")
            .setEventDesc("Props to recover gold coins")
            .setEventId(details.getId())
        );
        return;
      }

      if (PropsActivityRewardEnum.GIFT.eq(props.getType())) {

        giftBackpackService.deductGift(details.getTeamMemberId(),
            DataTypeUtils.toLong(props.getContent()), props.getQuantity());
        return;
      }

      if (PropsActivityRewardEnum.BADGE.eq(props.getType())) {
        badgeBackpackService.reduceDaysAndUnUse(details.getTeamMemberId(),
            Set.of(DataTypeUtils.toLong(props.getContent())), props.getQuantity());
      }

    });
  }

  /**
   * 将目标数据中的目标扣除传入的退款目标.
   *
   * @param cloneDeductTarget 需要退款的目标
   * @param memberTarget      成员目标
   */
  private void deductOriginalTarget(AtomicReference<Long> cloneDeductTarget,
      TeamMemberTarget memberTarget) {

    memberTarget.getDailyTargets().forEach(target -> {
      if (cloneDeductTarget.get() <= 0) {
        return;
      }

      if (cloneDeductTarget.get() - target.getAcceptGiftValue() > 0) {
        cloneDeductTarget.set(cloneDeductTarget.get() - target.getAcceptGiftValue());
        target.setAcceptGiftValue(0L);
        return;
      }

      target.setAcceptGiftValue(target.getAcceptGiftValue() - cloneDeductTarget.get());
      cloneDeductTarget.set(0L);
    });

  }

  private TeamMemberTarget getTeamMemberTarget(String id) {
    return teamMemberTargetService.getById(id);
  }

  private void addRemark(String id, String desc) {
    teamSalaryPaymentService.addRemark(id, desc);
  }

}
