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

import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.dto.cmd.AppExtCommand;
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.dto.cmd.family.ReceiveFamilyAwardCmd;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberInfo;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberWeekAwardRecord;
import com.red.circle.other.infra.database.rds.entity.family.FamilyRewardRule;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberWeekAwardRecordService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberWeekExpService;
import com.red.circle.other.infra.database.rds.service.family.FamilyRewardRuleService;
import com.red.circle.other.inner.asserts.FamilyErrorCode;
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.util.Objects;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * <p>
 * 用户领取家族奖励.
 * </p>
 *
 * @author pengshigang
 * @since 2021-08-06
 */
@Component
@RequiredArgsConstructor
public class ReceiveFamilyAwardExe {

  private final RedisService redisService;
  private final WalletGoldClient walletGoldClient;
  private final FamilyMemberInfoService familyMemberInfoService;
  private final FamilyRewardRuleService familyRewardRuleService;
  private final FamilyMemberWeekExpService familyMemberWeekExpService;
  private final FamilyMemberWeekAwardRecordService familyMemberWeekAwardRecordService;

  public void execute(ReceiveFamilyAwardCmd cmd) {

    try {
      if (redisService.lock(getLockKey(cmd), 10)) {
        handleAwards(cmd);
      }
    } finally {
      redisService.unlock(getLockKey(cmd));
    }

  }

  private String getLockKey(ReceiveFamilyAwardCmd cmd) {
    return "RECEIVE_FAMILY_AWARD_" + cmd.getAwardRuleId() + "_" + cmd.getReqSysOrigin().getOrigin()
        + "_"
        + cmd.getReqUserId();
  }

  private void handleAwards(ReceiveFamilyAwardCmd cmd) {
    ResponseAssert.notNull(CommonErrorCode.OPERATING_FAILURE, cmd.getAwardRuleId());

    FamilyMemberInfo memberInfo = getFamilyMemberByUserId(cmd);
    ResponseAssert.notNull(FamilyErrorCode.NOT_EXIST_FAMILY_INFO_DATA, memberInfo);

    FamilyRewardRule rule = familyRewardRuleService.getRuleById(cmd.getAwardRuleId());
    ResponseAssert.notNull(FamilyErrorCode.NOT_FOUND_REWARD, rule);

    if (isReceived(cmd, memberInfo)) {
      ResponseAssert.failure(CommonErrorCode.NON_REPEATABLE);
      return;
    }

    if (getExpCountByFamily(memberInfo) < rule.getGoalExp()) {
      ResponseAssert.failure(CommonErrorCode.CAN_NOT_RECEIVE);
      return;
    }

    addWeekAwardRecord(cmd, memberInfo, rule);

    sendCandy(cmd, rule);
  }

  private void addWeekAwardRecord(ReceiveFamilyAwardCmd cmd, FamilyMemberInfo memberInfo,
      FamilyRewardRule rule) {
    familyMemberWeekAwardRecordService.save(new FamilyMemberWeekAwardRecord()
        .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
        .setFamilyId(memberInfo.getFamilyId())
        .setFamilyMemberId(memberInfo.getId())
        .setRewardRuleId(rule.getId())
    );
  }

  private void sendCandy(ReceiveFamilyAwardCmd cmd, FamilyRewardRule rule) {
    walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .appIncome()
        .userId(cmd.getReqUserId())
        .eventId(cmd.getAwardRuleId())
        .sysOrigin(SysOriginPlatformEnum.valueOf(cmd.getReqSysOrigin().getOrigin()))
        .origin(GoldOrigin.GOLD_CREATE_FAMILY)
        .amount(new BigDecimal(rule.getRewardQuantity()))
        .build());
  }

  private Boolean isReceived(ReceiveFamilyAwardCmd cmd, FamilyMemberInfo memberInfo) {
    return familyMemberWeekAwardRecordService
        .isReceived(memberInfo.getFamilyId(), memberInfo.getId(), cmd.getAwardRuleId());
  }

  private FamilyMemberInfo getFamilyMemberByUserId(AppExtCommand cmd) {
    return familyMemberInfoService.getFamilyMemberByUserId(cmd.getReqUserId());
  }

  private Long getExpCountByFamily(FamilyMemberInfo memberInfo) {
    return Optional.ofNullable(familyMemberWeekExpService
            .getFamilyMemberWeekExp(memberInfo.getFamilyId(), memberInfo.getMemberUserId()))
        .map(weekExp -> Objects.isNull(weekExp.getExp()) ? 0 : weekExp.getExp()).orElse(0L);
  }

}
