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

import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.external.inner.endpoint.message.ImMessageClient;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.mq.business.model.event.user.UserCardApplyEvent;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.other.app.dto.cmd.user.relation.friendcard.FriendshipCardApplyCmd;
import com.red.circle.other.infra.database.cache.service.user.UserFriendshipCardConfigCacheService;
import com.red.circle.other.infra.database.rds.entity.user.user.FriendshipCardConfig;
import com.red.circle.other.infra.database.rds.entity.user.user.GiveFriendshipCardApply;
import com.red.circle.other.infra.database.rds.service.user.user.FriendshipCardService;
import com.red.circle.other.infra.database.rds.service.user.user.GiveFriendshipCardApplyService;
import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
import com.red.circle.other.infra.enums.user.user.UserCardRelationshipEnum;
import com.red.circle.other.inner.asserts.user.UserRelationErrorCode;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.error.WalletErrorCode;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

/**
 * <p>
 * 发送友谊关系卡邀请 - 用户列表 .
 * </p>
 *
 * @author pengshigang
 * @since 2022-06-10
 */
@Component
@RequiredArgsConstructor
public class UserFriendCardSendApplyExe {

  private final RedisService redisService;
  private final ImMessageClient imMessageClient;
  private final WalletGoldClient walletGoldClient;
  private final UserMqMessageService userMqMessageService;
  private final FriendshipCardService friendshipCardService;
  private final GiveFriendshipCardApplyService giveFriendshipCardApplyService;
  private final UserFriendshipCardConfigCacheService userFriendshipCardConfigCacheService;

  public void execute(FriendshipCardApplyCmd cmd) {

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

    // 校验是否已经发送过该类型的卡片申请了，已发送则不能再次发送，需等到对方拒绝。
    ResponseAssert.isFalse(UserRelationErrorCode.EXIST_OR_REPEAT_APPLY_ERROR, isExistApply(cmd));

    // 校验存在同一卡片类型关系则不能再次邀请。
    ResponseAssert.isFalse(UserRelationErrorCode.ALREADY_FRIENDS, isExistCard(cmd));

    // 获得卡片配置
    FriendshipCardConfig configCache = getConfigCache(cmd);
    ResponseAssert.notNull(CommonErrorCode.CONFIGURATION_ERROR, configCache);

    // 校验余额
    ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE,
        ResponseAssert.requiredSuccess(
            walletGoldClient.getBalance(cmd.requiredReqUserId())
        ).getPennyAmount() > 0);

    // 防止重复触发
    ResponseAssert.isTrue(CommonErrorCode.REPEATED_SUBMISSION, redisService.setIfAbsent(
        "USER_FRIEND_CARD_SEND_APPLY:" + cmd.requiredReqUserId() + "_" + cmd
            .getAcceptApplyUserId(), 5, TimeUnit.SECONDS));

    // 发生邀请成功则扣配置的金币。拒绝或自然退回则返回金币。不处理则1天过期，到期自动退回。
    Long id = save(cmd, configCache);

    // 支付卡片费用
    changeGoldBalance(cmd, configCache);

    // 发通知
    imMessageClient.sendMessageText(cmd.getSendApplyUserIdString(),
        cmd.getAcceptApplyUserIdString(),
        cmd.getApplyText());

    // 定时1天后系统处理申请
    userMqMessageService.userFriendshipCardApplyDelayed(
        new UserCardApplyEvent()
            .setId(id)
            .setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.requireReqSysOrigin())),
        TimestampUtils.nowPlusDays(1));
  }

  private WalletReceiptResDTO changeGoldBalance(FriendshipCardApplyCmd cmd,
      FriendshipCardConfig cardConfig) {
    return ResponseAssert.requiredSuccess(
        walletGoldClient.changeBalance(GoldReceiptCmd.builder()
            .appExpenditure()
            .userId(cmd.requiredReqUserId())
            .eventId(cardConfig.getId())
            .sysOrigin(cmd.requireReqSysOrigin())
            .origin(GoldOrigin.USER_FRIENDSHIP_CARD_GIVE_AWAY)
            .amount(cardConfig.getCardMoney())
            .build())
    );
  }

  private Long save(FriendshipCardApplyCmd cmd, FriendshipCardConfig cardConfig) {

    Long id = IdWorkerUtils.getId();

    giveFriendshipCardApplyService.save(new GiveFriendshipCardApply()
        .setId(id)
        .setSendUserId(cmd.requiredReqUserId())
        .setAcceptUserId(cmd.getAcceptApplyUserId())
        .setCardType(cardConfig.getCardType())
        .setCardGold(cardConfig.getCardMoney())
        .setGiveText(cmd.getApplyText())
        .setStatus(CpApplyStatus.WAIT.name())
    );

    return id;
  }

  private FriendshipCardConfig getConfigCache(FriendshipCardApplyCmd cmd) {
    return userFriendshipCardConfigCacheService.getSysOriginConfig(cmd.requireReqSysOrigin(),
        cmd.getType());
  }

  private Boolean isExistCard(FriendshipCardApplyCmd cmd) {
    return friendshipCardService
        .exist(cmd.getType(), cmd.requiredReqUserId(), cmd.getAcceptApplyUserId());
  }

  private Boolean isExistApply(FriendshipCardApplyCmd cmd) {
    return giveFriendshipCardApplyService
        .exist(cmd.getType(), cmd.getAcceptApplyUserId(), cmd.requiredReqUserId())
        || giveFriendshipCardApplyService
        .exist(cmd.getType(), cmd.requiredReqUserId(), cmd.getAcceptApplyUserId());
  }


}
