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

import com.google.common.collect.Lists;
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.dto.ResultResponse;
import com.red.circle.framework.web.spring.RequestCallable;
import com.red.circle.mq.business.model.event.team.TeamSalaryCountEvent;
import com.red.circle.mq.rocket.business.streams.TeamSalaryCountSink;
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.entity.team.team.TeamBillCycle;
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.user.region.SysRegionConfig;
import com.red.circle.other.infra.database.mongo.service.team.team.TeamBillCycleService;
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.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.user.region.SysRegionConfigService;
import com.red.circle.other.infra.database.rds.entity.team.BillDiamondBalance;
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.inner.enums.team.TeamPolicyTypeEnum;
import com.red.circle.other.inner.model.cmd.team.TeamBillCmd;
import com.red.circle.tool.core.collection.CollectionUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 团队工资实时统计.
 *
 * @author pengshigang on 2023/4/10
 */
@Slf4j
@RocketMqMessageListener(groupId = TeamSalaryCountSink.INPUT, tag = TeamSalaryCountSink.TAG)
@RequiredArgsConstructor
public class TeamSalaryCountListener implements MessageListener {

  private final TeamProfileService teamProfileService;
  private final MessageEventProcess messageEventProcess;
  private final TeamBillCycleService teamBillCycleService;
  private final TeamMemberTargetService teamMemberTargetService;
  private final TeamPolicyManagerService teamPolicyManagerService;
  private final SysRegionConfigService sysRegionConfigService;
  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(TeamSalaryCountSink.TAG)
            .message(message)
            .build(),
        TeamSalaryCountEvent.class,
        this::business);
  }

  private void business(TeamSalaryCountEvent event) {
    //log.warn("团队工资实时统计{}", event);
    List<TeamProfile> teamProfiles = listTeamProfile(event);
    if (CollectionUtils.isEmpty(teamProfiles)) {
      log.warn("[团队工资实时统计消息]根据入参找不到团队数据,团队ID:{}", event.getTeamIds());
      return;
    }

    teamProfiles.forEach(teamProfile -> {

      if (Boolean.FALSE.equals(teamProfileService.checkStatusAvailable(teamProfile.getId()))) {
        log.warn("[团队工资实时统计消息]该用户没有可用的代理团队,团队ID:{}", teamProfile.getId());
        return;
      }

      teamBillCycleService.createIfAbsent(new TeamBillCmd()
          .setSysOrigin(teamProfile.getSysOrigin())
          .setTeamId(teamProfile.getId())
          .setRegion(teamProfile.getRegion())
          .setOperationTime(TimestampUtils.now())
          .setOperationBackUser(teamProfile.getCreateUser()));

      TeamBillCycle teamBillCycle = getTeamUnpaidBill(teamProfile.getId());
      if (Objects.isNull(teamBillCycle)) {
        log.warn("[团队工资实时统计消息]该团队没有未出账账单数据,团队ID:{}", teamProfile.getId());
        return;
      }

      TeamPolicyManager regionPolicyManager = teamPolicyManagerService.getReleaseByRegionAndType(
          teamBillCycle.getSysOrigin(), teamBillCycle.getRegion()
      );

      if (Objects.isNull(regionPolicyManager)) {
        log.warn("[团队工资实时统计消息]该团队没有区域政策,团队ID:{}", teamProfile.getId());
        return;
      }

      try {

        List<TeamMemberTarget> teamMemberTargets = getTeamMemberTargets(teamBillCycle);

        List<TeamBillMemberTarget> teamBillMemberTargets = calculatorTarget(teamMemberTargets,
            regionPolicyManager);

        // 结算账单.
        teamBillCycleService.updateSelective(new TeamBillCycle()
            .setId(teamBillCycle.getId())
            .setSettleResult(new TeamBillSettleResult()
                .setPolicy(regionPolicyManager)
                .setWorkMembers(countWorkMembers(teamMemberTargets))
                .setFinishMembers(teamBillMemberTargets.size())
                .setMemberSalary(sumMemberSalary(teamBillMemberTargets))
                .setOwnSalary(ownSalary(teamBillMemberTargets))
                .setTotalSalary(ownTotalSalary(teamBillMemberTargets))
            )
        );
      } catch (Exception ex) {
        log.error("[团队工资实时统计消息]计算账单错误,团队ID:{}", teamProfile.getId());
      }

    });
  }

  /**
   * 插入账单数据
   *
   * @param teamBillMemberTargets
   * @param teamProfile
   * @param teamBillCycle
   */
  private void addBillDiamondBalance(List<TeamBillMemberTarget> teamBillMemberTargets, TeamProfile teamProfile, TeamBillCycle teamBillCycle) {
    teamBillMemberTargets.forEach(item -> {
      billDiamondBalanceService.add(new BillDiamondBalance()
              .setTeamId(teamProfile.getId())
              .setUserId(item.getUserId())
              .setSysOrigin(item.getSysOrigin())
              .setBillBelong(teamBillCycle.getBillBelong())
              .setEarnPoints(item.getMemberSalary())
              .setBalance(item.getMemberSalary()));

      billDiamondBalanceDetailsService.add(new BillDiamondBalanceDetails()
              .setTeamId(teamProfile.getId())
              .setUserId(item.getUserId())
              .setSysOrigin(item.getSysOrigin())
              .setBillBelong(teamBillCycle.getBillBelong())
              .setBillBelongDay(ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
              .setEarnPoints(item.getMemberSalary())
              .setBalance(item.getMemberSalary()));
    });

    // 如果是代理添加代理的那一份
    billDiamondBalanceService.addAgent(new BillDiamondBalance()
            .setTeamId(teamProfile.getId())
            .setUserId(teamProfile.getOwnUserId())
            .setSysOrigin(teamProfile.getSysOrigin())
            .setBillBelong(teamBillCycle.getBillBelong())
            .setAgentEarnPoints(ownSalary(teamBillMemberTargets))
            .setAgentBalance(ownSalary(teamBillMemberTargets)));

    // 如果是代理添加代理的那一份
    billDiamondBalanceDetailsService.addAgent(new BillDiamondBalanceDetails()
            .setTeamId(teamProfile.getId())
            .setUserId(teamProfile.getOwnUserId())
            .setSysOrigin(teamProfile.getSysOrigin())
            .setBillBelong(teamBillCycle.getBillBelong())
            .setBillBelongDay(ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
            .setAgentEarnPoints(ownSalary(teamBillMemberTargets))
            .setAgentBalance(ownSalary(teamBillMemberTargets)));
  }

  /**
   * 插入账单数据，这里有时间要改造成批量插入
   *
   * @param teamBillMemberTargets
   * @param teamProfile
   * @param teamBillCycle
   */
  private void addDayBillDiamondBalance(List<TeamBillMemberTarget> teamBillMemberTargets, TeamProfile teamProfile, TeamBillCycle teamBillCycle) {
    teamBillMemberTargets.forEach(item -> {
      if (billDiamondBalanceDetailsService.existsBillBelongDay(item.getUserId(), ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())) {
        // 如果有查询到有日账单，说明不是新的一天，累计剩余金额
        billDiamondBalanceService.add(new BillDiamondBalance()
                .setTeamId(teamProfile.getId())
                .setUserId(item.getUserId())
                .setSysOrigin(item.getSysOrigin())
                .setBillBelong(teamBillCycle.getBillBelong())
                .setEarnPoints(item.getMemberSalary())
                .setBalance(item.getMemberSalary()));
        if (Objects.equals(item.getUserId(), teamProfile.getOwnUserId())) {
          billDiamondBalanceService.addAgent(new BillDiamondBalance()
                  .setTeamId(teamProfile.getId())
                  .setUserId(teamProfile.getOwnUserId())
                  .setSysOrigin(teamProfile.getSysOrigin())
                  .setBillBelong(teamBillCycle.getBillBelong())
                  .setAgentEarnPoints(ownSalary(teamBillMemberTargets))
                  .setAgentBalance(ownSalary(teamBillMemberTargets)));
        }
      } else {
        // 如果没有查询到日账单，说明是新的一天，清空已兑钻石
        billDiamondBalanceService.addBalance(new BillDiamondBalance()
                .setTeamId(teamProfile.getId())
                .setUserId(item.getUserId())
                .setSysOrigin(item.getSysOrigin())
                .setBillBelong(teamBillCycle.getBillBelong())
                .setEarnPoints(item.getMemberSalary())
                .setBalance(item.getMemberSalary()));
        if (Objects.equals(item.getUserId(), teamProfile.getOwnUserId())) {
          billDiamondBalanceService.addAgentBalance(new BillDiamondBalance()
                  .setTeamId(teamProfile.getId())
                  .setUserId(teamProfile.getOwnUserId())
                  .setSysOrigin(teamProfile.getSysOrigin())
                  .setBillBelong(teamBillCycle.getBillBelong())
                  .setAgentEarnPoints(ownSalary(teamBillMemberTargets))
                  .setAgentBalance(ownSalary(teamBillMemberTargets)));
        }
      }

      billDiamondBalanceDetailsService.add(new BillDiamondBalanceDetails()
              .setTeamId(teamProfile.getId())
              .setUserId(item.getUserId())
              .setSysOrigin(item.getSysOrigin())
              .setBillBelong(teamBillCycle.getBillBelong())
              .setBillBelongDay(ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
              .setEarnPoints(item.getMemberSalary())
              .setBalance(item.getMemberSalary()));
    });

    // 如果是代理添加代理的那一份
    billDiamondBalanceDetailsService.addAgent(new BillDiamondBalanceDetails()
            .setTeamId(teamProfile.getId())
            .setUserId(teamProfile.getOwnUserId())
            .setSysOrigin(teamProfile.getSysOrigin())
            .setBillBelong(teamBillCycle.getBillBelong())
            .setBillBelongDay(ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())
            .setAgentEarnPoints(ownSalary(teamBillMemberTargets))
            .setAgentBalance(ownSalary(teamBillMemberTargets)));
  }

  private Map<String, String> getMetadata(SysRegionConfig regionConfig) {
    return Optional.ofNullable(regionConfig)
            .map(SysRegionConfig::getMetadata).orElseGet(CollectionUtils::newHashMap);
  }

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

  private List<TeamBillMemberTarget> calculatorTarget(List<TeamMemberTarget> teamMemberTargets,
      TeamPolicyManager regionPolicyManager) {
    //2fun新增钻石政策
    if (Objects.isNull(regionPolicyManager.getPolicyType()) || Objects.equals(TeamPolicyTypeEnum.MONEY.name(), regionPolicyManager.getPolicyType())) {
      return TeamBillCycleUtils.calculatorPolicyTarget(teamMemberTargets, regionPolicyManager);
    }

    return TeamBillCycleUtils.calculatorPolicyDiamondTarget(teamMemberTargets, regionPolicyManager);
  }

  private List<TeamBillMemberTarget> calculatorDayTarget(List<TeamMemberTarget> teamMemberTargets,
                                                      TeamPolicyManager regionPolicyManager) {
    return TeamBillCycleUtils.calculatorPolicyDiamondDayTarget(teamMemberTargets, regionPolicyManager);
  }

  private List<TeamProfile> listTeamProfile(TeamSalaryCountEvent event) {

    if (CollectionUtils.isEmpty(event.getTeamIds())) {
      // [团队工资实时统计消息]入参为null
      return Lists.newArrayList();
    }

    return Optional.ofNullable(teamProfileService.listStatusAvailable(event.getTeamIds()))
        .orElse(Lists.newArrayList());
  }

  private List<TeamMemberTarget> getTeamMemberTargets(TeamBillCycle teamBillCycle) {
    return teamMemberTargetService.listByBillBelong(
        teamBillCycle.getTeamId(), teamBillCycle.getBillBelong());
  }

  private BigDecimal ownTotalSalary(List<TeamBillMemberTarget> teamBillMemberTargets) {
    return teamBillMemberTargets.stream().map(TeamBillMemberTarget::getTotalSalary)
        .reduce(BigDecimal.ZERO, BigDecimal::add)
        .setScale(2, RoundingMode.DOWN);
  }

  private BigDecimal ownSalary(List<TeamBillMemberTarget> teamBillMemberTargets) {
    return teamBillMemberTargets.stream().map(TeamBillMemberTarget::getOwnSalary)
        .reduce(BigDecimal.ZERO, BigDecimal::add)
        .setScale(2, RoundingMode.DOWN);
  }

  private BigDecimal sumMemberSalary(List<TeamBillMemberTarget> teamBillMemberTargets) {
    return teamBillMemberTargets.stream().map(TeamBillMemberTarget::getMemberSalary)
        .reduce(BigDecimal.ZERO, BigDecimal::add)
        .setScale(2, RoundingMode.DOWN);
  }

  private int countWorkMembers(List<TeamMemberTarget> teamMemberTargets) {
    return CollectionUtils.isNotEmpty(teamMemberTargets) ? teamMemberTargets.size() : 0;
  }

  private TeamBillCycle getTeamUnpaidBill(Long teamId) {
    return teamBillCycleService.getTeamUnpaidBill(teamId);
  }

}
