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

import com.red.circle.common.business.dto.cmd.AppExtCommand;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.dto.CommonCommand;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.team.EntryBdCO;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.mongo.dto.team.BdRoleEnum;
import com.red.circle.other.infra.database.mongo.entity.team.team.TeamBillCycle;
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.user.region.SysRegionConfigService;
import com.red.circle.other.infra.database.rds.entity.team.BusinessDevelopmentBaseInfo;
import com.red.circle.other.infra.database.rds.entity.team.RoomBdLead;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentBaseInfoService;
import com.red.circle.other.infra.database.rds.service.team.BusinessDevelopmentTeamService;
import com.red.circle.other.infra.database.rds.service.team.RoomBdLeadService;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.enums.team.TeamBillCycleStatus;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.wallet.inner.endpoint.bank.UserBankBalanceClient;
import com.red.circle.wallet.inner.model.dto.BankBalanceDTO;
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.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * <p>
 * 进入BD系统.
 * </p>
 *
 * @author pengshigang
 * @since 2023-4-7
 */
@Component
@RequiredArgsConstructor
public class BdEnterSysExe {

  private final RoomBdLeadService roomBdLeadService;
  private final UserProfileGateway userProfileGateway;
  private final TeamBillCycleService teamBillCycleService;
  private final UserBankBalanceClient userBankBalanceClient;
  private final SysRegionConfigService sysRegionConfigService;
  private final UserProfileAppConvertor userProfileAppConvertor;
  private final BusinessDevelopmentTeamService businessDevelopmentTeamService;
  private final BusinessDevelopmentBaseInfoService businessDevelopmentBaseInfoService;

  public EntryBdCO execute(AppExtCommand cmd) {

    Set<Long> teamIds = getTeamIdsByBdUserId(cmd);

    EntryBdCO entryBd = new EntryBdCO()
        .setBdQuantity(0L)
        .setBalance(BigDecimal.ZERO)
        .setUserProfile(getByUserId(cmd.requiredReqUserId()))
        .setTeamQuantity((long) teamIds.size())
        .setRole("NONE");

    if (CollectionUtils.isNotEmpty(teamIds)) {
      entryBd.setTeamLastMonthSalary(getTeamLastMonthSalary(teamIds));
      entryBd.setTeamThisMonthSalary(getTeamThisMonthSalary(teamIds));
    }

    RoomBdLead roomBdLead = roomBdLeadService.getByUserId(cmd.requiredReqUserId());
    if (Objects.nonNull(roomBdLead)) {
      entryBd.setRole(BdRoleEnum.BD_LEADER.name());
      entryBd.setBdQuantity(getBdCountByLeadUserId(cmd));
      entryBd.setTeamQuantity(getCountTeamByBdUserId(cmd));
      entryBd.setBalance(getBankBalance(cmd));
      entryBd.setMetadata(getMetadata(roomBdLead.getRegionId()));
      entryBd.setDiamondMetadata(getDiamondMetadata(roomBdLead.getRegionId()));
      return entryBd;
    }

    BusinessDevelopmentBaseInfo bd = businessDevelopmentBaseInfoService.getByUserId(
        cmd.requiredReqUserId());
    if (Objects.nonNull(bd)) {
      entryBd.setRole(BdRoleEnum.BD.name());
      entryBd.setTeamQuantity(getCountTeamByBdUserId(cmd));
      entryBd.setBalance(getBankBalance(cmd));
      entryBd.setMetadata(getMetadata(bd.getRegion()));
      entryBd.setDiamondMetadata(getDiamondMetadata(bd.getRegion()));
      return entryBd;
    }

    return entryBd;
  }

  private BigDecimal getTeamThisMonthSalary(Set<Long> teamIds) {

    return getMonthSalary(teamBillCycleService.listByTeamIds(teamIds, TeamBillCycleStatus.UNPAID));
  }

  private BigDecimal getTeamLastMonthSalary(Set<Long> teamIds) {

    return getMonthSalary(teamBillCycleService.listLastMonthByTeamIds(teamIds));
  }

  private static BigDecimal getMonthSalary(List<TeamBillCycle> cycles) {

    if (CollectionUtils.isEmpty(cycles)) {
      return BigDecimal.ZERO;
    }

    BigDecimal tmp = BigDecimal.valueOf(100);

    long salary = cycles.stream().mapToLong(billCycle -> {

      if (Objects.isNull(billCycle) || Objects.isNull(billCycle.getSettleResult())) {
        return 0;
      }

      BigDecimal totalSalary = billCycle.getSettleResult().getTotalSalary();

      if (Objects.isNull(totalSalary) || totalSalary.compareTo(BigDecimal.ZERO) <= 0) {
        return 0;
      }

      return totalSalary.multiply(tmp).setScale(2, RoundingMode.DOWN).longValue();

    }).filter(Objects::nonNull).sum();

    if (salary <= 0) {
      return BigDecimal.ZERO;
    }

    return ArithmeticUtils.toBigDecimal(salary).divide(tmp, 2, RoundingMode.DOWN);
  }

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

  private Map<String, String> getDiamondMetadata(String regionId) {
    return Optional.ofNullable(sysRegionConfigService.getById(regionId))
            .map(SysRegionConfig::getDiamondMetadata)
            .orElseGet(CollectionUtils::newHashMap);
  }

  private BigDecimal getBankBalance(AppExtCommand cmd) {
    BankBalanceDTO bankBalanceDTO = getBalance(cmd);
    return bankBalanceDTO.getAccurateBalance();
  }

  private BankBalanceDTO getBalance(AppExtCommand cmd) {
    return ResponseAssert.requiredSuccess(
        userBankBalanceClient.getBalance(cmd.requiredReqUserId()));
  }

  private UserProfileDTO getByUserId(Long userId) {
    return userProfileAppConvertor.toUserProfileDTO(userProfileGateway.getByUserId(userId));
  }

  private Long getCountTeamByBdUserId(CommonCommand cmd) {
    return businessDevelopmentTeamService.countTeamByBdUserId(cmd.requiredReqUserId());
  }

  private Long getBdCountByLeadUserId(CommonCommand cmd) {
    return businessDevelopmentBaseInfoService.countBdByLeaderUserId(cmd.requiredReqUserId());
  }

  private Set<Long> getTeamIdsByBdUserId(CommonCommand cmd) {
    return businessDevelopmentTeamService.getTeamIdsByBdUserId(cmd.requiredReqUserId());
  }

}
