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

import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.common.pet.PetCommon;
import com.red.circle.other.app.dto.clientobject.pet.PetIncomeCO;
import com.red.circle.other.app.dto.cmd.pet.PetCurrentFeedingCmd;
import com.red.circle.other.infra.database.cache.service.other.PetCacheService;
import com.red.circle.other.infra.database.rds.dto.pet.PetFeedingDTO;
import com.red.circle.other.infra.database.rds.entity.pet.PetFeedingRecord;
import com.red.circle.other.infra.database.rds.entity.pet.PetStage;
import com.red.circle.other.infra.database.rds.entity.pet.PetUserPetBag;
import com.red.circle.other.infra.database.rds.enums.pet.PetStageLevelEnum;
import com.red.circle.other.infra.database.rds.service.pet.PetFeedingRecordService;
import com.red.circle.other.infra.database.rds.service.pet.PetStageService;
import com.red.circle.other.infra.database.rds.service.pet.PetUserPetBagService;
import com.red.circle.other.infra.database.rds.service.pet.PetUserPetIncomeDetailsService;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.text.StringUtils;
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.time.ZoneId;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * 当前喂养.
 *
 * @author pengliang on 2021/10/16
 */
@Component
@RequiredArgsConstructor
public class PetCurrentFeedingCmdExe {

  private final PetCommon petCommon;
  private final RedisService redisService;
  private final PetCacheService petCacheService;
  private final PetStageService petStageService;
  private final WalletGoldClient walletGoldClient;
  private final PetUserPetBagService petUserPetBagService;
  private final PetFeedingRecordService petFeedingRecordService;
  private final PetUserPetIncomeDetailsService petUserPetIncomeDetailsService;

  public Boolean execute(PetCurrentFeedingCmd cmd) {

    if (isFeed(cmd)) {
      return Boolean.FALSE;
    }

    PetUserPetBag petBag = petUserPetBagService.getLatest(cmd.getUid());
    // 1033 记录不存在
    ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_RECORD_INFO, petBag);
    PetStage petStage = petStageService
        .getByState(petBag.getPetId(), petBag.getPetStage());
    // 1034 配置信息不存在
    ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, petStage);
    // 1036 喂养失败，没到喂养时间
    ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
        petBag.getLatestFeedingTime().before(TimestampUtils.now()));

    int feedType = getFeedType(cmd, petBag, petStage);

    if (!Objects.equals(feedType, 0) && ArithmeticUtils
        .gtZero(petStage.getFoodFeedingNum())) {

      BigDecimal consumeQuantity = feedType == 2
          ? petStage.getAccelerateFeed().multiply(BigDecimal.valueOf(petBag.getFeedingNum()))
          .setScale(0, RoundingMode.DOWN)
          : petStage.getFoodFeedingNum();
      petStage.setFoodFeedingNum(consumeQuantity);
      walletGoldClient.changeBalance(GoldReceiptCmd.builder()
          .appExpenditure()
          .eventId(petBag.getPetId())
          .userId(cmd.getReqUserId())
          .sysOrigin(cmd.getReqSysOrigin().getOrigin())
          .origin(Objects.equals(cmd.getUid(), cmd.getUserId()) ? GoldOrigin.FEEDING_PETS
              : GoldOrigin.FEEDING_PETS_HELP_OTHERS)
          .amount(consumeQuantity)
          .build());
    }

    petFeedingRecordService.save(new PetFeedingRecord()
        .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
        .setUserId(petBag.getUserId())
        .setPetId(petBag.getPetId())
        .setPetBagId(petBag.getId())
        .setFeedingNum(petStage.getFoodFeedingNum())
        .setFeedType(feedType)
        .withCreateUser(cmd.getReqUserId()));

    String stage = getPetStageLevel(petBag, petStage);
    if (PetStageLevelEnum.valueOf(stage).getLevel() > petStage.getLevel()) {
      petUserPetBagService.feedingAdvancedStage(petBag.getId(), stage);
      calculateIncome(petBag, petStage);
      return Boolean.TRUE;
    }
    petUserPetBagService.changeStage(new PetFeedingDTO()
        .setId(petBag.getId())
        .setStage(stage)
        .setFeedingNum(1)
        .setConsumeQuantity(petStage.getFoodFeedingNum())
    );
    return Boolean.FALSE;
  }

  private boolean isFeed(PetCurrentFeedingCmd cmd) {
    return !Objects.equals(cmd.getFeedType(), 2) && !redisService
        .setIfAbsent("PET_F:" + cmd.getUserId(), 1, TimeUnit.MINUTES);
  }

  /**
   * 0.免费喂养 1.正常喂养 2.加速喂养.
   */
  private int getFeedType(PetCurrentFeedingCmd cmd, PetUserPetBag petBag, PetStage petStage) {
    if (Objects.equals(cmd.getFeedType(), 2)) {
      return 2;
    }
    long freeFeedSize = petCacheService
        .incrFreeFeeding(petBag.getUserId(), petBag.getPetId(), ZoneId.of(cmd.getReqZoneId()));
    return petStage.getFreeFeedingNum().longValue() >= freeFeedSize
        ? 0 : 1;
  }

  private void calculateIncome(PetUserPetBag petBag, PetStage petStage) {
    if (petUserPetIncomeDetailsService.existsNotCharged(petBag.getUserId())) {
      return;
    }
    List<PetIncomeCO> petIncomes = petCommon.calculateIncome(petStage);
    if (CollectionUtils.isEmpty(petIncomes)) {
      return;
    }
    petUserPetIncomeDetailsService
        .saveBatch(petCommon.toListPetUserPetIncomeDetails(petIncomes, petBag));
  }

  private String getPetStageLevel(PetUserPetBag petBag, PetStage petStage) {
    String state = petBag.getPetStage();
    if ((petBag.getFeedingNum() + 1) < petStage.getUpgradeFeedingNum()) {
      return state;
    }
    PetStage nextPetStage = petStageService
        .getNextPetStage(petBag.getPetId(), petStage.getLevel());

    if (Objects.isNull(nextPetStage)) {
      return state;
    }

    String name = PetStageLevelEnum.getNameByLevel(nextPetStage.getLevel());
    return StringUtils.isNotBlank(name) ? name : state;
  }
}
