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

import com.google.common.collect.Sets;
import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd;
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.response.CommonErrorCode;
import com.red.circle.other.app.common.cp.CpRelationGoldConfig;
import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpDismissApplyCO;
import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.model.user.UserProfile;
import com.red.circle.other.infra.common.user.UserGiftBackpackCommon;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.cache.service.cp.CpRelationshipCacheService;
import com.red.circle.other.infra.database.cache.service.user.UserCpValueCacheService;
import com.red.circle.other.infra.database.mongo.service.user.count.WeekCpValueCountService;
import com.red.circle.other.infra.database.rds.entity.user.user.CpRelationship;
import com.red.circle.other.infra.database.rds.service.user.user.CpApplyService;
import com.red.circle.other.infra.database.rds.service.user.user.CpBlessRecordService;
import com.red.circle.other.infra.database.rds.service.user.user.CpCabinAchieveService;
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.CpRelationshipType;
import com.red.circle.tool.core.collection.MapBuilder;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.tuple.PennyAmount;
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 org.springframework.stereotype.Component;

/**
 * 取消CP申请.
 *
 * @author pengliang on 2021/7/14
 */
@Component
@RequiredArgsConstructor
public class DismissCpApplyCmdExe {

  private static final long DISMISS_DURATION_MS = 0L;

  private final CpValueService cpValueService;
  private final CpApplyService cpApplyService;
  private final OfficialNoticeClient officialNoticeClient;
  private final CpRelationshipService cpRelationshipService;
  private final UserGiftBackpackCommon userGiftBackpackCommon;
  private final WeekCpValueCountService weekCpValueCountService;
  private final UserCpValueCacheService userCpValueCacheService;
  private final CpBlessRecordService cpBlessRecordService;
  private final CpCabinAchieveService cpCabinAchieveService;
  private final UserProfileGateway userProfileGateway;
  private final CpRelationshipCacheService cpRelationshipCacheService;
  private final WalletGoldClient walletGoldClient;
  private final EnumConfigCacheService enumConfigCacheService;

  public CpDismissApplyCO execute(CpApplyDismissCmd cmd) {
    String relationType = parseRelationType(cmd.getRelationType());

    var cpRelationship = cpRelationshipService.getByUserId(
        cmd.requiredReqUserId(), cmd.getCpUserId(), relationType);
    if (Objects.isNull(cpRelationship)) {
      CpRelationship dismissingCp = cpRelationshipService.getDismissingCp(
          cmd.requiredReqUserId(), cmd.getCpUserId(), relationType);
      if (Objects.nonNull(dismissingCp)) {
        cleanCpData(dismissingCp, relationType);
      }
      return buildDismissedResult(cmd.getCpUserId(), relationType, TimestampUtils.now().getTime());
    }

    String sysOrigin = resolveSysOrigin(cmd, cpRelationship);
    BigDecimal dismissConsumeGold = dismissConsumeGold(relationType, sysOrigin);
    deductDismissGold(cmd.requiredReqUserId(), cpRelationship, relationType,
        dismissConsumeGold, sysOrigin);

    // 更新申请状态为解散
    cpApplyService.dismiss(cmd.requiredReqUserId(), relationType);

    // 解除冷静期为0，先标记解除再立即清理关系数据
    cpRelationshipService.updateToDismissing(cmd.requiredReqUserId(), cmd.getCpUserId(), relationType);

    sendDismissNotice(cmd, relationType);

    Long dismissEndTime = TimestampUtils.now().getTime() + DISMISS_DURATION_MS;
    cleanCpData(cpRelationship, relationType);
    return buildDismissedResult(cmd.getCpUserId(), relationType, dismissEndTime);

  }

  private void sendDismissNotice(CpApplyDismissCmd cmd, String relationType) {
    UserProfile userProfile = userProfileGateway.getByUserId(cmd.requiredReqUserId());
    if (Objects.isNull(userProfile)) {
      return;
    }
    long dismissEndTime = TimestampUtils.now().getTime() + DISMISS_DURATION_MS;
    officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder()
        .toAccount(cmd.getCpUserId())
        .noticeType(OfficialNoticeTypeEnum.CP_DISMISS)
        .templateParam(MapBuilder.builder()
            .put("nickname", userProfile.getUserNickname())
            .put("userNickname", userProfile.getUserNickname())
            .put("account", userProfile.getAccount())
            .put("userAvatar", userProfile.getUserAvatar())
            .put("countryCode", userProfile.getCountryCode())
            .put("countryName", userProfile.getCountryName())
            .put("relationType", relationType)
            .put("dismissEndTime", dismissEndTime)
            .build())
        .expand(cmd.requiredReqUserId())
        .build());
  }

  private void cleanCpData(CpRelationship relationship, String relationType) {
    cpRelationshipService.deleteByUserPair(relationship.getUserId(), relationship.getCpUserId(),
        relationType);
    weekCpValueCountService.removeThisWeek(relationship.getUserId(), relationship.getCpUserId());
    weekCpValueCountService.removeThisSeason(relationship.getUserId(), relationship.getCpUserId());
    cpValueService.deleteById(relationship.getCpValId());
    userGiftBackpackCommon.removeUserCpGift(
        Sets.newHashSet(relationship.getUserId(), relationship.getCpUserId()));
    userCpValueCacheService.remove(relationship.getUserId(), relationship.getCpUserId());
    cpCabinAchieveService.deleteByCpValId(relationship.getCpValId());
    cpBlessRecordService.deleteByCpValId(relationship.getCpValId());
    cpRelationshipCacheService.remove(
        Arrays.asList(relationship.getUserId(), relationship.getCpUserId()));
  }

  private BigDecimal dismissConsumeGold(String relationType, String sysOrigin) {
    return CpRelationGoldConfig.defaultGold(enumConfigCacheService.getValueBigDecimal(
        CpRelationGoldConfig.dismissConsumeGoldKey(relationType), sysOrigin));
  }

  private void deductDismissGold(Long userId, CpRelationship relationship, String relationType,
      BigDecimal dismissConsumeGold, String sysOrigin) {
    if (dismissConsumeGold.compareTo(BigDecimal.ZERO) <= 0) {
      return;
    }
    ResponseAssert.requiredSuccess(walletGoldClient.changeBalance(GoldReceiptCmd.builder()
        .appExpenditure()
        .userId(userId)
        .sysOrigin(sysOrigin)
        .eventId(dismissEventId(relationship, relationType))
        .origin(GoldOrigin.CP_DISMISS)
        .amount(PennyAmount.ofDollar(dismissConsumeGold))
        .build()));
  }

  private String dismissEventId(CpRelationship relationship, String relationType) {
    if (Objects.nonNull(relationship.getId())) {
      return "CP_DISMISS:" + relationship.getId();
    }
    if (Objects.nonNull(relationship.getCpValId())) {
      return "CP_DISMISS:" + relationship.getCpValId();
    }
    return "CP_DISMISS:" + relationType + ":" + relationship.getUserId() + ":"
        + relationship.getCpUserId();
  }

  private String resolveSysOrigin(CpApplyDismissCmd cmd, CpRelationship relationship) {
    try {
      return cmd.requireReqSysOrigin();
    } catch (Exception ignored) {
      return resolveRelationshipSysOrigin(relationship);
    }
  }

  private String resolveRelationshipSysOrigin(CpRelationship relationship) {
    return Objects.toString(relationship.getSysOrigin(), "");
  }

  private CpDismissApplyCO buildDismissedResult(Long cpUserId, String relationType,
      Long dismissEndTime) {
    return new CpDismissApplyCO()
        .setCpUserId(cpUserId)
        .setRelationType(relationType)
        .setStatus("NONE")
        .setDismissEndTime(dismissEndTime)
        .setDismissRemainSeconds(0L);
  }

  private String parseRelationType(String relationType) {
    try {
      return CpRelationshipType.normalize(relationType);
    } catch (IllegalArgumentException ex) {
      ResponseAssert.failure(CommonErrorCode.TYPE_IS_NOT_IN_SCOPE, "relationType is not supported.");
      return CpRelationshipType.CP.name();
    }
  }

}
