package com.red.circle.console.app.service.app.team.bd;

import com.google.common.collect.Lists;
import com.red.circle.console.app.convertor.team.BdAppConvertor;
import com.red.circle.console.app.dto.clienobject.team.bd.TeamBdCO;
import com.red.circle.console.app.dto.clienobject.team.bd.TeamBdWorkStatisticsCO;
import com.red.circle.console.app.dto.cmd.app.team.bd.TeamBdAddCmd;
import com.red.circle.console.app.service.app.user.UserBaseInfoService;
import com.red.circle.console.infra.database.rds.entity.admin.User;
import com.red.circle.console.infra.database.rds.service.admin.UserService;
import com.red.circle.console.inner.error.ConsoleErrorCode;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.framework.dto.PageResult;
import com.red.circle.mq.business.model.event.BadgeOperateEvent;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.other.inner.endpoint.team.bd.BdTeamInfoClient;
import com.red.circle.other.inner.endpoint.team.bd.BdTeamMemberClient;
import com.red.circle.other.inner.endpoint.user.user.UserGoldCoinRechargeClient;
import com.red.circle.other.inner.endpoint.user.user.UserInviteUserClient;
import com.red.circle.other.inner.endpoint.team.logs.TeamImportanceLogClient;
import com.red.circle.other.inner.endpoint.team.logs.TeamProcessApprovalClient;
import com.red.circle.other.inner.endpoint.team.target.UserHistoryIdentityClient;
import com.red.circle.other.inner.enums.material.BadgeKeyEnum;
import com.red.circle.other.inner.model.cmd.team.bd.BdTeamPageQryCmd;
import com.red.circle.other.inner.model.cmd.team.bd.BdTeamUpdateOpsCmd;
import com.red.circle.other.inner.model.cmd.team.bd.BdTeamWorkStatisticsQryCmd;
import com.red.circle.other.inner.model.cmd.team.bd.ChangeBdLeaderCmd;
import com.red.circle.other.inner.model.cmd.user.InviteStatisticsQryCmd;
import com.red.circle.other.inner.model.dto.agency.agency.TeamApplicationProcessApprovalDTO;
import com.red.circle.other.inner.model.dto.agency.bd.BdTeamInfoDTO;
import com.red.circle.other.inner.model.dto.agency.bd.BdTeamMemberDTO;
import com.red.circle.other.inner.model.dto.agency.logs.TeamImportanceLogDTO;
import com.red.circle.other.inner.enums.team.TeamAppProcessApprovalReason;
import com.red.circle.other.inner.enums.team.TeamApplicationType;
import com.red.circle.other.inner.model.dto.user.UserInviteUserCountDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.region.RegionConfigClient;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * DB管理 实现.
 *
 * @author pengliang on 2023/11/20
 */
@Service
@RequiredArgsConstructor
public class TeamBDInfoServiceImpl implements TeamBDInfoService {

  private final UserService userService;
  private final BdAppConvertor bdAppConvertor;
  private final BdTeamInfoClient bdTeamInfoClient;
  private final UserProfileClient userProfileClient;
  private final BdTeamMemberClient bdTeamMemberClient;
  private final RegionConfigClient regionConfigClient;
  private final UserBaseInfoService userBaseInfoService;
  private final UserInviteUserClient userInviteUserClient;
  private final TeamImportanceLogClient teamImportanceLogClient;
  private final TeamProcessApprovalClient teamProcessApprovalClient;
  private final UserHistoryIdentityClient userHistoryIdentityClient;
  private final UserMqMessageService userMqMessageService;
  private final UserGoldCoinRechargeClient userGoldCoinRechargeClient;


  @Override
  public PageResult<TeamBdWorkStatisticsCO> pageWorkStatistics(BdTeamWorkStatisticsQryCmd qryCmd) {
    Integer cursor = qryCmd.getPageQuery().getCursor();
    Integer size = qryCmd.getPageQuery().getLimit();
    //获取所有bd用户数据
    List<BdTeamInfoDTO> listResult = bdTeamInfoClient.pageWorkStatistics(qryCmd).getBody();
    if (CollectionUtils.isEmpty(listResult)) {
      return new PageResult<>();
    }
    if(Objects.nonNull(qryCmd.getAccount())){
      //筛选短账号
      List<UserProfileDTO> accountList = userBaseInfoService.getByAccount(
          qryCmd.getAccount().toString());
      List<Long> ids = accountList.stream().map(UserProfileDTO::getId).toList();
      listResult.removeIf(list->!ids.contains(list.getUserId()));
    }
    //获取所有bd用户id
    Set<Long> userIds = getUserIds(listResult);
    //获取用户基本信息
    Map<Long, UserProfileDTO> userMap = userProfileClient.mapByUserIds(userIds).getBody();
    //获取当前bd用户所邀请的人数
    Map<Long, UserInviteUserCountDTO> dbUserInviteMap = userInviteUserClient.mapInviteStatisticsByUserIds(
        new InviteStatisticsQryCmd().setQryCmd(qryCmd).setUserIds(userIds)).getBody();
    //获取bd所在的区域名称
    Map<String, String> regionNameMap = regionConfigClient.mapRegionNameByRegionIds(
            listResult.stream().map(BdTeamInfoDTO::getRegion)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet()))
        .getBody();
    //获取bd邀请总充值
    Map<Long, BigDecimal> userRechargeMap = userGoldCoinRechargeClient.mapGoldCoinRechargeByUserIds(
        new InviteStatisticsQryCmd().setQryCmd(qryCmd).setUserIds(userIds)).getBody();
    PageResult<BdTeamInfoDTO> bdTeamInfoPage = new PageResult<>();
    bdTeamInfoPage.setRecords(listResult);
    bdTeamInfoPage.setCurrent(cursor);
    bdTeamInfoPage.setSize(size);
    bdTeamInfoPage.setTotal(listResult.size());
    PageResult<TeamBdWorkStatisticsCO> convert = bdTeamInfoPage.convert(
        info -> new TeamBdWorkStatisticsCO()
            .setId(info.getId())
            .setSysOrigin(info.getSysOrigin())
            .setUserId(info.getUserId())
            .setUserProfile(userMap.get(info.getUserId()))
            .setBdLeadUserId(info.getBdLeadUserId())
            .setBdLeadUserProfile(userMap.get(info.getBdLeadUserId()))
            .setRegion(info.getRegion())
            //区域名称
            .setRegionName(regionNameMap.get(info.getRegion()))
            .setCreateTime(info.getCreateTime())
            .setUpdateTime(info.getUpdateTime())
            //bd邀请信息
            .setUserInviteUserCount(dbUserInviteMap.get(info.getUserId()))
            //总充值
            .setTotalRecharge(userRechargeMap.get(info.getUserId()))
            //bd客单价
            .setCustomerUnitPrice(
                calculateCustomerUnitPrice(
                    Objects.isNull(dbUserInviteMap.get(info.getUserId())) ? 0L
                        : dbUserInviteMap.get(info.getUserId()).getTotalInvitation(),
                    userRechargeMap.get(info.getUserId()))));
    Map<String, List<TeamBdWorkStatisticsCO>> regionMap = convert.getRecords().stream()
        .collect(Collectors.groupingBy(TeamBdWorkStatisticsCO::getRegion));
    convert.getRecords().forEach(workStatistics -> {
      List<TeamBdWorkStatisticsCO> workStatisticsList = regionMap.get(workStatistics.getRegion());
      //区域总人数
      Long regionTotalInvitation =
          CollectionUtils.isEmpty(workStatisticsList) ? 0L
              : workStatisticsList.stream()
                  .mapToLong(inviteInfo -> Objects.isNull(inviteInfo.getUserInviteUserCount()) ? 0L
                      : inviteInfo.getUserInviteUserCount().getTotalInvitation())
                  .sum();
      //区域总充值
      BigDecimal regionTotalRecharge = CollectionUtils.isEmpty(workStatisticsList) ? BigDecimal.ZERO
          : workStatisticsList.stream()
              .map(TeamBdWorkStatisticsCO::getTotalRecharge)
              .filter(Objects::nonNull)
              .reduce(BigDecimal.ZERO, BigDecimal::add);
      //计算区域平均客单价
      workStatistics.setRegionalAvgCustomerUnitPrice(
          calculateRegionalAvgCustomerUnitPrice(regionTotalRecharge, regionTotalInvitation));
      //计算有效人数
      workStatistics.setEffectivePerson(calculateEffectivePerson(
          Objects.isNull(workStatistics.getUserInviteUserCount()) ? 0
              : workStatistics.getUserInviteUserCount().getTotalInvitation(),
          workStatistics.getCustomerUnitPrice(), workStatistics.getRegionalAvgCustomerUnitPrice()));
    });
    //赋值区域排名
    convert.getRecords().forEach(workStatistics -> {
      List<TeamBdWorkStatisticsCO> workStatisticsList = regionMap.get(workStatistics.getRegion());
      workStatisticsList = workStatisticsList.stream().sorted(
          Comparator.comparing(TeamBdWorkStatisticsCO::getEffectivePerson).reversed()).toList();
      for (int i = 0; i < workStatisticsList.size(); i++) {
        TeamBdWorkStatisticsCO statistics = workStatisticsList.get(i);
        statistics.setRegionRanking(i + 1);
      }
    });
    convert.setRecords(convert.getRecords().stream()
        .sorted(Comparator.comparing(TeamBdWorkStatisticsCO::getEffectivePerson).reversed().thenComparing(TeamBdWorkStatisticsCO::getRegionRanking))
        .collect(Collectors.toList()));
    for (int i = 0; i < convert.getRecords().size(); i++) {
      TeamBdWorkStatisticsCO statisticsCo = convert.getRecords().stream()
          .toList().get(i);
      statisticsCo.setSortFiled(i+1);
    }
    if (null != qryCmd.getSortType() && qryCmd.getSortType() == 1) {
      convert.setRecords(convert.getRecords().stream()
          .sorted(Comparator.comparing(TeamBdWorkStatisticsCO::getSortFiled).reversed())
          .toList());
    } else {
      convert.setRecords(convert.getRecords().stream()
          .sorted(Comparator.comparing(TeamBdWorkStatisticsCO::getSortFiled))
          .toList());
    }
    //代码手动分页
    convert.setRecords(convert.getRecords().stream().skip((long) (cursor - 1) * size).limit(size)
        .collect(Collectors.toList()));
    return convert;
  }

  /**
   * 计算有效人数
   *
   * @param totalInvitation              bd邀请的总人数
   * @param customerUnitPrice            bd客单价
   * @param regionalAvgCustomerUnitPrice 区域平均客单价
   * @return
   */
  private BigDecimal calculateEffectivePerson(Long totalInvitation, BigDecimal customerUnitPrice,
      BigDecimal regionalAvgCustomerUnitPrice) {
    if (0 == totalInvitation || BigDecimal.ZERO.equals(customerUnitPrice) || BigDecimal.ZERO.equals(
        regionalAvgCustomerUnitPrice)) {
      return BigDecimal.ZERO;
    }
    return customerUnitPrice.divide(regionalAvgCustomerUnitPrice, 2, RoundingMode.HALF_UP)
        .multiply(BigDecimal.valueOf(totalInvitation));
  }

  /**
   * 计算bd客单价
   *
   * @param totalInvitation 邀请总人数
   * @param totalRecharge   总充值
   * @return
   */
  private BigDecimal calculateCustomerUnitPrice(Long totalInvitation, BigDecimal totalRecharge) {
    if (0 == totalInvitation || BigDecimal.ZERO.equals(totalRecharge) || null == totalRecharge) {
      return BigDecimal.ZERO;
    }
    return totalRecharge.divide(BigDecimal.valueOf(totalInvitation), 2, RoundingMode.HALF_UP);
  }

  /**
   * 计算区域平均客单价
   *
   * @param regionTotalRecharge   区域总充值
   * @param regionTotalInvitation 区域总人数
   * @return
   */
  private BigDecimal calculateRegionalAvgCustomerUnitPrice(BigDecimal regionTotalRecharge,
      Long regionTotalInvitation) {
    if (0 == regionTotalInvitation || BigDecimal.ZERO.equals(regionTotalRecharge)) {
      return BigDecimal.ZERO;
    }
    return regionTotalRecharge.divide(BigDecimal.valueOf(regionTotalInvitation), 2,
        RoundingMode.HALF_UP);
  }

  @Override
  public PageResult<TeamBdCO> pageOps(BdTeamPageQryCmd qryCmd) {

    PageResult<BdTeamInfoDTO> pageResult = bdTeamInfoClient.pageOps(qryCmd).getBody();

    if (pageResult.checkRecordsEmpty()) {
      return pageResult.emptyRecords();
    }

    Set<Long> userIds = getUserIds(pageResult.getRecords());

    Map<Long, User> sysUserMap = userService.mapBackUser(
        Stream.of(pageResult.getRecords().stream()
                    .filter(baseInfo -> Objects.nonNull(baseInfo.getCreateUser())
                        && baseInfo.getCreateUser() > 0)
                    .map(BdTeamInfoDTO::getCreateUser)
                    .collect(Collectors.toList()),
                pageResult.getRecords().stream()
                    .filter(baseInfo -> Objects.nonNull(baseInfo.getUpdateUser())
                        && baseInfo.getUpdateUser() > 0)
                    .map(BdTeamInfoDTO::getUpdateUser)
                    .collect(Collectors.toList()))
            .flatMap(Collection::stream)
            .collect(Collectors.toSet()));

    Map<Long, UserProfileDTO> userMap = userProfileClient.mapByUserIds(userIds).getBody();

    Map<String, String> regionNameMap = regionConfigClient.mapRegionNameByRegionIds(
            pageResult.getRecords().stream().map(BdTeamInfoDTO::getRegion)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet()))
        .getBody();

    return pageResult.convert(info -> new TeamBdCO()
        .setId(info.getId())
        .setSysOrigin(info.getSysOrigin())
        .setUserId(info.getUserId())
        .setUserProfile(userMap.get(info.getUserId()))
        .setAgentCount(info.getMemberQuantity())
        .setBdLeadUserId(info.getBdLeadUserId())
        .setBdLeadUserProfile(userMap.get(info.getBdLeadUserId()))
        .setCreateUserName(
            Optional.ofNullable(sysUserMap.get(info.getCreateUser())).map(User::getNickname)
                .orElse(""))
        .setUpdateUserName(
            Optional.ofNullable(sysUserMap.get(info.getUpdateUser())).map(User::getNickname)
                .orElse(""))
        .setContact(info.getContact())
        .setRegion(info.getRegion())
        .setRegionName(regionNameMap.get(info.getRegion()))
        .setCreateTime(info.getCreateTime())
        .setUpdateTime(info.getUpdateTime()));
  }

  private Set<Long> getUserIds(Collection<BdTeamInfoDTO> bdList) {
    return Stream.of(
            bdList.stream()
                .map(BdTeamInfoDTO::getUserId)
                .collect(Collectors.toSet()),
            bdList.stream()
                .map(BdTeamInfoDTO::getBdLeadUserId)
                .collect(Collectors.toSet()))
        .flatMap(Collection::stream)
        .filter(Objects::nonNull)
        .collect(Collectors.toSet());
  }

  @Override
  public BdTeamInfoDTO getByUserId(Long userId) {
    return bdTeamInfoClient.getByUserId(userId).getBody();
  }

  @Override
  public void updateById(BdTeamUpdateOpsCmd cmd) {
    bdTeamInfoClient.updateOpsById(cmd);
  }

  @Override
  public void addBd(TeamBdAddCmd cmd) {

    UserProfileDTO userProfile = userProfileClient.getByUserId(cmd.getUserId()).getBody();
    ResponseAssert.notNull(ConsoleErrorCode.USER_NOT_FOUND, userProfile);

    ResponseAssert.isFalse(ConsoleErrorCode.USER_ALREADY_EXISTS,
        bdTeamInfoClient.check(cmd.getUserId()).getBody());

    cmd.setId(IdWorkerUtils.getId());

    ResponseAssert.requiredSuccess(bdTeamInfoClient.add(bdAppConvertor.toBdTeamInfoCmd(cmd)));

    teamProcessApprovalClient.addProcessLog(new TeamApplicationProcessApprovalDTO()
        .setSysOrigin(cmd.getSysOrigin())
        .setType(TeamApplicationType.BD)
        .setReason(TeamAppProcessApprovalReason.SYSTEM_ADD_BD)
        .setAssociateId(cmd.getId())
        .setBeProcessUserId(cmd.getUserId())
        .setCreateTime(TimestampUtils.now())
        .setCreateUser(cmd.getCreateUser())
        .setCreateUserOrigin(1)
    );

    userHistoryIdentityClient.saveBd(userProfile.getId());

    userMqMessageService.sendBadgeOperateEvent(userProfile.getId(), BadgeKeyEnum.BD.name(), BadgeOperateEvent.OperationType.WEAR.name());
  }

  @Override
  public void deleteById(Long id, Long createUserId) {

    BdTeamInfoDTO db = bdTeamInfoClient.getById(id).getBody();
    ResponseAssert.notNull(CommonErrorCode.DELETE_FAILURE, db);

    // 删除BD
    ResponseAssert.requiredSuccess(bdTeamInfoClient.deleteById(id));

    teamProcessApprovalClient.addProcessLog(new TeamApplicationProcessApprovalDTO()
        .setSysOrigin(db.getSysOrigin())
        .setType(TeamApplicationType.BD)
        .setReason(TeamAppProcessApprovalReason.SYSTEM_DELETE_BD)
        .setAssociateId(db.getId())
        .setBeProcessUserId(db.getUserId())
        .setCreateTime(TimestampUtils.now())
        .setCreateUser(createUserId)
        .setCreateUserOrigin(1)
    );

    teamImportanceLogClient.add(new TeamImportanceLogDTO()
        .setRemarks("[后台]在BD列表中单个删除了BD")
        .setSysOrigin(db.getSysOrigin())
        .setBeProcessUserId(db.getUserId())
        .setCreateUser(createUserId)
        .setCreateUserOrigin(1)
    );

    userMqMessageService.sendBadgeOperateEvent(db.getUserId(), BadgeKeyEnum.BD.name(), BadgeOperateEvent.OperationType.REMOVE.name());

    // 获得需要被移除的成员
    List<BdTeamMemberDTO> teamList = bdTeamMemberClient
        .listByUserId(db.getUserId()).getBody();

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

    //解散bd团队
    ResponseAssert.requiredSuccess(bdTeamMemberClient.deleteAllByUserId(db.getUserId()));

    //添加日志
    Lists.partition(teamList, 100)
        .forEach(members -> teamProcessApprovalClient.addBatchProcessLog(
                members.stream().map(member -> new TeamApplicationProcessApprovalDTO()
                    .setSysOrigin(member.getSysOrigin())
                    .setType(TeamApplicationType.BD)
                    .setReason(TeamAppProcessApprovalReason.SYSTEM_DELETE_BD_AND_DELETE_MEMBER)
                    .setAssociateId(db.getUserId())
                    .setBeProcessUserId(member.getUserId())
                    .setCreateTime(TimestampUtils.now())
                    .setCreateUser(createUserId)
                    .setCreateUserOrigin(1)
                ).collect(Collectors.toList())
            )
        );

    Lists.partition(teamList, 100)
        .forEach(members -> teamImportanceLogClient.addBatch(
                members.stream().map(member -> new TeamImportanceLogDTO()
                        .setSysOrigin(member.getSysOrigin())
                        .setRemarks("[后台]在BD列表中单个删除了BD => 解除基于该BD的团队BD关联关系")
                        .setAssociateId(db.getUserId())
                        .setBeProcessUserId(member.getUserId())
                        .setCreateTime(TimestampUtils.now())
                        .setCreateUser(createUserId)
                        .setCreateUserOrigin(1)
                    )
                    .collect(Collectors.toList())
            )
        );

  }

  @Override
  public void changeBdLeader(ChangeBdLeaderCmd cmd) {
    bdTeamInfoClient.changeBdLeader(cmd);
  }

}
