package com.red.circle.other.infra.common.activity;

import com.google.common.collect.Sets;
import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.common.business.enums.PropsActivityRewardEnum;
import com.red.circle.common.business.enums.SendPropsOrigin;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.order.inner.asserts.ProductErrorCode;
import com.red.circle.other.domain.gateway.props.ActivitySourceGroupGateway;
import com.red.circle.other.domain.gateway.props.PropsStoreGateway;
import com.red.circle.other.infra.common.activity.send.ReceiveActivityReward;
import com.red.circle.other.infra.common.activity.send.ReceiveActivityReward.ActivityCycle;
import com.red.circle.other.infra.common.activity.send.SendRewardAbstract;
import com.red.circle.other.infra.common.activity.send.SendRewardGroup;
import com.red.circle.other.infra.common.activity.send.SendRewardSingle;
import com.red.circle.other.infra.common.props.PropsSendCommon;
import com.red.circle.other.infra.convertor.material.PropsMaterialInfraConvertor;
import com.red.circle.other.infra.database.mongo.entity.live.RoomSetting;
import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService;
import com.red.circle.other.infra.database.rds.entity.activity.ActivityReceiveRewardLog;
import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardConfig;
import com.red.circle.other.infra.database.rds.entity.badge.BadgeBackpack;
import com.red.circle.other.infra.database.rds.entity.props.PropsSendLog;
import com.red.circle.other.infra.database.rds.entity.props.PropsSourceRecord;
import com.red.circle.other.infra.database.rds.service.activity.ActivityReceiveRewardLogService;
import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardConfigService;
import com.red.circle.other.infra.database.rds.service.badge.BadgeBackpackService;
import com.red.circle.other.infra.database.rds.service.badge.RoomBadgeBackpackService;
import com.red.circle.other.infra.database.rds.service.emoji.EmojiBagService;
import com.red.circle.other.infra.database.rds.service.gift.GiftBackpackService;
import com.red.circle.other.infra.database.rds.service.props.FragmentsBackpackService;
import com.red.circle.other.infra.database.rds.service.props.PropsSendLogService;
import com.red.circle.other.infra.database.rds.service.props.PropsSourceRecordService;
import com.red.circle.other.infra.database.rds.service.props.PropsVipActualEquityService;
import com.red.circle.other.inner.enums.material.NobleVipEnum;
import com.red.circle.other.inner.enums.material.PropsCommodityType;
import com.red.circle.other.inner.model.cmd.material.PrizeDescribe;
import com.red.circle.other.inner.model.cmd.material.PrizeDescribeRewardCmd;
import com.red.circle.other.inner.model.dto.activity.props.ActivityPropsRule;
import com.red.circle.other.inner.model.dto.material.props.NobleVipAbility;
import com.red.circle.other.inner.model.dto.material.props.ProductProps;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.parse.DataTypeUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 活动发送服务.
 *
 * @author pengliang on 2023/6/4
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PropsActivitySendCommon {

  private final PropsSendCommon propsSendCommon;
  private final EmojiBagService emojiBagService;
  private final WalletGoldClient walletGoldGateway;
  private final PropsStoreGateway propsStoreRepository;
  private final PropsSendLogService propsSendLogService;
  private final GiftBackpackService giftBackpackService;
  private final BadgeBackpackService badgeBackpackService;
  private final RoomBadgeBackpackService roomBadgeBackpackService;
  private final FragmentsBackpackService fragmentsBackpackService;
  private final PropsSourceRecordService propsSourceRecordService;
  private final RoomProfileManagerService roomProfileManagerService;
  private final ActivitySourceGroupGateway activitySourceGroupGateway;
  private final PropsMaterialInfraConvertor propsMaterialInfraConvertor;
  private final PropsVipActualEquityService propsVipActualEquityService;
  private final ActivityReceiveRewardLogService activityReceiveRewardLogService;
  private final PropsActivityRewardConfigService propsActivityRewardConfigService;

  /**
   * 领取活动奖励.
   */
  public void receiveActivityReward(ReceiveActivityReward reward) {
    ActivityPropsRule ruleConfig = activitySourceGroupGateway.getByRuleId(reward.getRuleId());
    // 没有找到映射信息
    ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, ruleConfig);

    if (Objects.isNull(reward.getActivityCycle())) {
      reward.setActivityCycle(ActivityCycle.DAILY);
    }

    reward.getReceiveBefore().accept(ruleConfig);
    // 不可重复领取
    ResponseAssert.isFalse(CommonErrorCode.NON_REPEATABLE, checkNotReceive(reward, ruleConfig));

    sendActivityGroup(SendRewardGroup.builder()
        .trackId(ruleConfig.getId())
        .sysOrigin(reward.getSysOrigin())
        .acceptUserId(reward.getAcceptUserId())
        .resourceGroupId(ruleConfig.getResourceGroupId())
        .origin(reward.getSendPropsOrigin())
        .build());

    activityReceiveRewardLogService.save(new ActivityReceiveRewardLog()
        .setUserId(reward.getAcceptUserId())
        .setReceiveId(ruleConfig.getId())
        .setSysOrigin(reward.getSysOrigin().name())
        .setType(reward.getPropsActivityType().name())
    );
    reward.getReceiveAfter().accept(ruleConfig);
  }

  private boolean checkNotReceive(ReceiveActivityReward reward, ActivityPropsRule ruleConfig) {
    if (Objects.equals(reward.getActivityCycle(), ActivityCycle.DAILY)) {
      return activityReceiveRewardLogService
          .existsThisNow(reward.getAcceptUserId(), ruleConfig.getId(),
              reward.getPropsActivityType());
    }

    if (Objects.equals(reward.getActivityCycle(), ActivityCycle.WEEKLY)) {
      return activityReceiveRewardLogService
          .existsThisWeek(reward.getAcceptUserId(), ruleConfig.getId(),
              reward.getPropsActivityType());
    }
    if (Objects.equals(reward.getActivityCycle(), ActivityCycle.MONTHLY)) {
      return activityReceiveRewardLogService
          .existsThisMonth(reward.getAcceptUserId(), ruleConfig.getId(),
              reward.getPropsActivityType());
    }
    return true;
  }

  /**
   * 发送单个-活动道具.
   */
  public void sendActivitySingle(SendRewardSingle param) {
    if (Objects.isNull(param.getRewardConfig())) {
      throw new IllegalArgumentException("Send rewardConfig is null.");
    }
    PropsActivityRewardConfig rewardConfig = param.getRewardConfig();
    sendGroup(param, Collections.singletonList(toPrizeDescribe(rewardConfig)));
  }

  /**
   * 发送奖励.
   */
  public void sendActivityGroup(SendRewardGroup param) {
    List<PropsActivityRewardConfig> configs = propsActivityRewardConfigService.listByGroupId(
        param.getResourceGroupId());

    if (CollectionUtils.isEmpty(configs)) {
      return;
    }
    propsSendCommon.send(new PrizeDescribeRewardCmd()
        .setTrackId(param.getTrackId())
        .setAcceptUserId(param.getAcceptUserId())
        .setOrigin(param.getOrigin())
        .setSysOrigin(param.getSysOrigin())
        .setPrizes(toListPrizeDescribe(configs))
    );
  }


  /**
   * 发送奖励, 特殊个例.
   */
  public void sendActivityGroupBySpecial(SendRewardGroup param, Boolean sendGold) {

    Map<String, List<PropsActivityRewardConfig>> propsRewardGroup = mapPropsGroup(
        param.getResourceGroupId());

    if (CollectionUtils.isEmpty(propsRewardGroup)) {
      return;
    }
    propsRewardGroup.forEach((key, val) -> {

      List<PrizeDescribe> prizeArrays = toListPrizeDescribe(val);

      if (!PropsActivityRewardEnum.CUSTOMIZE.eq(key)) {
        saveSendLog(param, prizeArrays);
      }

      if (PropsActivityRewardEnum.EMOJI.eq(key)) {
        sendEmoji(param, prizeArrays);
        return;
      }

      if (PropsActivityRewardEnum.PROPS.eq(key)) {
        sendProps(param, prizeArrays);
        return;
      }

      if (PropsActivityRewardEnum.FRAGMENTS.eq(key)) {

        prizeArrays.forEach(propsActivityRewardConfig ->
            fragmentsBackpackService.incrFragments(param.getAcceptUserId(),
                DataTypeUtils.toLong(propsActivityRewardConfig.getContent()),
                propsActivityRewardConfig.getQuantity(),
                propsActivityRewardConfig.getDetailType()));

      }

      if (PropsActivityRewardEnum.GOLD.eq(key) && Boolean.TRUE.equals(sendGold)) {
        sendGold(param, prizeArrays);
        return;
      }

//      if (PropsActivityRewardEnum.GAME_COUPON.eq(key)) {
//        sendGameCoupon(param, prizeArrays);
//        return;
//      }
//
//      if (PropsActivityRewardEnum.DIAMOND.eq(key)) {
//        sendDiamond(param, prizeArrays);
//        return;
//      }

      if (PropsActivityRewardEnum.GIFT.eq(key)) {
        prizeArrays.forEach(propsActivityRewardConfig ->
            giftBackpackService.incrGift(param.getAcceptUserId(),
                DataTypeUtils.toLong(propsActivityRewardConfig.getContent()),
                propsActivityRewardConfig.getQuantity()));
        return;
      }

      if (PropsActivityRewardEnum.BADGE.eq(key)) {
        sendUserBadge(param, prizeArrays);
        sendRoomBadge(param, prizeArrays);
      }
    });
  }


  /**
   * 发送一组 指定类型奖品.
   */
  public void sendGroup(SendRewardAbstract param, List<PrizeDescribe> prizeArrays) {
    propsSendCommon.send(new PrizeDescribeRewardCmd()
        .setTrackId(param.getTrackId())
        .setAcceptUserId(param.getAcceptUserId())
        .setOrigin(param.getOrigin())
        .setSysOrigin(param.getSysOrigin())
        .setPrizes(prizeArrays)
    );
  }

  /**
   * 发送单个奖品.
   */
  public void sendSingle(SendRewardAbstract param, PrizeDescribe prizeDescribe) {
    propsSendCommon.send(new PrizeDescribeRewardCmd()
        .setTrackId(param.getTrackId())
        .setAcceptUserId(param.getAcceptUserId())
        .setOrigin(param.getOrigin())
        .setSysOrigin(param.getSysOrigin())
        .setPrizes(List.of(prizeDescribe))
    );
  }

  private List<PrizeDescribe> toListPrizeDescribe(List<PropsActivityRewardConfig> configs) {
    return configs.stream().map(this::toPrizeDescribe).collect(Collectors.toList());
  }

  private PrizeDescribe toPrizeDescribe(PropsActivityRewardConfig config) {
    return new PrizeDescribe()
        .setTrackId(config.getGroupId())
        .setType(config.getType())
        .setDetailType(config.getDetailType())
        .setContent(config.getContent())
        .setQuantity(config.getQuantity());
  }

  private void sendEmoji(SendRewardAbstract param, List<PrizeDescribe> val) {
    val.forEach(props ->
        emojiBagService.add(param.getAcceptUserId(), Long.valueOf(props.getContent())));
//    userCacheService.remove(param.getAcceptUserId(), UserHashKey.EMOJI);
  }

  public void sendProps(SendRewardAbstract param, List<PrizeDescribe> val) {
    Map<Long, PropsSourceRecord> propsSourceRecordMap = propsSourceRecordService.mapByIds(
        val.stream().map(propsActivityRewardConfig -> DataTypeUtils
                .toLong(propsActivityRewardConfig.getContent()))
            .collect(Collectors.toSet()));

    val.forEach(prizeDescribe -> sendActivityPropsReward(param.getAcceptUserId(),
        param.getOrigin(),
        prizeDescribe,
        propsSourceRecordMap.get(prizeDescribe.getContentLong()))
    );
  }

  private void saveSendLog(SendRewardAbstract param, List<PrizeDescribe> values) {
    propsSendLogService.saveBatch(values.stream().filter(Objects::nonNull)
        .map(conf -> {
          PropsSendLog propsSendLog = new PropsSendLog()
              .setUserId(param.getAcceptUserId())
              .setGroupId(conf.getTrackId())
              .setType(conf.getType())
              .setDetailType(conf.getDetailType())
              .setContent(conf.getContent())
              .setQuantity(conf.getQuantity())
              .setOrigin(String.valueOf(param.getOrigin()));
          propsSendLog.setCreateTime(TimestampUtils.now());
          propsSendLog.setUpdateTime(TimestampUtils.now());
          return propsSendLog;
        }).collect(Collectors.toList()));
  }

  private void sendGold(SendRewardAbstract param, List<PrizeDescribe> val) {
    BigDecimal obtainGolds = val.stream()
        .filter(prize -> StringUtils
            .isNotBlank(prize.getContent()))
        .map(prize -> new BigDecimal(prize.getContent()))
        .reduce(BigDecimal.ZERO, BigDecimal::add)
        .setScale(0, RoundingMode.DOWN);

    walletGoldGateway.changeBalance(new GoldReceiptCmd()
        .setReceiptType(ReceiptType.INCOME)
        .setConsumeId(IdWorkerUtils.getId())
        .setEventId(param.getTrackId().toString())
        .setUserId(param.getAcceptUserId())
        .setSysOrigin(param.getSysOrigin())
        .setOrigin(GoldOrigin.valueOf(param.getOrigin().name()))
        .setAmount(PennyAmount.ofDollar(obtainGolds))
        .setRemark(val.stream().map(PrizeDescribe::getRemark).filter(StringUtils::isNotBlank)
            .distinct()
            .collect(Collectors.joining(","))));
  }


  private void sendActivityPropsReward(Long userId,
      SendPropsOrigin origin,
      PrizeDescribe prize,
      PropsSourceRecord propsSourceRecord) {

    if (Objects.isNull(propsSourceRecord)) {
      return;
    }

    if (PropsCommodityType.NOBLE_VIP.eq(propsSourceRecord.getType())) {
      sendNobleVip(prize.getTrackId(),
          userId,
          propsSourceRecord,
          prize.getQuantity(),
          origin);

      return;
    }

    propsStoreRepository.shippingProductProps(new ProductProps()
        .setInitiateUserId(userId)
        .setDays(prize.getQuantity())
        .setProductId(prize.getTrackId())
        .setPropsOrigin(origin.name())
        .setPropsResources(propsMaterialInfraConvertor.toPropsResources(propsSourceRecord))
        .setPropsPrices(BigDecimal.ZERO));
  }





  public void sendNobleVip(Long productId, Long acceptUserId, PropsSourceRecord record,
      Integer days, SendPropsOrigin origin) {

    NobleVipAbility config = getNobleVipConfig(record.getId());
    propsStoreRepository.shippingProductProps(new ProductProps()
        .setInitiateUserId(acceptUserId)
        .setDays(days)
        .setProductId(productId)
        .setPropsOrigin(origin.name())
        .setPropsResources(propsMaterialInfraConvertor.toPropsResources(record))
        .setPropsPrices(BigDecimal.ZERO));

    changeCapacity(acceptUserId, config);

    if (Objects.nonNull(config.cardId())  && config.cardId() > 0) {
      propsStoreRepository.shippingProductProps(new ProductProps()
          .setInitiateUserId(acceptUserId)
          .setDays(days)
          .setProductId(productId)
          .setPropsOrigin(origin.name())
          .setPropsResources(propsStoreRepository.getPropsResourcesById(config.cardId()))
          .setPropsPrices(BigDecimal.ZERO));
    }

    if (Objects.nonNull(config.getAvatarFrameId()) && config.getAvatarFrameId() > 0) {
      propsStoreRepository.shippingProductProps(new ProductProps()
          .setInitiateUserId(acceptUserId)
          .setDays(days)
          .setProductId(productId)
          .setPropsOrigin(origin.name())
          .setPropsResources(propsStoreRepository.getPropsResourcesById(config.getAvatarFrameId()))
          .setPropsPrices(BigDecimal.ZERO));
    }

    if (Objects.nonNull(config.chatBubbleId()) && config.chatBubbleId() > 0) {
      propsStoreRepository.shippingProductProps(new ProductProps()
          .setInitiateUserId(acceptUserId)
          .setDays(days)
          .setProductId(productId)
          .setPropsOrigin(origin.name())
          .setPropsResources(propsStoreRepository.getPropsResourcesById(config.chatBubbleId()))
          .setPropsPrices(BigDecimal.ZERO));
    }
    if (Objects.nonNull(config.dataCardId()) && config.dataCardId() > 0) {
      propsStoreRepository.shippingProductProps(new ProductProps()
          .setInitiateUserId(acceptUserId)
          .setDays(days)
          .setProductId(productId)
          .setPropsOrigin(origin.name())
          .setPropsResources(propsStoreRepository.getPropsResourcesById(config.dataCardId()))
          .setPropsPrices(BigDecimal.ZERO));
    }

    //保存vip实际权益有效时间
    boolean isDuke = Objects.equals(config.getVipType(), NobleVipEnum.DUKE.name());
    boolean isKing = Objects.equals(config.getVipType(), NobleVipEnum.KING.name());
    boolean isEmperor = Objects.equals(config.getVipType(), NobleVipEnum.EMPEROR.name());
    if (isDuke || isKing || isEmperor) {
      propsVipActualEquityService.save(acceptUserId, record.getId(), Long.valueOf(days));
    }

  }
  private void changeCapacity(Long userId, NobleVipAbility config) {
    roomProfileManagerService.updateSelectiveSettingByUserId(userId,
        new RoomSetting()
            .setMaxMember(config.getRoomMaxMember())
            .setMaxAdmin(config.getAdminNumber()));
  }

  private NobleVipAbility getNobleVipConfig(Long nobleVipId) {
    NobleVipAbility nobleVipAbility = propsStoreRepository.getNobleVipAbility(nobleVipId);
    // 5003 产品不存在
    ResponseAssert.notNull(ProductErrorCode.NOT_FOUND_PRODUCT, nobleVipAbility);
    return nobleVipAbility;
  }

  private void sendUserBadge(SendRewardAbstract param, List<PrizeDescribe> val) {
    val.stream()
        .filter(prize -> !prize.isRoomBadge())
        .forEach(prize -> sendUserBadge(param.getAcceptUserId(), prize));
  }

  public void sendUserBadge(Long userId, PrizeDescribe prize) {

    //获得正在佩戴中的徽章
    List<BadgeBackpack> useBadges = badgeBackpackService.listUseBadgeByUserId(userId);

    if (CollectionUtils.isNotEmpty(useBadges) && useBadges.size() >= 8) {
      //卸下部分佩戴中的徽章
        removeWears(useBadges, 8);
    }

    if (Objects.equals(prize.getQuantity(), BigDecimal.ZERO)) {
      badgeBackpackService
          .activationPermanentAndUse(userId,
              DataTypeUtils.toLong(prize.getContent()));
//      UserCacheService.removeCache(userId);
      return;
    }
    badgeBackpackService.activationTemporaryAndUse(userId,
        DataTypeUtils.toLong(prize.getContent()), prize.getQuantity());
//    userProfileRepository.removeCache(userId);
  }

    private void removeWears(List<BadgeBackpack> useBadges, Integer maxDisplay) {
        Set<Long> ids = Sets.newHashSet();
        for (int i = 0; i < useBadges.size(); i++) {
          if (i > maxDisplay) {
            ids.add(useBadges.get(i).getId());
          }
        }
        badgeBackpackService.removeWearByIds(ids);
    }


    private void sendRoomBadge(SendRewardAbstract param, List<PrizeDescribe> val) {
    Long roomId = roomProfileManagerService.getRoomId(param.getAcceptUserId());
    if (Objects.isNull(roomId)) {
      log.warn("领取房间徽章错误,没有找到用户房间信息:{}", roomId);
      return;
    }
    val.stream().filter(PrizeDescribe::isRoomBadge)
        .forEach(prize -> sendRoomBadge(param.getAcceptUserId(), roomId, prize));
  }

  private void sendRoomBadge(Long acceptUserId, Long roomId, PrizeDescribe prize) {
    if (Objects.equals(prize.getQuantity(), BigDecimal.ZERO)) {
      roomBadgeBackpackService
          .activationPermanent(roomId, acceptUserId, prize.getContentLong());
      return;
    }
    roomBadgeBackpackService
        .activationTemporary(roomId,
            acceptUserId,
            prize.getContentLong(),
            prize.getQuantity());
  }


  private Map<String, List<PropsActivityRewardConfig>> mapPropsGroup(
      Long sourceGroupId) {
    return propsActivityRewardConfigService.mapGroupTypeById(sourceGroupId);
  }


}
