package com.red.circle.console.app.service.app.props;

import com.red.circle.common.business.core.enums.ReceiptType;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.common.business.enums.DiamondOrigin;
import com.red.circle.common.business.enums.PropsActivityRewardEnum;
import com.red.circle.common.business.enums.SendPropsOrigin;
import com.red.circle.console.app.dto.cmd.app.props.PropsGiveUserCmd;
import com.red.circle.console.app.service.admin.UserAccountService;
import com.red.circle.console.inner.error.ConsoleErrorCode;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.other.inner.endpoint.live.RoomManagerClient;
import com.red.circle.other.inner.endpoint.material.props.*;
import com.red.circle.other.inner.endpoint.user.user.SysUserProfileClient;
import com.red.circle.other.inner.enums.material.BadgeBackpackExpireTypeEnum;
import com.red.circle.other.inner.enums.material.ConsolePropsTypeEnum;
import com.red.circle.other.inner.enums.material.SysNobleVipTypeEnum;
import com.red.circle.other.inner.enums.sys.SysBadgeConfigTypeEnum;
import com.red.circle.other.inner.enums.sys.SysCurrencySendReasonEnum;
import com.red.circle.other.inner.model.cmd.material.GiveBadgeCmd;
import com.red.circle.other.inner.model.cmd.material.GivePropsBackpackCmd;
import com.red.circle.other.inner.model.cmd.material.PropCouponGrantCmd;
import com.red.circle.other.inner.model.dto.material.PropsCommodityStoreDTO;
import com.red.circle.other.inner.model.dto.material.PropsResourcesDTO;
import com.red.circle.other.inner.model.dto.material.props.PropsNobleVipAbilityDTO;
import com.red.circle.tool.core.collection.CollectionUtils;
import com.red.circle.tool.core.date.TimestampUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.parse.DataTypeUtils;
import com.red.circle.tool.core.sequence.IdWorkerUtils;
import com.red.circle.tool.core.text.StringUtils;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.model.cmd.user.UserAccountQryCmd;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.other.inner.endpoint.user.user.UserProfileClient;
import com.red.circle.wallet.inner.endpoint.wallet.WalletDiamondClient;
import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient;
import com.red.circle.wallet.inner.model.cmd.DiamondReceiptCmd;
import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 给用户赠送道具 服务实现类.
 * </p>
 *
 * @author pengshigang
 * @since 2021-06-05
 */
@Service
@RequiredArgsConstructor
public class PropsGiveServiceImpl implements PropsGiveService {

  private final WalletGoldClient walletGoldClient;
  private final UserProfileClient userProfileClient;
  private final RoomManagerClient roomManagerClient;
  private final PropsSourceClient propsSourceClient;
  private final WalletDiamondClient walletDiamondClient;
  private final PropsBackpackClient propsBackpackClient;
  private final BadgeBackpackClient badgeBackpackClient;
  private final PropsNobleVipClient propsNobleVipClient;
  private final RoomThemeBackpackClient roomThemeBackpackClient;
  private final PropsCommodityStoreClient propsCommodityStoreClient;
  private final UserAccountService userAccountService;
  private final PropCouponClient propCouponClient;

  @Override
  public String give(PropsGiveUserCmd cmd) {
    ResponseAssert.isFalse(ConsoleErrorCode.ACCOUNT_DOES_NOT_EXIST,
        StringUtils.isBlank(cmd.getReceiverAccounts()));

    ResponseAssert.isFalse(ConsoleErrorCode.ACCOUNT_DOES_NOT_EXIST,
        StringUtils.isBlank(cmd.getReceiverAccounts().replaceAll("\\s", "")));

    Set<String> accounts = Arrays.stream(cmd.getReceiverAccounts().split(","))
        .filter(StringUtils::isNotBlank)
        .collect(Collectors.toSet());

    ResponseAssert.notEmpty(ConsoleErrorCode.ACCOUNT_DOES_NOT_EXIST, accounts);
    ResponseAssert.isFalse(ConsoleErrorCode.ACCOUNT_QUANTITY_EXCESS_ERROR, accounts.size() > 15);

    List<UserProfileDTO> users = ResponseAssert.requiredSuccess(
        userProfileClient.listByAccount(
            new UserAccountQryCmd()
                .setSysOrigin(cmd.getSysOrigin())
                .setAccounts(accounts)
        )
    );
    ResponseAssert.notEmpty(ConsoleErrorCode.ACCOUNT_DOES_NOT_EXIST, users);

    StringBuilder errorAccounts = getErrorAccounts(accounts, users);
    if (errorAccounts.length() > 0) {
      return errorAccounts.toString();
    }

    // 群发
    users.forEach(user ->
        send(new PropsGiveUserCmd()
            .setReceiverId(user.getId())
            .setExchangeDays(cmd.getExchangeDays())
            .setType(cmd.getType())
            .setSecondaryType(cmd.getSecondaryType())
            .setContent(cmd.getContent())
            .setSysOrigin(cmd.getSysOrigin())
            .setReceiverAccounts(cmd.getReceiverAccounts())
            .setVipOrigin(cmd.getVipOrigin())
            .setOpsUser(cmd.getOpsUser())
        ));
    return null;
  }

  @Override
  public void givePropCoupon(PropCouponGrantCmd cmd) {
    propCouponClient.grantCoupons(cmd);
  }

  private void send(PropsGiveUserCmd cmd) {
    String sysOrigin = getUserSysOrigin(cmd.getReceiverId());
    ResponseAssert.isFalse(UserErrorCode.USER_INFO_NOT_FOUND, StringUtils.isBlank(sysOrigin));

    // 后台赠送道具校验权限
    if (!"BADGE".equals(cmd.getType())) {
      checkUserAccountAlias(cmd);

    }

    if (Objects.equals(cmd.getType(), PropsActivityRewardEnum.PROPS.name())) {

      isNullStr(cmd.getSecondaryType());
      isNullOrZero(cmd.getExchangeDays());
      gtThirtyDaysError(cmd.getExchangeDays());

      if (Objects.equals(cmd.getSecondaryType(), ConsolePropsTypeEnum.NOBLE_VIP.getName())) {
        ResponseAssert.isFalse(ConsoleErrorCode.DATE_INCOMPLETE_ERROR,
            StringUtils.isBlank(cmd.getVipOrigin()));
        handleNobleVip(cmd, sysOrigin);
        // 使用最大等级的VIP
        useMaxNobleVip(cmd.getReceiverId());
        return;
      }
      if (Objects.equals(cmd.getSecondaryType(), ConsolePropsTypeEnum.THEME.getName())) {
        handleTheme(cmd);
        //发送通知
        propsBackpackClient.givePropsNotify(cmd.getReceiverId(),cmd.getSecondaryType(),cmd.getExchangeDays(), Long.valueOf(cmd.getContent()));
        return;
      }
      sendPropsBackpack(cmd);
      return;
    }
    if (Objects.equals(cmd.getType(), PropsActivityRewardEnum.BADGE.name())) {
//      ApiAssert.isFalse(ErrorCodeEnum.ADMIN_NO_RIGHT_SEND_ROOM_BADGE,
//          Objects.equals(giveParam.getSecondaryType(),
//              SysBadgeConfigTypeEnum.ROOM_ACHIEVEMENT.getName()));
      isNullOrZero(cmd.getExchangeDays());
      handleBadge(cmd);
      //发送通知
      propsBackpackClient.givePropsNotify(cmd.getReceiverId(),cmd.getSecondaryType(),cmd.getExchangeDays(), Long.valueOf(cmd.getContent()));
      return;
    }
    if (Objects.equals(cmd.getType(), PropsActivityRewardEnum.GOLD.name())) {
      handleGold(cmd);
      return;
    }
    if (Objects.equals(cmd.getType(), PropsActivityRewardEnum.DIAMOND.name())) {
      handleDiamond(cmd);
      return;
    }


    ResponseAssert.failure(ConsoleErrorCode.NOT_DATA);
  }

  private void checkUserAccountAlias(PropsGiveUserCmd cmd) {

    boolean hassed = userAccountService.hasButtonsAlias(cmd.getOpsUser().intValue(), "send:props:" + cmd.getSecondaryType());
    if (!hassed) {
      ResponseAssert.failure(ConsoleErrorCode.ACCOUNT_NOT_PERMISSIONS);
    }

  }

  private StringBuilder getErrorAccounts(Set<String> accounts, List<UserProfileDTO> users) {

    StringBuilder errorAccounts = new StringBuilder();
    if (CollectionUtils.isEmpty(users)) {
      accounts.forEach(account -> errorAccounts.append("【").append(account).append("】"));
      return errorAccounts;
    }

    Set<String> correctAccounts = users.stream().map(user -> {
      if (accounts.contains(user.getAccount())) {
        return user.getAccount();
      }

      if (Objects.nonNull(user.getOwnSpecialId()) && accounts
          .contains(user.getOwnSpecialId().getAccount())) {
        return user.getOwnSpecialId().getAccount();
      }
      return null;
    }).filter(Objects::nonNull).collect(Collectors.toSet());

    if (CollectionUtils.isEmpty(correctAccounts)) {
      accounts.forEach(account -> errorAccounts.append("【").append(account).append("】"));
      return errorAccounts;
    }

    Set<String> errorAccountSet = accounts.stream()
        .filter(account -> !correctAccounts.contains(account)).collect(Collectors.toSet());
    if (CollectionUtils.isEmpty(errorAccountSet)) {
      return errorAccounts;
    }

    errorAccountSet.forEach(account -> errorAccounts.append("【").append(account).append("】"));
    return errorAccounts;
  }

  private String getUserSysOrigin(Long userId) {
    return Optional.ofNullable(
            ResponseAssert.requiredSuccess(userProfileClient.getByUserId(userId)))
        .map(UserProfileDTO::getOriginSys)
        .orElse(null);
  }

  private void handleTheme(PropsGiveUserCmd cmd) {

    Long sourceId = Long.parseLong(cmd.getContent());
    ResponseAssert.notNull(ConsoleErrorCode.RESOURCES_SHELF_NOT_EXISTENT, sourceId);

    PropsResourcesDTO propsSourceRecord = ResponseAssert.requiredSuccess(
        propsSourceClient.getById(sourceId)
    );

    ResponseAssert.notNull(ConsoleErrorCode.RESOURCES_SHELF_NOT_EXISTENT, propsSourceRecord);

    ResponseAssert.requiredSuccess(roomThemeBackpackClient.sendRoomTheme(
        cmd.getReceiverId(),
        sourceId,
        cmd.getExchangeDays(),
        getSendPropsOrigin(cmd)
    ));
    //发送通知
//    propsBackpackClient.givePropsNotify(cmd.getReceiverId(),cmd.getSecondaryType(),cmd.getExchangeDays());

  }


  private void handleNobleVip(PropsGiveUserCmd cmd, String sysOrigin) {

    PropsNobleVipAbilityDTO vipAbility = getVipAbilityBySysOriginSourceId(cmd, sysOrigin);
    sendPropsBackpack(cmd);

    if (Objects.nonNull(vipAbility.getCarId()) && !Objects
        .equals(vipAbility.getCarId(), 0L)) {
      cmd.setContent(vipAbility.getCarId().toString());
      cmd.setSecondaryType(ConsolePropsTypeEnum.RIDE.getName());
      sendPropsBackpack(cmd);
    }

    if (Objects.nonNull(vipAbility.getAvatarFrameId())
        && !Objects.equals(vipAbility.getAvatarFrameId(), 0L)) {
      cmd.setContent(vipAbility.getAvatarFrameId().toString());
      cmd.setSecondaryType(ConsolePropsTypeEnum.AVATAR_FRAME.getName());
      sendPropsBackpack(cmd);
    }

    if (Objects.nonNull(vipAbility.getChatBubbleId())
        && !Objects.equals(vipAbility.getChatBubbleId(), 0L)) {
      cmd.setContent(vipAbility.getChatBubbleId().toString());
      cmd.setSecondaryType(ConsolePropsTypeEnum.CHAT_BUBBLE.getName());
      sendPropsBackpack(cmd);
    }

    if (Objects.nonNull(vipAbility.getDataCardId())
        && !Objects.equals(vipAbility.getDataCardId(), 0L)) {
      cmd.setContent(vipAbility.getDataCardId().toString());
      cmd.setSecondaryType(ConsolePropsTypeEnum.DATA_CARD.getName());
      sendPropsBackpack(cmd);
    }

    roomManagerClient.updateRoomSettingMemberCapacity(cmd.getReceiverId(),
        vipAbility.getRoomMaxMember(), vipAbility.getAdminNumber());

    //保存vip实际权益有效时间
    Boolean isDuke = Objects.equals(vipAbility.getVipType(), SysNobleVipTypeEnum.DUKE.name());
    Boolean isKing = Objects.equals(vipAbility.getVipType(), SysNobleVipTypeEnum.KING.name());
    Boolean isBuy = Objects.equals(cmd.getVipOrigin(), SendPropsOrigin.BUY_OR_GIVE.name());
    Boolean isActivity = Objects
        .equals(cmd.getVipOrigin(), SendPropsOrigin.ACTIVITY_AWARD.name());
    if ((isDuke || isKing) && (isBuy || isActivity)) {
      propsNobleVipClient.addActualEquity(cmd.getReceiverId(),
          vipAbility.getId(), Long.valueOf(cmd.getExchangeDays()));
    }

  }

  private void useMaxNobleVip(Long receiverId) {
    PropsNobleVipAbilityDTO abilityDTO = propsNobleVipClient.getUserMaxAbilityDTO(receiverId).getBody();
    if (Objects.isNull(abilityDTO)) {
      return;
    }
    propsBackpackClient.switchUseProps(receiverId, abilityDTO.getId());
    propsBackpackClient.switchUseProps(receiverId, abilityDTO.getAvatarFrameId());
    propsBackpackClient.switchUseProps(receiverId, abilityDTO.getDataCardId());
    propsBackpackClient.switchUseProps(receiverId, abilityDTO.getCarId());
    userProfileClient.removeCacheAll(receiverId);
  }

  private PropsNobleVipAbilityDTO getVipAbilityBySysOriginSourceId(PropsGiveUserCmd cmd,
      String sysOrigin) {
    PropsCommodityStoreDTO commodityStore = ResponseAssert.requiredSuccess(
        propsCommodityStoreClient.getPropsCommodityStore(sysOrigin,
            DataTypeUtils.toLong(cmd.getContent()))
    );

    ResponseAssert.notNull(ConsoleErrorCode.NOT_FOUND_INFO, commodityStore);
    PropsNobleVipAbilityDTO nobleVipAbility = ResponseAssert.requiredSuccess(
        propsNobleVipClient.getAbilityDTO(commodityStore.getSourceId()));
    ResponseAssert.notNull(ConsoleErrorCode.NOT_FOUND_INFO, nobleVipAbility);
    return nobleVipAbility;
  }

  private void sendPropsBackpack(PropsGiveUserCmd cmd) {

    ResponseAssert
        .isFalse(ConsoleErrorCode.RESOURCES_SHELF_NOT_EXISTENT, propsIdNull(cmd.getContent()));

    SendPropsOrigin origin = getSendPropsOrigin(cmd);
    GivePropsBackpackCmd givePropsBackpackCmd = new GivePropsBackpackCmd()
            .setAcceptUserId(cmd.getReceiverId())
            .setPropsId(DataTypeUtils.toLong(cmd.getContent()))
            .setType(cmd.getSecondaryType())
            .setOrigin(origin.name())
            .setOriginDesc(origin.getDesc())
            .setDays(cmd.getExchangeDays())
            .setUseProps(Boolean.TRUE)
            .setAllowGive(Boolean.FALSE)
            .setOpUser(cmd.getOpsUser());
    propsBackpackClient.giveProps(givePropsBackpackCmd);
    //发送通知
    propsBackpackClient.givePropsNotify(cmd.getReceiverId(),cmd.getSecondaryType(),cmd.getExchangeDays(), Long.valueOf(cmd.getContent()));

  }

  private void gtThirtyDaysError(Integer day) {
    ResponseAssert.isFalse(ConsoleErrorCode.DAY_EXCEED_LIMIT_ERROR, day > 30);
  }

  private SendPropsOrigin getSendPropsOrigin(PropsGiveUserCmd cmd) {
    if (Objects.equals(cmd.getVipOrigin(), SendPropsOrigin.ACTIVITY_AWARD.name())) {
      return SendPropsOrigin.ACTIVITY_AWARD;
    }
    if (Objects.equals(cmd.getVipOrigin(), SendPropsOrigin.BUY_OR_GIVE.name())) {
      return SendPropsOrigin.BUY_OR_GIVE;
    }
    return SendPropsOrigin.OFFICIAL_GIFT;
  }

  private void handleBadge(PropsGiveUserCmd cmd) {

    isNullStr(cmd.getContent());
    isNullOrZero(cmd.getExchangeDays());

    //徽章赠送天数大于1000天则为永久赠送
    BadgeBackpackExpireTypeEnum typeEnum;
    if (cmd.getExchangeDays() >= 1000) {
      typeEnum = BadgeBackpackExpireTypeEnum.PERMANENT;
    } else {
      typeEnum = BadgeBackpackExpireTypeEnum.TEMPORARY;
      gtThirtyDaysError(cmd.getExchangeDays());
    }

    isUserNormal(cmd.getReceiverId());
    badgeBackpackClient.giveBadge(new GiveBadgeCmd()
        .setAcceptUserId(cmd.getReceiverId())
        .setBadgeId(Long.parseLong(cmd.getContent()))
        .setExpireType(typeEnum)
        .setDays(cmd.getExchangeDays())
        .setBadgeType(SysBadgeConfigTypeEnum.valueOf(cmd.getSecondaryType()))
    );

  }

  private void isUserNormal(Long userId) {
    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(userId));
    ResponseAssert.notNull(UserErrorCode.USER_INFO_NOT_FOUND, userProfile);
    ResponseAssert.isTrue(UserErrorCode.USER_IS_DISABLED,
        userProfile.checkAccountAvailable(userProfile.getAccountStatus().name(),
            userProfile.getFreezingTime()));
  }


  private void isNullOrZero(Integer exchangeDays) {
    ResponseAssert.isFalse(ConsoleErrorCode.NUMBER_VIOLATION_ERROR,
        Objects.isNull(exchangeDays) );
  }

  private void handleDiamond(PropsGiveUserCmd cmd) {
    numberError(cmd.getContent());
    Long id = IdWorkerUtils.getId();
    walletDiamondClient.changeBalance(
        new DiamondReceiptCmd()
            .setConsumeId(Objects.toString(id))
            .setTrackId(id)
            .setUserId(cmd.getReceiverId())
            .setSysOrigin(SysOriginPlatformEnum.valueOf(cmd.getSysOrigin()))
            .setOriginUserId(cmd.getReceiverId())
            .setOrigin(DiamondOrigin.OFFICIAL_GIFT)
            .setAmount(toBigDecimal(cmd.getContent()))
            .setCreateTime(TimestampUtils.now())
            .setType(ReceiptType.INCOME)
    );
  }

  private void handleGold(PropsGiveUserCmd cmd) {
    numberError(cmd.getContent());

    Long userId = cmd.getReceiverId();

    UserProfileDTO userProfile = ResponseAssert.requiredSuccess(
        userProfileClient.getByUserId(userId));
    if (Objects.isNull(userProfile)) {
      return;
    }
    BigDecimal quantity = toBigDecimal(cmd.getContent());
    walletGoldClient.changeBalance(
        GoldReceiptCmd.builder()
            .opsIncome()
            .opUserId(cmd.getOpsUser())
            .userId(userId)
            .sysOrigin(userProfile.getOriginSys())
            .eventId("-")
            .origin(SysCurrencySendReasonEnum.INTERNAL.name())
            .originDescribe(SysCurrencySendReasonEnum.INTERNAL.getTypeName())
            .amount(quantity)
            .remark("道具赠送")
            .build()
    );
  }


  private BigDecimal toBigDecimal(String money) {
    return ArithmeticUtils.toBigDecimal(money);
  }

  private void numberError(String number) {
    ResponseAssert.isFalse(ConsoleErrorCode.NUMBER_VIOLATION_ERROR,
        StringUtils.isBlank(number) || Objects.equals(number, "0"));
  }

  private Boolean propsIdNull(String id) {
    return StringUtils.isBlank(id) || Objects.equals(id, "0");
  }

  private void isNullStr(String str) {
    ResponseAssert.isFalse(ConsoleErrorCode.DATE_INCOMPLETE_ERROR, StringUtils.isBlank(str));
  }

}
