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

import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
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.other.app.convertor.sys.SysConfigAppConvertor;
import com.red.circle.other.app.dto.clientobject.sys.PrizeContentCO;
import com.red.circle.other.infra.common.activity.PropsActivitySendCommon;
import com.red.circle.other.infra.common.activity.send.SendRewardBuild;
import com.red.circle.other.infra.database.cache.service.other.ActivityLotteryConfCacheService;
import com.red.circle.other.inner.model.cmd.activity.lottery.ActivityLotteryTurntableCache;
import com.red.circle.other.inner.model.cmd.activity.lottery.Prizes;
import com.red.circle.other.inner.model.cmd.material.PrizeDescribe;
import com.red.circle.tool.core.AliasMethod;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.wallet.inner.endpoint.wallet.WalletBeanClient;
import com.red.circle.wallet.inner.model.cmd.BeanReceiptCmd;
import com.red.circle.wallet.inner.model.enums.BeanOrigin;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * 宠物转盘抽奖.
 *
 * @author pengliang on 2021/11/22
 */
@Component
@RequiredArgsConstructor
public class PetTurntableLotteryCmdExe {

  private final WalletBeanClient walletBeanClient;
  private final SysConfigAppConvertor sysConfigMapper;
  private final PropsActivitySendCommon sendPropsManager;
  private final ActivityLotteryConfCacheService activityLotteryConfCacheService;

  public PrizeContentCO execute(AppExtCommand cmd) {
    ActivityLotteryTurntableCache cache = activityLotteryConfCacheService
        .getLotteryTurntable(SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin()));
    ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, cache);

    return Optional.ofNullable(
            lottery(SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin()),
                createAliasMethod(cache.filterInStock()),
                cache.filterInStock()))
        .map(prizeContent -> {
          walletBeanClient.changeBalance(new BeanReceiptCmd()
              .setConsumeId(IdWorkerUtils.getIdStr())
              .setTrackId(IdWorkerUtils.getIdStr())
              .setType(ReceiptType.EXPENDITURE)
              .setUserId(cmd.getReqUserId())
              .setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin()))
              .setOrigin(BeanOrigin.PET_TURNTABLE_LOTTERY)
              .setAmount(new BigDecimal(cache.getStartConsume()))
          );

          sendPropsManager.sendGroup(SendRewardBuild.builder()
                  .trackId(IdWorkerUtils.getId())
                  .acceptUserId(cmd.getReqUserId())
                  .origin(SendPropsOrigin.PET_TURNTABLE_LOTTERY)
                  .sysOrigin(SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin()))
                  .build(),
              Collections.singletonList(new PrizeDescribe()
                  .setTrackId(cmd.getReqUserId())
                  .setType(prizeContent.getPropsType())
                  .setDetailType(prizeContent.getPropsDetailType())
                  .setContent(prizeContent.getContent())
                  .setQuantity(prizeContent.getQuantity())
              )
          );
          return prizeContent;
        }).orElse(null);
  }

  private AliasMethod createAliasMethod(List<Prizes> prizesArrays) {
    return new AliasMethod(getProbabilityArrayDouble(prizesArrays));
  }

  private List<Double> getProbabilityArrayDouble(List<Prizes> prizesArrays) {
    return prizesArrays.stream().map(prizes -> prizes.getProbability().doubleValue())
        .collect(Collectors.toList());
  }

  private PrizeContentCO lottery(SysOriginPlatformEnum sysOrigin, AliasMethod aliasMethod,
      List<Prizes> prizesArrays) {

    if (CollectionUtils.isEmpty(prizesArrays)) {
      return null;
    }

    Prizes prizes = prizesArrays.get(aliasMethod.next());

    if (Objects.equals(prizes.getUnlimited(), Boolean.TRUE)) {
      return sysConfigMapper.toPrizeContentCO(prizes.getPrize());
    }

    Long consumeInStock = activityLotteryConfCacheService
        .consumeInStock(sysOrigin, prizes.getCode());
    if (consumeInStock > prizes.getInStock()) {
      prizes.setInStock(consumeInStock);
      activityLotteryConfCacheService.minusConsumeInStock(sysOrigin, prizes.getCode());
      List<Prizes> newPrizesArrays = getAvailableInStockPrizes(prizesArrays);
      return lottery(sysOrigin, createAliasMethod(newPrizesArrays), newPrizesArrays);
    }
    return sysConfigMapper.toPrizeContentCO(prizes.getPrize());
  }

  private List<Prizes> getAvailableInStockPrizes(List<Prizes> prizesArrays) {
    return prizesArrays.stream()
        .filter(Prizes::checkAvailableInStock)
        .collect(Collectors.toList());
  }
}
