package com.red.circle.other.app.listener.gift.strategy;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.mq.MessageEventProcess;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.mq.business.model.event.gift.OfflineProcessGiftEvent;
import com.red.circle.mq.business.model.event.team.TeamSalaryCountEvent;
import com.red.circle.mq.rocket.business.producer.TeamSalaryMqMessage;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.infra.common.team.TeamTargetCommon;
import com.red.circle.other.infra.database.mongo.dto.team.TeamBillMemberTarget;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftAcceptUser;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftGiveRunningWater;
import com.red.circle.other.infra.database.mongo.entity.gift.GiftValue;
import com.red.circle.other.infra.database.mongo.entity.live.RoomProfile;
import com.red.circle.other.infra.database.mongo.entity.team.team.*;
import com.red.circle.other.infra.database.mongo.entity.user.region.SysRegionConfig;
import com.red.circle.other.infra.database.mongo.service.activity.ActivityAgentAnchorCountService;
import com.red.circle.other.infra.database.mongo.service.activity.AgentActivityCountService;
import com.red.circle.other.infra.database.mongo.service.gift.GiftGiveRunningWaterService;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.mongo.service.team.TeamBillCycleUtils;
import com.red.circle.other.infra.database.mongo.service.team.team.*;
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.activity.PropsActivityTypeEnum;
import com.red.circle.other.inner.enums.team.TeamPolicyTypeEnum;
import com.red.circle.other.inner.enums.user.RegionAssistTypeEnum;
import com.red.circle.other.inner.model.dto.agency.agency.TeamTargetBillRes;
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.num.ArithmeticUtils;
import com.red.circle.tool.core.regex.RegexConstant;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.wallet.inner.endpoint.diamond.SalaryDiamondBalanceClient;
import com.red.circle.wallet.inner.endpoint.diamond.SalaryDiamondRunningWaterClient;
import com.red.circle.wallet.inner.endpoint.wallet.WalletDiamondClient;
import com.red.circle.wallet.inner.endpoint.wallet.api.WalletDiamondClientApi;
import com.red.circle.wallet.inner.model.cmd.UserSalaryDiamondCmd;
import com.red.circle.wallet.inner.model.dto.UserSalaryDiamondRunningWaterDTO;
import com.red.circle.wallet.inner.model.enums.UserBankWaterEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 主播统计相关.
 *
 * @author pengliang on 2022/7/26
 */
@Slf4j
@Service("GIFT_ANCHOR_COUNT_LISTENER")
@RequiredArgsConstructor
public class GiftAnchorCountStrategy implements GiftStrategy {

  private final TeamTargetCommon teamTargetCommon;
  private final TeamMemberService teamMemberService;
  private final TeamProfileService teamProfileService;
  private final TeamSalaryMqMessage teamSalaryMqMessage;
  private final AgentActivityCountService agentActivityCountService;
  private final RoomProfileManagerService roomProfileManagerService;
  private final GiftGiveRunningWaterService giftGiveRunningWaterService;
  private final ActivityAgentAnchorCountService activityAgentAnchorCountService;
  private final SalaryDiamondBalanceClient salaryDiamondBalanceClient;
  private final SalaryDiamondRunningWaterClient salaryDiamondRunningWaterClient;
  private final UserRegionGateway userRegionGateway;
  private final BillDiamondBalanceService billDiamondBalanceService;
  private final BillDiamondBalanceDetailsService billDiamondBalanceDetailsService;
  private final MessageEventProcess messageEventProcess;
  private final TeamBillCycleService teamBillCycleService;
  private final TeamMemberTargetService teamMemberTargetService;
  private final TeamPolicyManagerService teamPolicyManagerService;
  private final SysRegionConfigService sysRegionConfigService;

  @Override
  public void processor(OfflineProcessGiftEvent event) {

    log.warn("【主播统计相关】找到流水信息，处理---{}", JSON.toJSONString(event));
    GiftGiveRunningWater runningWater = giftGiveRunningWaterService.getByIdPrimary(
        event.getRunningWaterId());

    if (Objects.isNull(runningWater)) {
      log.warn("【主播统计】没有找到流水信息，忽律处理");
      return;
    }
//    giftGiveRunningWaterService.addLog(runningWater.getId(), "开始：主播统计相关");

    GiftValue giftValue = runningWater.getGiftValue();

    TeamMember sendMember = teamMemberService.getByMemberId(runningWater.getUserId());

    if (runningWater.getOriginId().matches(RegexConstant.NUMBER)) {
      RoomProfile roomProfile = roomProfileManagerService
          .getProfileById(Long.parseLong(runningWater.getOriginId()));

      // 主播房间贡献数值统计
      if (Objects.nonNull(roomProfile) && Objects.equals(roomProfile.getUserId(), runningWater
          .getUserId()) && Objects.nonNull(sendMember)) {

        teamTargetCommon
            .incrementRoomValue(roomProfile.getUserId(), giftValue.getActualAmount());
      }
    }

    // 主播发送礼物金额
    Set<Long> teamIds = Sets.newHashSet();
    if (Objects.nonNull(sendMember)) {

      teamIds.add(sendMember.getTeamId());
      teamTargetCommon
          .incrementSendGiftQuantity(runningWater.getUserId(), giftValue.getActualAmount());
    }

    // 主播接收目标统计
    List<GiftAcceptUser> giftAcceptUsers = getGiftAcceptAnchorUsers(runningWater);
    //log.warn("giftAcceptUsers：{}", giftAcceptUsers);

    if (CollectionUtils.isNotEmpty(giftAcceptUsers)) {

      // 根据接受人id获得其中的主播成员信息
      Map<Long, TeamMember> teamMemberMap = getTeamMemberMap(giftAcceptUsers);

      giftAcceptUsers.forEach(acceptAnchorUser -> {

        if (ArithmeticUtils.lteZero(acceptAnchorUser.getTargetAmount())) {
          giftGiveRunningWaterService.updateAcceptUserCountTargetAmountTrue(
              runningWater.getId(), 0L, acceptAnchorUser.getAcceptUserId());
          return;
        }

        countTarget(runningWater, acceptAnchorUser);

        TeamMember teamMember = teamMemberMap.get(acceptAnchorUser.getAcceptUserId());

        if (Objects.nonNull(teamMember)) {

          teamIds.add(teamMember.getTeamId());

          // 团队内 -> 代理主播活动（永久活动）
//          activityAgentAnchorCount(acceptAnchorUser.getTargetAmount().longValue(),
//              acceptAnchorUser.getAcceptUserId(), teamMember.getTeamId());
          // 代理活动(永久活动)
//          agentActivityCount(acceptAnchorUser, teamMember.getTeamId());
        }
      });

    }
    //yoloSalaryDiamondCount(runningWater, giftAcceptUsers.get(0));
    log.warn("发送通知,更新代理工资数据，团队Ids：{}", teamIds);
    // 发送通知,更新代理工资数据
    if (CollectionUtils.isNotEmpty(teamIds)) {
//      teamSalaryMqMessage.sendSalaryCount(teamIds);
      //teamSalaryMqMessage.sendSalary(teamIds, false);
    }
//    giftGiveRunningWaterService.addLog(runningWater.getId(), "结束：主播统计相关");

//    CompletableFuture.runAsync(() -> {
//      log.warn("异步同步钻石情况{}", teamIds);
//      initProfilesDiamond(teamIds, giftAcceptUsers);
//    });
  }


  /**
   * 插入账单数据
   *
   * @param teamBillMemberTargets
   * @param teamProfile
   * @param teamBillCycle
   */
  private void addBillDiamondBalance(List<TeamBillMemberTarget> teamBillMemberTargets, TeamProfile teamProfile
          , TeamBillCycle teamBillCycle, List<Long> giftAcceptUser) {
    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, List<Long> giftAcceptUser) {
    for(TeamBillMemberTarget item : teamBillMemberTargets) {
      if (!giftAcceptUser.contains(item.getUserId()) || !giftAcceptUser.contains(teamProfile.getOwnUserId())) {
        continue;
      }
      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()));
    }

    TeamBillMemberTarget target = teamBillMemberTargets.stream().filter(item -> item.getUserId().equals(teamProfile.getOwnUserId())).findFirst().orElse(null);
    if (target == null) {
      if (billDiamondBalanceDetailsService.existsBillBelongDay(teamProfile.getOwnUserId(), ZonedDateTimeAsiaRiyadhUtils.nowDateToInt())) {
        // 如果代理没工作给代理计算提成
        billDiamondBalanceService.addAgent(new BillDiamondBalance()
                .setTeamId(teamProfile.getId())
                .setUserId(teamProfile.getOwnUserId())
                .setSysOrigin(teamProfile.getSysOrigin())
                .setBillBelong(teamBillCycle.getBillBelong())
                .setAgentEarnPoints(ownSalary(teamBillMemberTargets))
                .setAgentBalance(ownSalary(teamBillMemberTargets)));
      } else {
        billDiamondBalanceService.addAgentBalance(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)));
  }

  private static List<GiftAcceptUser> getGiftAcceptAnchorUsers(GiftGiveRunningWater runningWater) {
    return runningWater.getAcceptUsers().stream()
        .filter(water -> Objects.equals(water.getAnchor(), Boolean.TRUE))
        .toList();
  }

  private void agentActivityCount(GiftAcceptUser acceptAnchorUser, Long teamId) {
    TeamProfile teamProfile = teamProfileService.getById(teamId);
    if (Objects.isNull(teamProfile)) {
      return;
    }
    agentActivityCountService.incrTarget(teamId,
        acceptAnchorUser.getTargetAmount().longValue(), teamProfile.getRegion());
  }

  private Map<Long, TeamMember> getTeamMemberMap(List<GiftAcceptUser> giftAcceptUsers) {
    return Optional.ofNullable(teamMemberService
            .mapByMemberIds(giftAcceptUsers.stream().map(GiftAcceptUser::getAcceptUserId).collect(
                Collectors.toSet())))
        .orElseGet(CollectionUtils::newHashMap);
  }

  private void countTarget(GiftGiveRunningWater runningWater, GiftAcceptUser acceptAnchorUser) {
    // 累计目标
    TeamTargetBillRes teamTargetBillRes = teamTargetCommon.incrementGiftValue(
        acceptAnchorUser.getAcceptUserId(),
        acceptAnchorUser.getTargetAmount());

    if (Boolean.TRUE.equals(teamTargetBillRes.getSuccess())) {
      // 标记已被统计
      giftGiveRunningWaterService.updateAcceptUserCountTargetAmountTrue(
          runningWater.getId(),
          teamTargetBillRes.getBillId(),
          acceptAnchorUser.getAcceptUserId());
    }
  }

  /**
   * 代理主播活动（永久活动）
   */
  private void activityAgentAnchorCount(Long targetAmount, Long acceptUserId, Long teamId) {

    // 主播月收礼，永久活动
    activityAgentAnchorCountService.incr(targetAmount, acceptUserId,
        PropsActivityTypeEnum.ACTIVE_ANCHOR_MONTH_TARGET_REWARD.name(),
        getThisMonthDate());

    // 主播日收礼，永久活动
    activityAgentAnchorCountService.incr(targetAmount, acceptUserId,
        PropsActivityTypeEnum.ACTIVE_ANCHOR_DAY_TARGET_REWARD.name(),
        getThisDayDate());

    // 代理名下主播当月总积分, 永久活动
    activityAgentAnchorCountService.incr(targetAmount, teamId,
        PropsActivityTypeEnum.ACTIVE_AGENT_MONTH_TARGET_REWARD.name(),
        getThisMonthDate());
  }

  private Integer getThisMonthDate() {
    return ZonedDateTimeAsiaRiyadhUtils.nowYearMonthToInt();
  }

  private Integer getThisDayDate() {
    return ZonedDateTimeAsiaRiyadhUtils.nowDateToInt();
  }

  /**
   * yolo钻石工资统计
   *
   * @param runningWater
   * @param acceptAnchorUser
   */
  private void yoloSalaryDiamondCount(GiftGiveRunningWater runningWater, GiftAcceptUser acceptAnchorUser) {
    log.warn("yolo钻石入账:" + runningWater + "\n" + acceptAnchorUser);
    if (!Objects.equals(runningWater.getSysOrigin(), SysOriginPlatformEnum.YOLO.name())) {
      // 如果不是YOLO就停止入账
      return;
    }
    BigDecimal ratio;
    if (Objects.equals(runningWater.getUserId(), acceptAnchorUser.getAcceptUserId())) {
      ratio = getAssistGiftToOwnGoldRatio(runningWater.getOriginId());
    } else {
      ratio = getAssistGiftToOtherGoldRatio(runningWater.getOriginId());
    }

    BigDecimal quantity = runningWater.getGiftValue().getUnitPrice().multiply(ratio).setScale(0, RoundingMode.DOWN);

    salaryDiamondBalanceClient.changeSalaryBalanceAsync(new UserSalaryDiamondCmd()
            .setUserId(acceptAnchorUser.getAcceptUserId())
            .setQuantity(quantity)
            .setSysOrigin(runningWater.getSysOrigin()));

    salaryDiamondRunningWaterClient.addAsync(new UserSalaryDiamondRunningWaterDTO()
            .setId(IdWorkerUtils.getId())
            .setUserId(acceptAnchorUser.getAcceptUserId())
            .setSysOrigin(runningWater.getSysOrigin())
            .setType(ReceiptType.INCOME.getType())
            .setTrackId(runningWater.getId()+"")
            .setAmount(quantity)
            .setSalaryEvent(UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES.name())
            .setEventDesc(UserBankWaterEvent.SYSTEM_AUTOMATIC_SETTLEMENT_WAGES.getDescribe())
            .setRemark("")
            .setBalance(
                    ResponseAssert.requiredSuccess(
                            salaryDiamondBalanceClient.getBalance(acceptAnchorUser.getAcceptUserId())).getBalance())
            .setCreateTime(TimestampUtils.now())
            .setCreateUser(0L)
            .setCreateUserOrigin(0)
    );
  }

  /**
   * 赠送礼物给别人返还金币比例.
   */
  private BigDecimal getAssistGiftToOtherGoldRatio(String regionId) {
    return userRegionGateway.getAssistGiftToOtherGoldRatio(regionId);
  }

  /**
   * 赠送礼物给别人返还金币比例.
   */
  private BigDecimal getAssistGiftToOwnGoldRatio(String regionId) {
    return userRegionGateway.getAssistGiftToOwnGoldRatio(regionId);
  }

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

  /**
   * 获取区域配置
   */
  private Map<String, String> getMetadata(SysRegionConfig regionConfig) {
    return Optional.ofNullable(regionConfig)
            .map(SysRegionConfig::getMetadata).orElseGet(CollectionUtils::newHashMap);
  }


  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);
  }

}
