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

import cn.hutool.core.map.MapUtil;
import com.google.common.base.Throwables;
import 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.SendPropsOrigin;
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.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.dto.ResultResponse;
import com.red.circle.mq.business.model.event.team.TeamSalaryPaymentEvent;
import com.red.circle.mq.rocket.business.streams.TeamSalaryPaymentSink;
import com.red.circle.other.infra.common.props.PropsSendCommon;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillMemberTarget;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillSettleResult;
import com.red.circle.other.infra.database.mongo.dto.team.TeamPolicy;
import com.red.circle.other.infra.database.mongo.dto.team.TeamSalaryPaymentDetails;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamBillCycle;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamMember;
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.TeamProfile;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamSalaryPayment;
import com.red.circle.other.infra.database.mongo.entity.user.region.SysRegionConfig;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamMemberTargetService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamPolicyManagerService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamProfileService;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamSalaryPaymentService;
import com.red.circle.other.infra.database.mongo.service.user.region.SysRegionConfigService;
import com.red.circle.other.infra.database.rds.entity.team.BillDiamondBalanceDetails;
import com.red.circle.other.infra.database.rds.service.team.BillDiamondBalanceDetailsService;
import com.red.circle.other.infra.database.rds.service.team.BillDiamondBalanceService;
import com.red.circle.other.infra.utils.ZonedDateTimeUtils;
import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum;
import com.red.circle.other.inner.model.cmd.material.PrizeDescribe;
import com.red.circle.other.inner.model.cmd.material.PrizeDescribeRewardCmd;
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.other.inner.model.dto.agency.agency.TeamStatus;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.json.JacksonUtils;
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.diamond.SalaryDiamondBalanceClient;
import com.red.circle.wallet.inner.endpoint.diamond.SalaryDiamondRunningWaterClient;
import com.red.circle.wallet.inner.model.cmd.UserBankBalanceIncrCmd;
import com.red.circle.wallet.inner.model.cmd.UserSalaryDiamondCmd;
import com.red.circle.wallet.inner.model.dto.SalaryDiamondBalanceDTO;
import com.red.circle.wallet.inner.model.dto.UserBankRunningWaterDTO;
import com.red.circle.wallet.inner.model.dto.UserSalaryDiamondRunningWaterDTO;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 满足政策的团队工资实时发送.
 *
 * @author pengshigang on 2023/4/13
 */
@Slf4j
@RocketMqMessageListener(groupId = TeamSalaryPaymentSink.INPUT, tag = TeamSalaryPaymentSink.TAG)
@RequiredArgsConstructor
public class TeamSalaryPaymentListener implements MessageListener {

  private final PropsSendCommon propsSendCommon;
  private final TeamMemberService teamMemberService;
  private final TeamProfileService teamProfileService;
  private final MessageEventProcess messageEventProcess;
  private final TeamBillCycleService teamBillCycleService;
  private final UserBankBalanceClient userBankBalanceClient;
  private final SysRegionConfigService sysRegionConfigService;
  private final TeamMemberTargetService teamMemberTargetService;
  private final TeamPolicyManagerService teamPolicyManagerService;
  private final TeamSalaryPaymentService teamSalaryPaymentService;
  private final UserBankRunningWaterClient userBankRunningWaterClient;
  private final SalaryDiamondBalanceClient salaryDiamondBalanceClient;
  private final SalaryDiamondRunningWaterClient salaryDiamondRunningWaterClient;
  private final BillDiamondBalanceService billDiamondBalanceService;
  private final BillDiamondBalanceDetailsService billDiamondBalanceDetailsService;

  @Override
  public Action consume(ConsumerMessage message) {
    return messageEventProcess.consume(
        MessageEventProcessDescribe.builder()
            .logTag("满足政策的团队工资实时发送")
            .consumeMsgTimeoutMinute(30)
            .repeatConsumeMinute(10)
            .repeatConsumeTag("TeamSalaryCount")
            .checkConsumeTag(TeamSalaryPaymentSink.TAG)
            .message(message)
            .build(),
        TeamSalaryPaymentEvent.class,
        eventBody -> {
          if (CollectionUtils.isEmpty(eventBody.getTeamIds())) {
            log.error("[满足政策的团队工资实时发送]参数不存在:{}", JacksonUtils.toJson(eventBody));
            return;
          }

          List<TeamProfile> teamProfiles = teamProfileService.listByIds(eventBody.getTeamIds());
          if (CollectionUtils.isEmpty(teamProfiles)) {
            return;
          }

          teamProfiles.forEach(teamProfile -> {

            if (Objects.equals(teamProfile.getStatus(), TeamStatus.CLOSE)) {
              // [满足政策的团队工资实时发送]团队已关闭
              return;
            }
            business(teamProfile, eventBody);
          });

        });
  }

  private void business(TeamProfile teamProfile, TeamSalaryPaymentEvent event) {
    log.warn("满足政策的团队工资实时发送:"+ event);
    TeamBillCycle billCycle = getTeamBillCycle(teamProfile, event);
    if (Objects.isNull(billCycle)) {
      // [满足政策的团队工资实时发送]没有账单
      return;
    }
    log.warn("billCycle:"+ billCycle);

    SysRegionConfig region = getSysRegionConfig(teamProfile);
    if (Objects.isNull(region) || Objects.isNull(region.getMetadata())) {
      // [满足政策的团队工资实时发送]没有区域数据
      return;
    }
    log.warn("region:"+ region);

    if (Boolean.FALSE.equals(isOpenDailyAutoSalary(region.getMetadata()))) {
      // [满足政策的团队工资实时发送]没有开放每日自动发放模式
      return;
    }

    Map<String, TeamPolicyManager> regionPolicyMap = getPaymentPolicyMap(teamProfile, billCycle, event);
    if (MapUtil.isEmpty(regionPolicyMap)) {
      // [满足政策的团队工资实时发送]该团队没有区域政策或账单政策
      return;
    }

    List<TeamMemberTarget> teamMemberTargets = getTeamMemberTargets(billCycle);
    if (CollectionUtils.isEmpty(teamMemberTargets)) {
      // [满足政策的团队工资实时发送]没有团队成员数据
      return;
    }
    log.warn("teamMemberTargets:"+ teamMemberTargets);

    // 循环发送每个成员工资
    teamMemberTargets.forEach(memberTarget -> {

      if (Objects.isNull(memberTarget) || memberTarget.sumAcceptGiftValue() <= 0) {
        return;
      }

      TeamMember member = teamMemberService.getByMemberId(memberTarget.getUserId());
      if (Objects.isNull(member)) {
        return;
      }

      TeamPolicyManager policyManager = TeamBillCycleUtils.getPolicyFromMap(
              regionPolicyMap,
              memberTarget.getRegion(),
              memberTarget.getCountryCode());

      // 计算工资与等级
      TeamBillMemberTarget billTarget = TeamBillCycleUtils
          .calculatorPolicyTarget(memberTarget, policyManager);
      log.warn("billTarget:"+ billTarget);
      if (Objects.isNull(billTarget)) {
        return;
      }

      // 发送达标的所有政策工资，以防漏发
      List<TeamPolicy> policyList = listCompletePolicy(policyManager, billTarget);
      if (CollectionUtils.isEmpty(policyList)) {
        return;
      }
      log.warn("policyList:"+ policyList);

      TeamSalaryPayment payment = getCreateIfAbsentPayment(member, event);
      if (Objects.isNull(payment)) {
        return;
      }

      for (int index = 0; index < policyList.size(); index++) {

        // 获得本次的具体政策存档
        TeamPolicy policy = getPolicyDetails(policyManager, policyList.get(index).getLevel());
        if (Objects.isNull(policy)) {
          addRemark(payment.getId(),
              "区域政策中的政策不存在,团长userID:" + teamProfile.getOwnUserId());
          continue;
        }

        // 辅助:组装工资辅助模型
        TeamBillMemberTarget tmpBillMemberTarget = buildTmpBillMemberTarget(policy, billTarget);

        // 第二级政策开始都需要减掉前面等级政策工资才能得到实际应发工资
        if (index > 0) {

          // 当前等级的前一份具体政策
          TeamPolicy previousPolicy = getPreviousLevelPolicy(policyManager, policy.getLevel());

          // 实际应发工资 = 当前初步工资 - 上一份具体政策工资
          calculateActualSalary(tmpBillMemberTarget, previousPolicy);

        }

        if (Objects.isNull(policyManager.getPolicyType()) || Objects.equals(TeamPolicyTypeEnum.MONEY.name(), policyManager.getPolicyType())) {
          log.warn("发送团队成员美元工资:{}\n{}\n{}\n{}\n{}\n{}", teamProfile, memberTarget, region, policy, payment, billCycle);
          // 发送团队成员工资
          memberBankBalancePayment(teamProfile, memberTarget.getId(), tmpBillMemberTarget,
                  region.getMetadata(), policy, payment, billCycle.getId());

          // 发送团队拥有者(代理)工资
          teamBankBalancePayment(teamProfile, memberTarget.getId(), tmpBillMemberTarget,
                  region.getMetadata(), policy, payment, billCycle.getId());
        } else {
          // 辅助:组装钻石工资辅助模型
          tmpBillMemberTarget = buildDiamondTmpBillMemberTarget(policy, billTarget);

          log.warn("发送团队成员钻石工资:{}\n{}\n{}\n{}\n{}\n{}", teamProfile, memberTarget, region, policy, payment, billCycle);

          // 发送团队成员钻石工资
          memberBankBalanceDiamondPayment(teamProfile, memberTarget.getId(), tmpBillMemberTarget,
                  region.getMetadata(), policy, payment);

          // 发送团队拥有者(代理)钻石工资
          teamBankBalanceDiamondPayment(teamProfile, memberTarget.getId(), tmpBillMemberTarget,
                  region.getMetadata(), policy, payment);
        }
      }

      // 将本次政策存入支付记录中,同区域政策新的覆盖旧的
      updatePolicyMap(policyManager, region, payment);

    });

  }

  /**
   * 组装辅助钻石工资模型.
   */
  private TeamBillMemberTarget buildDiamondTmpBillMemberTarget(TeamPolicy policy,
                                                               TeamBillMemberTarget memberTarget) {
    log.warn("钻石政策，组装辅助钻石工资模型：" + memberTarget.getAcceptGiftValue()+"--" + policy.getTarget());
    BigDecimal memberSalary = BigDecimal.ZERO;
    BigDecimal ownSalary = BigDecimal.ZERO;
    BigDecimal totalSalary = BigDecimal.ZERO;
    if (memberTarget.getAcceptGiftValue() != null && policy.getTarget() != null) {
      // 成员工资
      double multiple = (double) memberTarget.getAcceptGiftValue() / policy.getTarget();
      BigDecimal packet = BigDecimal.valueOf(multiple).setScale(2, RoundingMode.DOWN);
      memberSalary = policy.getMemberSalary().multiply(packet).setScale(2, RoundingMode.DOWN);
      // 代理工资
      ownSalary = policy.getOwnSalary().multiply(packet).setScale(2, RoundingMode.DOWN);
      // 总工资
      totalSalary = memberSalary.add(ownSalary);
    }

    return new TeamBillMemberTarget()
            .setTargetId(memberTarget.getTargetId())
            .setSysOrigin(memberTarget.getSysOrigin())
            .setLevel(policy.getLevel())
            .setSatisfyLevel(memberTarget.getSatisfyLevel())
            .setUserId(memberTarget.getUserId())
            .setAcceptGiftValue(memberTarget.getAcceptGiftValue())
            .setGiveGiftValue(memberTarget.getGiveGiftValue())
            .setRoomValue(memberTarget.getRoomValue())
            .setOnlineTime(memberTarget.getOnlineTime())
            .setMemberSalary(memberSalary)
            .setOwnSalary(ownSalary)
            .setTotalSalary(totalSalary)
            .setPropsRewards(policy.getPropsRewards());

  }


  /**
   * 发送团队成员钻石工资到银行账户
   */
  private void memberBankBalanceDiamondPayment(TeamProfile teamProfile, String memberTargetId,
                                               TeamBillMemberTarget target, Map<String, String> metadata, TeamPolicy policy,
                                               TeamSalaryPayment payment) {

    Integer teamBillId = ZonedDateTimeUtils.getAsiaRiyadhYesterday();
    // 是否已经发送过该成员工资
    if (Boolean.TRUE.equals(
            checkRepeatAndBillIdPayment(teamProfile, payment, policy, memberTargetId, teamBillId, Boolean.TRUE))) {
      return;
    }

    // 查询用户昨日的账单
    BillDiamondBalanceDetails billDiamondBalanceDetails = billDiamondBalanceDetailsService
            .getEntityByBillBelongAndUserIdAndBillBelongDay(teamProfile.getSysOrigin(), target.getUserId(), teamBillId);

    if (Objects.isNull(billDiamondBalanceDetails)) {
      return;
    }
    log.warn("日结昨日钻石账单入账：{}", billDiamondBalanceDetails);
    BigDecimal balance = billDiamondBalanceDetails.getBalance().setScale(0, RoundingMode.DOWN);
    if (balance.compareTo(BigDecimal.ZERO) > 0) {
      // 余额大于0，入账，小数抹零
      salaryDiamondBalanceClient.incr(new UserSalaryDiamondCmd()
              .setUserId(target.getUserId())
              .setQuantity(balance)
              .setSysOrigin(target.getSysOrigin()));

      String detailsId = IdWorkerUtils.getIdStr();
      // 插入钻石工资流水
      addUserSalaryDiamondRunningWater(detailsId, target.getUserId(), target.getSysOrigin(),
              balance, UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES);

      // 添加发送记录
      teamSalaryPaymentService.paymentSalary(payment.getId(),
              new TeamSalaryPaymentDetails()
                      .setId(detailsId)
                      .setSysOrigin(teamProfile.getSysOrigin())
                      .setTeamId(teamProfile.getId())
                      .setMemberTargetId(memberTargetId)
                      .setTeamOwnId(teamProfile.getOwnUserId())
                      .setTeamMemberId(target.getUserId())
                      .setTeamBillId(Long.valueOf(teamBillId))
                      .setTeamRegionId(teamProfile.getRegion())
                      .setRemarks(Lists.newArrayList(new TeamSalaryPaymentRemark()
                              .setRemark("将主播钻石工资发放至主播钻石钱包")
                              .setCreateTime(TimestampUtils.now())))
                      .setAnchor(Boolean.TRUE)
                      .setHostSalaryToAgent(Boolean.FALSE)
                      .setTarget(target.getAcceptGiftValue())
                      .setSalary(balance)
                      .setPolicyLevel(policy.getLevel())
                      .setPolicy(policy));

      // 发送道具
      sendProps(target.getUserId(), target.getSysOrigin(), Long.valueOf(detailsId),
              policy.getPropsRewards());

      // 扣除成员账单余额
      deduct(target.getUserId(), target.getSysOrigin(), teamBillId, Boolean.FALSE, balance, detailsId);
    }
  }

  /**
   * 校验是否为重复发送.
   *
   * @param teamProfile    团队
   * @param payment        支付信息
   * @param teamBillId     团队账单信息
   * @param policy         政策
   * @param memberTargetId 成员工作id
   * @param isPayMember    支付工资成员验证
   * @return true 重复发送, false 允许发送
   */
  private Boolean checkRepeatAndBillIdPayment(TeamProfile teamProfile, TeamSalaryPayment payment,
                                              TeamPolicy policy, String memberTargetId, Integer teamBillId, Boolean isPayMember) {
    Boolean billPay = teamSalaryPaymentService
            .checkRepeatAndBillIdPayment(payment.getId(), payment.getUserId(), teamProfile.getRegion(), teamBillId,
                    policy.getLevel(), memberTargetId, isPayMember);
    log.warn("账单{}：{}", teamBillId, billPay);
    return billPay;
  }

  /**
   * 发送团队拥有者钻石工资到银行账户
   */
  private void teamBankBalanceDiamondPayment(TeamProfile teamProfile, String memberTargetId,
                                             TeamBillMemberTarget target, Map<String, String> metadata, TeamPolicy policy,
                                             TeamSalaryPayment payment) {

    // 是否已经发送过代理部份工资
    Integer teamBillId = ZonedDateTimeUtils.getAsiaRiyadhYesterday();
    if (Boolean.FALSE.equals(
            checkRepeatAndBillIdPayment(teamProfile, payment, policy, memberTargetId, teamBillId, Boolean.FALSE))) {

      // 查询用户昨日的账单
      BillDiamondBalanceDetails billDiamondBalanceDetails = billDiamondBalanceDetailsService
              .getEntityByBillBelongAndUserIdAndBillBelongDay(teamProfile.getSysOrigin(), target.getUserId(), teamBillId);

      if (Objects.isNull(billDiamondBalanceDetails)) {
        return;
      }

      log.warn("日结团队拥有者昨日钻石工资入账：{}", billDiamondBalanceDetails);
      BigDecimal agentBalance = billDiamondBalanceDetails.getAgentBalance().setScale(0, RoundingMode.DOWN);
      if (agentBalance.compareTo(BigDecimal.ZERO) > 0) {
        // 余额大于0，入账
        // 钻石工资入账
        String detailsId = IdWorkerUtils.getIdStr();
        salaryDiamondBalanceClient.incr(new UserSalaryDiamondCmd()
                .setUserId(teamProfile.getOwnUserId())
                .setQuantity(agentBalance)
                .setSysOrigin(teamProfile.getSysOrigin()));

        // 插入钻石工资流水
        addUserSalaryDiamondRunningWater(detailsId, teamProfile.getOwnUserId(), teamProfile.getSysOrigin(),
                agentBalance, UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES_AGENT);

        // 扣除代理账单余额
        deduct(teamProfile.getOwnUserId(), target.getSysOrigin(), teamBillId, Boolean.TRUE, agentBalance, detailsId);

        // 添加发送记录
        teamSalaryPaymentService.paymentSalary(payment.getId(),
                new TeamSalaryPaymentDetails()
                        .setId(detailsId)
                        .setSysOrigin(teamProfile.getSysOrigin())
                        .setMemberTargetId(memberTargetId)
                        .setTeamId(teamProfile.getId())
                        .setTeamOwnId(teamProfile.getOwnUserId())
                        .setTeamMemberId(target.getUserId())
                        .setTeamBillId(Long.valueOf(teamBillId))
                        .setTeamRegionId(teamProfile.getRegion())
                        .setRemarks(Lists.newArrayList(new TeamSalaryPaymentRemark()
                                .setRemark("将代理工资放入代理钱包")
                                .setCreateTime(TimestampUtils.now())))
                        .setAnchor(Boolean.FALSE)
                        .setHostSalaryToAgent(Boolean.FALSE)
                        .setTarget(target.getAcceptGiftValue())
                        .setSalary(agentBalance)
                        .setPolicyLevel(policy.getLevel())
                        .setPolicy(policy));
      }
    }
  }

  /**
   * 扣除账单钻石.
   *
   * @param acceptUserId 用户ID.
   * @param sysOrigin 系统.
   * @param billBelongDay 账单id.
   * @param isAgent 是否是代理 TURE是/FALSE否.
   */
  private void deduct(Long acceptUserId, String sysOrigin,
                      Integer billBelongDay, Boolean isAgent, BigDecimal amount, String detailsId) {
    if (billDiamondBalanceDetailsService.exists(acceptUserId, billBelongDay)) {
      ResultResponse<SalaryDiamondBalanceDTO> balance = salaryDiamondBalanceClient.getBalance(acceptUserId);
      BillDiamondBalanceDetails billDiamondBalance;
      if (isAgent) {
        billDiamondBalance = billDiamondBalanceDetailsService.decrBillBelongDay(acceptUserId, amount, billBelongDay);
      } else {
        billDiamondBalance = billDiamondBalanceDetailsService.decrAgentBillBelongDay(acceptUserId, amount, billBelongDay);
      }

      // 钻石流水
      salaryDiamondRunningWaterClient.add(new UserSalaryDiamondRunningWaterDTO()
              .setId(IdWorkerUtils.getId())
              .setUserId(acceptUserId)
              .setSysOrigin(sysOrigin)
              .setType(ReceiptType.EXPENDITURE.getType())
              .setTrackId(detailsId)
              .setAmount(amount)
              .setSalaryEvent(UserBankWaterEvent.SYSTEM_DAILY_SETTLEMENT_DEDUCT.name())
              .setEventDesc(UserBankWaterEvent.SYSTEM_DAILY_SETTLEMENT_DEDUCT.getDescribe())
              .setRemark(UserBankWaterEvent.SYSTEM_DAILY_SETTLEMENT_DEDUCT.getDescribe())
              .setBalance(Objects.isNull(balance.getBody()) ? BigDecimal.ZERO : balance.getBody().getBalance())
              .setTempBalance(Objects.isNull(billDiamondBalance) ? BigDecimal.ZERO : billDiamondBalance.getBalance())
              .setCreateTime(TimestampUtils.now())
              .setCreateUser(0L)
              .setCreateUserOrigin(0)
      );
    }
  }

  /**
   * 获得团队政策.
   */
  private TeamBillCycle getTeamBillCycle(TeamProfile teamProfile, TeamSalaryPaymentEvent event) {

    if (Boolean.TRUE.equals(event.getThisMonthFirstDay())) {
      // 如果为1号则自动结算一次上个月工资
      TeamBillCycle legacyMonthBill = teamBillCycleService.getTeamBill(teamProfile.getId(), event.getLastMonth());
      if (Objects.nonNull(legacyMonthBill)) {
        return legacyMonthBill;
      }
      return teamBillCycleService.getLatestTeamBillByMonth(teamProfile.getId(), event.getLastMonth());
    }
    // 结算本月工资
    return teamBillCycleService.getLatestByTeamId(teamProfile.getId());
  }

  /**
   * 获得本次发放使用的政策.
   */
  private Map<String, TeamPolicyManager> getPaymentPolicyMap(TeamProfile teamProfile,
      TeamBillCycle billCycle, TeamSalaryPaymentEvent event) {

    if (Boolean.TRUE.equals(event.getThisMonthFirstDay())) {
      return Optional.ofNullable(billCycle.getSettleResult())
          .map(TeamBillSettleResult::getPolicy)
          .orElseGet(CollectionUtils::newHashMap);
    }

    return teamPolicyManagerService.mapRegionReleaseWithCountry(Set.of(teamProfile.getRegion()));
  }

  /**
   * 发送团队拥有者工资到银行账户
   */
  private void teamBankBalancePayment(TeamProfile teamProfile, String memberTargetId,
      TeamBillMemberTarget target, Map<String, String> metadata, TeamPolicy policy,
      TeamSalaryPayment payment, Long teamBillId) {

    // 是否已经发送过代理部份工资
    if (Boolean.FALSE.equals(
        checkRepeatPayment(teamProfile, payment, policy, memberTargetId, Boolean.FALSE))) {

      String detailsId = IdWorkerUtils.getIdStr();

      // 美元入账
      userBankBalanceClient.incr(new UserBankBalanceIncrCmd()
              .setUserId(teamProfile.getOwnUserId())
              .setSysOrigin(teamProfile.getSysOrigin())
              .setAmount(PennyAmount.ofDollar(target.getOwnSalary()))
      );
      // 插入流水
      addUserBankRunningWater(detailsId, teamProfile.getOwnUserId(),
              teamProfile.getSysOrigin(), target.getOwnSalary(),
              UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES_AGENT);

      // 添加发送记录
      teamSalaryPaymentService.paymentSalary(payment.getId(),
          new TeamSalaryPaymentDetails()
              .setId(detailsId)
              .setSysOrigin(teamProfile.getSysOrigin())
              .setMemberTargetId(memberTargetId)
              .setTeamId(teamProfile.getId())
              .setTeamOwnId(teamProfile.getOwnUserId())
              .setTeamMemberId(target.getUserId())
              .setTeamBillId(teamBillId)
              .setTeamRegionId(teamProfile.getRegion())
              .setRemarks(Lists.newArrayList(new TeamSalaryPaymentRemark()
                      .setRemark("将代理工资放入代理钱包")
                      .setCreateTime(TimestampUtils.now())
                  )
              )
              .setAnchor(Boolean.FALSE)
              .setHostSalaryToAgent(Boolean.FALSE)
              .setTarget(target.getAcceptGiftValue())
              .setSalary(target.getOwnSalary())
              .setPolicyLevel(policy.getLevel())
              .setPolicy(policy)
      );

    }

    // 是否将主播工资转到代理钱包
    if (Boolean.FALSE.equals(isHostSalaryToAgent(metadata))) {
      return;
    }

    // 是否已经发送过该成员工资
    if (Boolean.FALSE.equals(
        checkRepeatPayment(teamProfile, payment, policy, memberTargetId, Boolean.TRUE))) {

      String detailsId = IdWorkerUtils.getIdStr();

      // 美元入账
      userBankBalanceClient.incr(new UserBankBalanceIncrCmd()
              .setUserId(teamProfile.getOwnUserId())
              .setSysOrigin(teamProfile.getSysOrigin())
              .setAmount(PennyAmount.ofDollar(target.getMemberSalary()))
      );

      // 插入流水
      addUserBankRunningWater(detailsId, teamProfile.getOwnUserId(),
              teamProfile.getSysOrigin(), target.getMemberSalary(),
              UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES_MEMBER);

      // 添加发送记录
      teamSalaryPaymentService.paymentSalary(payment.getId(),
          new TeamSalaryPaymentDetails()
              .setId(detailsId)
              .setSysOrigin(teamProfile.getSysOrigin())
              .setTeamId(teamProfile.getId())
              .setMemberTargetId(memberTargetId)
              .setTeamOwnId(teamProfile.getOwnUserId())
              .setTeamMemberId(target.getUserId())
              .setTeamBillId(teamBillId)
              .setTeamRegionId(teamProfile.getRegion())
              .setRemarks(Lists.newArrayList(new TeamSalaryPaymentRemark()
                      .setRemark("本次主播工资已转入代理钱包")
                      .setCreateTime(TimestampUtils.now())
                  )
              )
              .setHostSalaryToAgent(Boolean.TRUE)
              .setAnchor(Boolean.TRUE)
              .setTarget(target.getAcceptGiftValue())
              .setSalary(target.getMemberSalary())
              .setPolicyLevel(policy.getLevel())
              .setPolicy(policy)
      );

      // 发送道具
      sendProps(target.getUserId(), target.getSysOrigin(), Long.valueOf(detailsId),
          policy.getPropsRewards());

    }

  }

  /**
   * 发送团队成员工资到银行账户
   */
  private void memberBankBalancePayment(TeamProfile teamProfile, String memberTargetId,
      TeamBillMemberTarget target, Map<String, String> metadata, TeamPolicy policy,
      TeamSalaryPayment payment, Long teamBillId) {

    // 是否已经发送过该成员工资
    if (Boolean.TRUE.equals(
        checkRepeatPayment(teamProfile, payment, policy, memberTargetId, Boolean.TRUE))) {
      return;
    }

    // 是否将主播工资转给代理钱包
    if (Boolean.FALSE.equals(isHostSalaryToAgent(metadata))) {

      // 添加发送记录
      String detailsId = IdWorkerUtils.getIdStr();

      userBankBalanceClient.incr(new UserBankBalanceIncrCmd()
              .setUserId(target.getUserId())
              .setSysOrigin(target.getSysOrigin())
              .setAmount(PennyAmount.ofDollar(target.getMemberSalary()))
      );

      // 插入流水
      addUserBankRunningWater(detailsId, target.getUserId(), target.getSysOrigin(),
              target.getMemberSalary(), UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES);

      teamSalaryPaymentService.paymentSalary(payment.getId(),
              new TeamSalaryPaymentDetails()
                      .setId(detailsId)
                      .setSysOrigin(teamProfile.getSysOrigin())
                      .setTeamId(teamProfile.getId())
                      .setMemberTargetId(memberTargetId)
                      .setTeamOwnId(teamProfile.getOwnUserId())
                      .setTeamMemberId(target.getUserId())
                      .setTeamBillId(teamBillId)
                      .setTeamRegionId(teamProfile.getRegion())
                      .setRemarks(Lists.newArrayList(new TeamSalaryPaymentRemark()
                                      .setRemark("将主播工资发放至主播钱包")
                                      .setCreateTime(TimestampUtils.now())
                              )
                      )
                      .setAnchor(Boolean.TRUE)
                      .setHostSalaryToAgent(Boolean.FALSE)
                      .setTarget(target.getAcceptGiftValue())
                      .setSalary(target.getMemberSalary())
                      .setPolicyLevel(policy.getLevel())
                      .setPolicy(policy)
      );

      // 发送道具
      sendProps(target.getUserId(), target.getSysOrigin(), Long.valueOf(detailsId),
          policy.getPropsRewards());
      return;
    }

    // 将成员工资转移至代理钱包
    BigDecimal balance = userBankBalanceClient.getBalance(target.getUserId())
        .getBody().getAccurateBalance();

    // 插入流水
    try {
      userBankRunningWaterClient.addBatch(List.of(new UserBankRunningWaterDTO()
              .setId(IdWorkerUtils.getId())
              .setUserId(target.getUserId())
              .setSysOrigin(target.getSysOrigin())
              .setType(ReceiptType.INCOME.getType())
              .setAmount(target.getMemberSalary())
              .setEvent(UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES.name())
              .setEventDescribe(UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES.getDescribe())
              .setTrackId(memberTargetId)
              .setBalance(balance.add(target.getMemberSalary()).setScale(2, RoundingMode.DOWN))
              .setCreateTime(TimestampUtils.now())
              .setCreateUser(0L)
              .setCreateUserOrigin(0),

          new UserBankRunningWaterDTO()
              .setId(IdWorkerUtils.getId())
              .setUserId(target.getUserId())
              .setSysOrigin(target.getSysOrigin())
              .setType(ReceiptType.EXPENDITURE.getType())
              .setAmount(target.getMemberSalary())
              .setEvent(UserBankWaterEvent.TRANSFER.name())
              .setEventDescribe(UserBankWaterEvent.TRANSFER.getDescribe())
              .setTrackId(Objects.toString(teamProfile.getOwnUserId()))
              .setRemark("由代理代收")
              .setBalance(balance)
              .setCreateTime(TimestampUtils.now())
              .setCreateUser(0L)
              .setCreateUserOrigin(0)
      ));
    } catch (Exception e) {

      log.error("[满足政策的团队工资实时发送] 插入多条银行流水记录失败:{},TeamId:{} MemberId:{}",
          Throwables.getStackTraceAsString(e), teamProfile.getId(), target.getUserId());
    }

  }

  /**
   * 校验是否为重复发送.
   *
   * @param teamProfile    团队
   * @param payment        支付信息
   * @param policy         政策
   * @param memberTargetId 成员工作id
   * @param isPayMember    支付工资成员验证
   * @return true 重复发送, false 允许发送
   */
  private Boolean checkRepeatPayment(TeamProfile teamProfile, TeamSalaryPayment payment,
      TeamPolicy policy, String memberTargetId, Boolean isPayMember) {

//    if (Objects.equals(payment.getUserId(), 1681823277871214593L) ||
//        Objects.equals(payment.getUserId(), 1684848110911922177L) ||
//        Objects.equals(payment.getUserId(), 1630240619660009473L)) {

    return teamSalaryPaymentService
        .checkRepeatPayment(payment.getId(), payment.getUserId(), teamProfile.getRegion(),
            policy.getLevel(), memberTargetId, isPayMember);
    //}

//    return teamSalaryPaymentService
//        .checkRepeatPayment(payment.getId(), payment.getUserId(), teamProfile.getRegion(),
//            policy.getLevel(), isPayMember);
  }

  /**
   * 基于当前等级获得上一个等级政策.
   *
   * @param policyManager 政策列表
   * @param currentLevel  当前政策等级
   * @return 上一个政策
   */
  private TeamPolicy getPreviousLevelPolicy(TeamPolicyManager policyManager, Integer currentLevel) {

    return policyManager.getPolicy().stream()
        .filter(v -> v.getLevel() < currentLevel)
        .max(Comparator.comparing(TeamPolicy::getLevel))
        .orElse(new TeamPolicy()
            .setOnlineTime(0L)
            .setTarget(0L)
            .setMemberSalary(BigDecimal.ZERO)
            .setOwnSalary(BigDecimal.ZERO)
            .setTotalSalary(BigDecimal.ZERO)
            .setPropsRewards(Lists.newArrayList())
        );

  }

  /**
   * 获得当前等级政策.
   *
   * @param policyManager 政策列表
   * @param currentLevel  当前政策等级
   * @return 当前政策
   */
  private TeamPolicy getPolicyDetails(TeamPolicyManager policyManager, Integer currentLevel) {

    return policyManager.getPolicy().stream()
        .filter(v -> Objects.equals(v.getLevel(), currentLevel))
        .findFirst()
        .orElse(null);
  }

  /**
   * 获得区域
   */
  private SysRegionConfig getSysRegionConfig(TeamProfile teamProfile) {
    return sysRegionConfigService.getById(teamProfile.getRegion());
  }

  /**
   * 是否将工资都结算到代理钱包
   */
  private boolean isHostSalaryToAgent(Map<String, String> metadata) {
    return Objects.equals(metadata.get("hostSalaryToAgent"), "true");
  }

  /**
   * 是否开启每日自动结算工资.
   */
  private boolean isOpenDailyAutoSalary(Map<String, String> metadata) {
    return Objects.equals(metadata.get("openDailyAutoSalary"), "true");
  }

  /**
   * 添加钻石流水记录.
   */
  private void addUserSalaryDiamondRunningWater(String trackId, Long userId, String sysOrigin,
                                       BigDecimal salary, UserBankWaterEvent waterEvent) {
    log.warn("添加钻石流水记录");
    try {
      salaryDiamondRunningWaterClient.add(new UserSalaryDiamondRunningWaterDTO()
              .setId(IdWorkerUtils.getId())
              .setUserId(userId)
              .setSysOrigin(sysOrigin)
              .setType(ReceiptType.INCOME.getType())
              .setTrackId(trackId)
              .setAmount(salary)
              .setSalaryEvent(waterEvent.name())
              .setEventDesc(waterEvent.getDescribe())
              .setRemark("")
              .setBalance(
                      ResponseAssert.requiredSuccess(
                              salaryDiamondBalanceClient.getBalance(userId)).getBalance())
              .setCreateTime(TimestampUtils.now())
              .setCreateUser(0L)
              .setCreateUserOrigin(0)
      );

    } catch (Exception e) {

      log.error("[满足政策的团队工资实时发送] 插入单条钻石流水记录失败:{},userId:{} waterEvent:{}",
              Throwables.getStackTraceAsString(e), userId, waterEvent.name());
    }

  }

  /**
   * 添加流水记录.
   */
  private void addUserBankRunningWater(String trackId, Long userId, String sysOrigin,
      BigDecimal salary, UserBankWaterEvent waterEvent) {

    try {

      userBankRunningWaterClient.add(new UserBankRunningWaterDTO()
          .setId(IdWorkerUtils.getId())
          .setUserId(userId)
          .setSysOrigin(sysOrigin)
          .setType(ReceiptType.INCOME.getType())
          .setAmount(salary)
          .setEvent(waterEvent.name())
          .setEventDescribe(waterEvent.getDescribe())
          .setTrackId(trackId)
          .setRemark("")
          .setBalance(userBankBalanceClient.getBalance(userId).getBody().getAccurateBalance())
          .setCreateTime(TimestampUtils.now())
          .setCreateUser(0L)
          .setCreateUserOrigin(0));

    } catch (Exception e) {

      log.error("[满足政策的团队工资实时发送] 插入单条银行流水记录失败:{},userId:{} waterEvent:{}",
          Throwables.getStackTraceAsString(e), userId, waterEvent.name());
    }

  }

  /**
   * 发送道具.
   *
   * @param acceptUserId 用户ID.
   * @param propsRewards 道具组.
   */
  private void sendProps(Long acceptUserId, String sysOrigin,
      Long trackId, List<ActivityRewardPropsDTO> propsRewards) {

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

    propsSendCommon.send(
        new PrizeDescribeRewardCmd()
            .setTrackId(trackId)
            .setAcceptUserId(acceptUserId)
            .setOrigin(SendPropsOrigin.POLICY_REWARDS)
            .setSysOrigin(SysOriginPlatformEnum.valueOf(sysOrigin))
            .setPrizes(propsRewards.stream().map(props -> new PrizeDescribe()
                    .setTrackId(acceptUserId)
                    .setType(props.getType())
                    .setDetailType(props.getDetailType())
                    .setContent(props.getContent())
                    .setQuantity(props.getQuantity())
                ).toList()
            )
    );

  }

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


  private List<TeamMemberTarget> getTeamMemberTargets(TeamBillCycle billCycle) {

    return teamMemberTargetService
        .listByBillBelong(billCycle.getTeamId(), billCycle.getBillBelong());
  }


  /**
   * 增加区域赠策.
   */
  private void updatePolicyMap(TeamPolicyManager policyManager, SysRegionConfig region,
      TeamSalaryPayment payment) {

    Map<String, TeamPolicyManager> managerMap = teamSalaryPaymentService.mapPolicy(payment.getId());
    managerMap.put(region.getId(), policyManager);
    teamSalaryPaymentService.updatePolicyMap(payment.getId(), managerMap);
  }

  /**
   * 获得用户工资发放凭证,没有则创建.
   */
  private TeamSalaryPayment getCreateIfAbsentPayment(TeamMember member,
      TeamSalaryPaymentEvent event) {

    if (Boolean.TRUE.equals(event.getThisMonthFirstDay())) {

      return teamSalaryPaymentService
          .createIfAbsent(member.getMemberId(), event.getLastMonth(), member.getSysOrigin());
    }

    return teamSalaryPaymentService
        .createIfAbsent(member.getMemberId(), ZonedDateTimeAsiaRiyadhUtils.nowYearMonthToInt(),
            member.getSysOrigin());
  }

  /**
   * 获得区域政策.
   */
  private TeamPolicyManager getReleaseByRegionAndType(TeamProfile teamProfile) {

    TeamPolicyManager manager = teamPolicyManagerService.getReleaseByRegionAndType(
        teamProfile.getSysOrigin(), teamProfile.getRegion());

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

    if (CollectionUtils.isEmpty(manager.getPolicy())) {
      return null;
    }

    return manager;

  }

  /**
   * 获得达到的所有政策等级并按等级生序排列.
   */
  private List<TeamPolicy> listCompletePolicy(TeamPolicyManager policyManager,
      TeamBillMemberTarget billMemberTarget) {

    if (Objects.isNull(billMemberTarget.getLevel())) {
      return Lists.newArrayList();
    }

    return policyManager.getPolicy().stream()
        .filter(v -> {

          if (Objects.isNull(v.getLevel())) {
            return Boolean.FALSE;
          }

          return v.getLevel() <= billMemberTarget.getLevel();
        })
        .sorted(Comparator.comparing(TeamPolicy::getLevel)).collect(
            Collectors.toList());
  }

  /**
   * 组装辅助工资模型.
   */
  private TeamBillMemberTarget buildTmpBillMemberTarget(TeamPolicy policy,
      TeamBillMemberTarget memberTarget) {

    return new TeamBillMemberTarget()
        .setTargetId(memberTarget.getTargetId())
        .setSysOrigin(memberTarget.getSysOrigin())
        .setLevel(policy.getLevel())
        .setSatisfyLevel(memberTarget.getSatisfyLevel())
        .setUserId(memberTarget.getUserId())
        .setAcceptGiftValue(memberTarget.getAcceptGiftValue())
        .setGiveGiftValue(memberTarget.getGiveGiftValue())
        .setRoomValue(memberTarget.getRoomValue())
        .setOnlineTime(memberTarget.getOnlineTime())
        .setMemberSalary(policy.getMemberSalary())
        .setOwnSalary(policy.getOwnSalary())
        .setTotalSalary(memberTarget.getTotalSalary())
        .setPropsRewards(policy.getPropsRewards());
  }

  /**
   * 实际应发工资
   *
   * @param billMemberTarget 初步预发工资
   * @param previousPolicy   上一份政策工资
   */
  private void calculateActualSalary(TeamBillMemberTarget billMemberTarget,
      TeamPolicy previousPolicy) {

    // 计算完成后 billMemberTarget 就是实际应发工资
    billMemberTarget.setMemberSalary(
        billMemberTarget.getMemberSalary().subtract(previousPolicy.getMemberSalary()));
    billMemberTarget
        .setOwnSalary(billMemberTarget.getOwnSalary().subtract(previousPolicy.getOwnSalary()));
    billMemberTarget.setTotalSalary(
        billMemberTarget.getTotalSalary().subtract(previousPolicy.getTotalSalary()));
  }


}
