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

import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
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.CpApplyEvent;
import com.red.circle.mq.rocket.business.producer.UserMqMessageService;
import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService;
import com.red.circle.other.infra.database.rds.entity.user.user.CpApply;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.entity.user.user.CpValue;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService;
import com.red.circle.other.infra.database.rds.service.user.user.CpValueService;
import com.red.circle.other.infra.enums.user.user.CpApplyStatus;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.other.inner.asserts.user.UserRelationErrorCode;
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.Arrays;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 处理cp.
 *
 * @author pengliang on 2021/7/13
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ProcessCpApplyCmd {

  private final CpApplyService cpApplyService;
  private final CpValueService cpValueService;
  private final ImMessageClient imMessageClient;
  private final WalletGoldClient walletGoldClient;
  private final UserMqMessageService userMqMessageService;
  private final CpRelationshipService cpRelationshipService;
  private final UserProfileGateway userProfileGateway;
  private final CpRelationshipCacheService cpRelationshipCacheService;

  public void execute(ProcessApplyCmd cmd) {
    process(cmd);
  }

  private void process(ProcessApplyCmd cmd) {
    CpApply cpApply = cpApplyService.getById(cmd.getApplyId());
    // 没有找到映射信息
    ResponseAssert.notNull(CommonErrorCode.NOT_FOUND_MAPPING_INFO, cpApply);

    // 状态错误
    ResponseAssert.isTrue(CommonErrorCode.STATE_ERROR,
        Objects.equals(cpApply.getStatus(), CpApplyStatus.WAIT.name()));

    // 检查是否是复合申请
    CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
        cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId());
    boolean isReconcile = Objects.nonNull(dismissingCp);

    if (!isReconcile) {
      // 非复合申请：CP已存在
      ResponseAssert.isFalse(UserRelationErrorCode.ME_OR_YOU_CP_EXISTED,
          cpRelationshipService.existsCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()));

      // 检查CP数量限制（最多4个）
      int senderCpCount = cpRelationshipService.countCp(cmd.requiredReqUserId());
      int receiverCpCount = cpRelationshipService.countCp(cpApply.getAcceptApplyUserId());
      ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, senderCpCount >= 4);
      ResponseAssert.isFalse(UserRelationErrorCode.CP_LIMIT_EXCEEDED, receiverCpCount >= 4);
    }

    // 操作失败
    ResponseAssert.isTrue(CommonErrorCode.OPERATING_FAILURE,
        cpApplyService.changeStatusById(cmd.getApplyId(), getStatus(cmd)));

    if (!isAgree(cmd)) {
      returnGoldCoins(cmd, cpApply);
      return;
    }

    refuseStatusWait(cmd, cpApply);
    
    if (isReconcile) {
      // 复合：更新状态为NORMAL
      cpRelationshipService.reconcile(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId());
    } else {
      // 新建关系
      cpRelationshipService
          .addCp(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId(), createCpValue(),
              cmd.requireReqSysOrigin());
    }

    cpRelationshipCacheService.remove(Arrays.asList(cpApply.getSendApplyUserId(), cpApply.getAcceptApplyUserId()));
  }

  private void refuseStatusWait(ProcessApplyCmd cmd, CpApply cpApply) {
    cpApplyService.refuseStatusWait(cmd.requiredReqUserId());
    userMqMessageService.cpApplyDelayed(
        new CpApplyEvent()
            .setId(cpApply.getId())
            .setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.requireReqSysOrigin())),
        TimestampUtils.nowPlusMinutes(1));
  }

  private void returnGoldCoins(ProcessApplyCmd cmd, CpApply cpApply) {
    walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .appIncome()
        .userId(cpApply.getSendApplyUserId())
        .eventId(cpApply.getId())
        .sysOrigin(cmd.requireReqSysOrigin())
        .origin(GoldOrigin.CP_BUILD)
        .amount(cpApply.getApplyConsumeGold())
        .build());
    cpApplyService.changeStatusById(cmd.getApplyId(), CpApplyStatus.REIMBURSE);
  }

  private Long createCpValue() {
    CpValue cpValue = new CpValue()
        .setCpVal(BigDecimal.ZERO);
    cpValueService.save(cpValue);
    return cpValue.getId();
  }

  private boolean isAgree(ProcessApplyCmd cmd) {
    return Objects.equals(cmd.getAgree(), Boolean.TRUE);
  }

  private CpApplyStatus getStatus(ProcessApplyCmd cmd) {
    return isAgree(cmd)
        ? CpApplyStatus.AGREE : CpApplyStatus.REFUSE;
  }

}
