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

import com.red.circle.component.redis.service.RedisService;
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.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.cmd.family.ApplyJoinFamilyCmd;
import com.red.circle.other.app.util.OfficialNoticeUtils;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.gateway.user.ability.UserRegionGateway;
import com.red.circle.other.infra.common.family.FamilyCommon;
import com.red.circle.other.infra.database.cache.key.FamilyKeys;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMemberInfo;
import com.red.circle.other.infra.database.rds.entity.family.FamilyMessage;
import com.red.circle.other.infra.database.rds.service.family.FamilyBaseInfoService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMemberInfoService;
import com.red.circle.other.infra.database.rds.service.family.FamilyMessageService;
import com.red.circle.other.infra.enums.family.FamilyMsgTypeEnum;
import com.red.circle.other.infra.enums.family.FamilyRoleEnum;
import com.red.circle.other.inner.asserts.DynamicErrorCode;
import com.red.circle.other.inner.asserts.FamilyErrorCode;
import com.red.circle.other.inner.asserts.user.UserRelationErrorCode;
import com.red.circle.other.inner.enums.FamilyMessageStatusEnum;
import com.red.circle.other.inner.model.dto.famliy.FamilyDetailsDTO;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 申请加入家族.
 * </p>
 *
 * @author pengshigang
 * @since 2021-07-26
 */
@Component
@RequiredArgsConstructor
public class ApplyJoinFamilyExe {

  private final RedisService redisService;
  private final FamilyCommon familyCommon;
  private final UserRegionGateway userRegionGateway;
  private final FamilyMessageService familyMessageService;
  private final FamilyMemberInfoService familyMemberInfoService;

  @Transactional(rollbackFor = Exception.class)
  public void execute(ApplyJoinFamilyCmd cmd) {
    String string = redisService.getString(FamilyKeys.USER_LEAVE.getKey(cmd.getReqUserId()));
    ResponseAssert.isNull(FamilyErrorCode.USER_RECENT_LEAVE, string);

    ResponseAssert.isFalse(FamilyErrorCode.THERE_ARE_FAMILIES_ERROR, getExistFamilyByUserId(cmd));

    ResponseAssert.isTrue(FamilyErrorCode.NOT_EXIST_FAMILY_INFO_DATA, isExistFamilyById(cmd));

    ResponseAssert.isFalse(FamilyErrorCode.REPEAT_APPLICATION, getExistApplyingMsg(cmd));

    FamilyDetailsDTO familyDetails = familyCommon.getFamilyDetails(cmd.getReqSysOrigin().getOrigin(), cmd.getFamilyId());

    FamilyMemberInfo admin = familyMemberInfoService.getAdmin(cmd.getFamilyId(), FamilyRoleEnum.ADMIN.name());
    boolean eqRegion = userRegionGateway.checkEqRegion(cmd.getReqUserId(), admin.getMemberUserId());
    ResponseAssert.isTrue(UserRelationErrorCode.UNAVAILABLE_NOT_REGION, eqRegion);

    Integer familyMemberCount = getFamilyMemberCount(familyDetails);

    ResponseAssert
        .isTrue(FamilyErrorCode.FAMILY_MEMBER_MAX, familyDetails.getMaxMember() > familyMemberCount);

    saveMsg(cmd);

  }

  private void saveMsg(ApplyJoinFamilyCmd cmd) {
    familyMessageService.save(new FamilyMessage()
        .setFamilyId(cmd.getFamilyId())
        .setSenderUser(cmd.getReqUserId())
        .setSysOrigin(cmd.getReqSysOrigin().getOrigin())
        .setStatus(FamilyMessageStatusEnum.PENDING.getCode())
        .setContent(FamilyMsgTypeEnum.JOIN_FAMILY.getDescription())
        .setType(FamilyMsgTypeEnum.JOIN_FAMILY.name())
    );
  }

  private Integer getFamilyMemberCount(FamilyDetailsDTO levelCacheCO) {
    return familyMemberInfoService.getFamilyMemberCount(levelCacheCO.getFamilyId());
  }

  private Boolean getExistApplyingMsg(ApplyJoinFamilyCmd cmd) {
    ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, cmd.getFamilyId());
    ResponseAssert.notNull(CommonErrorCode.DATA_ERROR, cmd.getReqUserId());
    return familyMessageService.isExistApplyingMsg(cmd.getFamilyId(), cmd.getReqUserId());
  }

  private Boolean getExistFamilyByUserId(ApplyJoinFamilyCmd cmd) {
    return familyCommon.isExistFamilyByUserId(cmd.getReqUserId());
  }

  private Boolean isExistFamilyById(ApplyJoinFamilyCmd cmd) {
    return familyCommon.isExistFamilyById(cmd.getFamilyId());
  }

}
