package com.red.circle.other.app.inner.service.user.user.impl;

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.other.domain.model.user.UserProfile;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.cache.service.user.RedPacketInviteUserCacheService;
import com.red.circle.other.infra.database.rds.entity.user.user.InviteConfig;
import com.red.circle.other.infra.database.rds.entity.user.user.InviteRedPacketDrawLog;
import com.red.circle.other.infra.database.rds.entity.user.user.InviteRedPacketHelp;
import com.red.circle.other.infra.database.rds.entity.user.user.InviteRedPacketRecord;
import com.red.circle.other.infra.database.rds.entity.user.user.InviteTargetTransitBalance;
import com.red.circle.other.infra.database.rds.entity.user.user.InviteWithdrawTransitBalance;
import com.red.circle.other.infra.database.rds.service.user.user.InviteCarouselAwardsService;
import com.red.circle.other.infra.database.rds.service.user.user.InviteConfigService;
import com.red.circle.other.infra.database.rds.service.user.user.InviteRedPacketDrawLogService;
import com.red.circle.other.infra.database.rds.service.user.user.InviteRedPacketHelpService;
import com.red.circle.other.infra.database.rds.service.user.user.InviteRedPacketRecordService;
import com.red.circle.other.infra.database.rds.service.user.user.InviteTargetTransitBalanceService;
import com.red.circle.other.infra.database.rds.service.user.user.InviteWithdrawTransitBalanceService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.infra.enums.user.user.InviteHelpUserTypeEnum;
import com.red.circle.other.infra.enums.user.user.InviteRedPacketDrawTypeEnum;
import com.red.circle.other.infra.enums.user.user.InviteRedPacketSourceEnum;
import com.red.circle.other.infra.enums.user.user.InviteRedPacketStatusEnum;
import com.red.circle.other.app.inner.convertor.user.RedPacketInviteNewUserInnerConvertor;
import com.red.circle.other.app.inner.convertor.user.UserProfileInnerConvertor;
import com.red.circle.other.inner.model.cmd.user.invite.InviteBalancePageQryCmd;
import com.red.circle.other.inner.model.cmd.user.invite.InviteCarouselAwardsCmd;
import com.red.circle.other.inner.model.cmd.user.invite.InviteConfigCmd;
import com.red.circle.other.inner.model.cmd.user.invite.InviteRedPacketDrawLogPageQryCmd;
import com.red.circle.other.inner.model.cmd.user.invite.InviteRedPacketHelpLogPageQryCmd;
import com.red.circle.other.inner.model.cmd.user.invite.InviteRedPacketPageQryCmd;
import com.red.circle.other.inner.model.cmd.user.invite.InviteTargetTransitBalanceCmd;
import com.red.circle.other.inner.model.dto.user.invite.InviteBalanceDTO;
import com.red.circle.other.inner.model.dto.user.invite.InviteCarouselAwardsBackDTO;
import com.red.circle.other.inner.model.dto.user.invite.InviteConfigDTO;
import com.red.circle.other.inner.model.dto.user.invite.InviteRedPacketDTO;
import com.red.circle.other.inner.model.dto.user.invite.InviteRedPacketDrawLogDTO;
import com.red.circle.other.inner.model.dto.user.invite.InviteRedPacketHelpLogDTO;
import com.red.circle.other.app.inner.service.user.user.UserRedPacketInviteNewClientService;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * 用户红包邀新服务.
 *
 * @author pengshigang on 2024/01/05
 */
@Service
@RequiredArgsConstructor
public class UserRedPacketInviteNewClientServiceImpl implements
    UserRedPacketInviteNewClientService {

  private final UserProfileGateway userProfileGateway;
  private final InviteConfigService inviteConfigService;
  private final UserProfileInnerConvertor userProfileInnerConvertor;
  private final InviteRedPacketHelpService inviteRedPacketHelpService;
  private final InviteCarouselAwardsService inviteCarouselAwardsService;
  private final InviteRedPacketRecordService inviteRedPacketRecordService;
  private final InviteRedPacketDrawLogService inviteRedPacketDrawLogService;
  private final RedPacketInviteUserCacheService redPacketInviteUserCacheService;
  private final InviteTargetTransitBalanceService inviteTargetTransitBalanceService;
  private final InviteWithdrawTransitBalanceService inviteWithdrawTransitBalanceService;
  private final RedPacketInviteNewUserInnerConvertor redPacketInviteNewUserInnerConvertor;

  @Override
  public PageResult<InviteBalanceDTO> pageTargetTransitBalance(InviteBalancePageQryCmd cmd) {

    PageResult<InviteTargetTransitBalance> balanceIPage = inviteTargetTransitBalanceService.
        pageTargetTransitBalance(cmd);

    if (CollectionUtils.isEmpty(balanceIPage.getRecords())) {
      return balanceIPage.convert(
          redPacketInviteNewUserInnerConvertor::toInviteTargetTransitBalanceDTO);
    }

    Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(
        balanceIPage.getRecords().stream().map(InviteTargetTransitBalance::getId).collect(
            Collectors.toSet()));
    if (CollectionUtils.isEmpty(userProfileMap)) {
      return balanceIPage.convert(
          redPacketInviteNewUserInnerConvertor::toInviteTargetTransitBalanceDTO);
    }

    return balanceIPage.convert(transitBalance -> {
      InviteBalanceDTO dto = redPacketInviteNewUserInnerConvertor.toInviteTargetTransitBalanceDTO(
          transitBalance);
      dto.setBalance(dto.getIncomeAmount().subtract(dto.getExpenditureBalance()));
      dto.setUserProfile(
          userProfileInnerConvertor.toUserProfileDTO(userProfileMap.get(dto.getId())));
      return dto;
    });
  }

  @Override
  public void reduceTarget(InviteTargetTransitBalanceCmd cmd) {

    inviteTargetTransitBalanceService.reduceTarget(cmd);
  }

  @Override
  public PageResult<InviteBalanceDTO> pageWithdrawTransitBalance(InviteBalancePageQryCmd cmd) {

    PageResult<InviteWithdrawTransitBalance> balanceIPage = inviteWithdrawTransitBalanceService.
        pageWithdrawTransitBalance(cmd);

    if (CollectionUtils.isEmpty(balanceIPage.getRecords())) {
      return balanceIPage.convert(
          redPacketInviteNewUserInnerConvertor::toInviteWithdrawTransitBalanceDTO);
    }

    Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(
        balanceIPage.getRecords().stream().map(InviteWithdrawTransitBalance::getId).collect(
            Collectors.toSet()));
    if (CollectionUtils.isEmpty(userProfileMap)) {
      return balanceIPage.convert(
          redPacketInviteNewUserInnerConvertor::toInviteWithdrawTransitBalanceDTO);
    }

    return balanceIPage.convert(transitBalance -> {
      InviteBalanceDTO dto = redPacketInviteNewUserInnerConvertor.toInviteWithdrawTransitBalanceDTO(
          transitBalance);
      dto.setBalance(dto.getIncomeAmount().subtract(dto.getExpenditureBalance()));
      dto.setUserProfile(
          userProfileInnerConvertor.toUserProfileDTO(userProfileMap.get(dto.getId())));
      return dto;
    });
  }

  @Override
  public PageResult<InviteRedPacketDrawLogDTO> pageRedPacketDrawLog(
      InviteRedPacketDrawLogPageQryCmd cmd) {

    PageResult<InviteRedPacketDrawLog> drawLogIPage = inviteRedPacketDrawLogService.pageLog(cmd);

    if (CollectionUtils.isEmpty(drawLogIPage.getRecords())) {
      return drawLogIPage.convert(
          redPacketInviteNewUserInnerConvertor::toInviteRedPacketDrawLogDTO);
    }

    Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(
        drawLogIPage.getRecords().stream().map(InviteRedPacketDrawLog::getUserId).collect(
            Collectors.toSet()));
    if (CollectionUtils.isEmpty(userProfileMap)) {
      return drawLogIPage.convert(
          redPacketInviteNewUserInnerConvertor::toInviteRedPacketDrawLogDTO);
    }

    return drawLogIPage.convert(drawLog -> {
      InviteRedPacketDrawLogDTO dto = redPacketInviteNewUserInnerConvertor.toInviteRedPacketDrawLogDTO(
          drawLog);
      dto.setAwardTypeName(Optional.of(InviteRedPacketDrawTypeEnum.valueOf(dto.getAwardType()))
          .map(InviteRedPacketDrawTypeEnum::getDesc).orElse(""));
      dto.setUserProfile(
          userProfileInnerConvertor.toUserProfileDTO(userProfileMap.get(dto.getUserId())));
      return dto;
    });
  }

  @Override
  public PageResult<InviteRedPacketHelpLogDTO> pageRedPacketHelpLog(
      InviteRedPacketHelpLogPageQryCmd cmd) {

    PageResult<InviteRedPacketHelp> drawLogIPage = inviteRedPacketHelpService.pageHelp(cmd);

    if (CollectionUtils.isEmpty(drawLogIPage.getRecords())) {
      return drawLogIPage.convert(redPacketInviteNewUserInnerConvertor::toInviteRedPacketHelpDTO);
    }

    Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(
        Stream.of(drawLogIPage.getRecords().stream().map(InviteRedPacketHelp::getUserId)
                    .filter(Objects::nonNull).collect(Collectors.toList()),
                drawLogIPage.getRecords().stream().map(InviteRedPacketHelp::getInviteUserId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList())).flatMap(Collection::stream)
            .collect(Collectors.toSet()));

    if (CollectionUtils.isEmpty(userProfileMap)) {
      return drawLogIPage.convert(redPacketInviteNewUserInnerConvertor::toInviteRedPacketHelpDTO);
    }

    return drawLogIPage.convert(drawLog -> {

      InviteRedPacketHelpLogDTO dto = redPacketInviteNewUserInnerConvertor.toInviteRedPacketHelpDTO(
          drawLog);

      dto.setUserTypeName(Optional.of(InviteHelpUserTypeEnum.valueOf(dto.getUserType()))
          .map(InviteHelpUserTypeEnum::getDesc).orElse(""));
      dto.setUserProfile(
          userProfileInnerConvertor.toUserProfileDTO(userProfileMap.get(dto.getUserId())));
      if (Objects.nonNull(dto.getInviteUserId())) {
        dto.setInviteUserProfile(
            userProfileInnerConvertor.toUserProfileDTO(userProfileMap.get(dto.getInviteUserId())));
      }
      return dto;
    });
  }

  @Override
  public PageResult<InviteRedPacketDTO> pageRedPacket(InviteRedPacketPageQryCmd cmd) {

    PageResult<InviteRedPacketRecord> packetRecordIPage = inviteRedPacketRecordService.pageRecord(
        cmd);
    if (CollectionUtils.isEmpty(packetRecordIPage.getRecords())) {
      return packetRecordIPage.convert(redPacketInviteNewUserInnerConvertor::toInviteRedPacketDTO);
    }

    Map<Long, UserProfile> userProfileMap = userProfileGateway.mapByUserIds(
        Stream.of(packetRecordIPage.getRecords().stream().map(InviteRedPacketRecord::getUserId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList()),
                packetRecordIPage.getRecords().stream().map(InviteRedPacketRecord::getInviteUserId)
                    .filter(Objects::nonNull).collect(Collectors.toList())).flatMap(Collection::stream)
            .collect(Collectors.toSet()));

    if (CollectionUtils.isEmpty(userProfileMap)) {
      return packetRecordIPage.convert(redPacketInviteNewUserInnerConvertor::toInviteRedPacketDTO);
    }

    return packetRecordIPage.convert(redPacketRecord -> {
      InviteRedPacketDTO dto = redPacketInviteNewUserInnerConvertor.toInviteRedPacketDTO(
          redPacketRecord);
      dto.setStatusName(Optional.of(InviteRedPacketStatusEnum.valueOf(dto.getStatus()))
          .map(InviteRedPacketStatusEnum::getDesc).orElse(""));
      dto.setRedPacketSourceName(
          Optional.of(InviteRedPacketSourceEnum.valueOf(dto.getRedPacketSource()))
              .map(InviteRedPacketSourceEnum::getDesc).orElse(""));
      dto.setUserProfile(
          userProfileInnerConvertor.toUserProfileDTO(userProfileMap.get(dto.getUserId())));
      if (Objects.nonNull(dto.getInviteUserId())) {
        dto.setInviteUserProfile(
            userProfileInnerConvertor.toUserProfileDTO(userProfileMap.get(dto.getInviteUserId())));
      }
      return dto;
    });
  }

  @Override
  public List<InviteCarouselAwardsBackDTO> listCarouselAwards(String sysOrigin) {

    return redPacketInviteNewUserInnerConvertor.toInviteCarouselAwardsBackDTOList(
        inviteCarouselAwardsService.listBySysOrigin(sysOrigin));
  }

  @Override
  public void saveCarouselAwards(InviteCarouselAwardsCmd cmd) {

    ResponseAssert.notNull(CommonErrorCode.TYPE_IS_NOT_IN_SCOPE,
        InviteRedPacketDrawTypeEnum.valueOf(cmd.getType()));

    inviteCarouselAwardsService.saveOrUpd(cmd);
  }

  @Override
  public void deleteCarouselAwards(Long id) {

    inviteCarouselAwardsService.deleteById(id);
  }

  @Override
  public InviteConfigDTO getConfigBySysOrigin(String sysOrigin) {

    return redPacketInviteNewUserInnerConvertor.toInviteConfigDTO(
        inviteConfigService.getBySysOrigin(sysOrigin));
  }

  @Override
  public void saveConfig(InviteConfigCmd cmd) {

    String regionCodeArray = "";
    if (CollectionUtils.isNotEmpty(cmd.getRegionList())) {
      regionCodeArray = StringUtils.join(cmd.getRegionList(), ",");
    }

    InviteConfig userInviteConfig = inviteConfigService.getBySysOrigin(cmd.getSysOrigin());
    if (Objects.isNull(userInviteConfig)) {
      userInviteConfig = redPacketInviteNewUserInnerConvertor.toInviteConfigCmd(cmd);
      userInviteConfig.setRegionCodeArray(regionCodeArray);
      inviteConfigService.save(userInviteConfig);
      redPacketInviteUserCacheService.removeConfigCache(cmd.getSysOrigin());
      return;
    }
    InviteConfig tmp = redPacketInviteNewUserInnerConvertor.toInviteConfigCmd(cmd);
    tmp.setRegionCodeArray(regionCodeArray);
    tmp.setUpdateTime(TimestampUtils.now());
    tmp.setId(userInviteConfig.getId());
    tmp.setCreateTime(userInviteConfig.getCreateTime());
    inviteConfigService.updateSelectiveById(tmp);
    redPacketInviteUserCacheService.removeConfigCache(cmd.getSysOrigin());
  }

}
