package com.red.circle.other.app.service.game;

import com.google.common.base.Throwables;
import com.red.circle.auth.inner.endpoint.AuthClient;
import com.red.circle.component.game.hkys.GameHkysService;
import com.red.circle.component.game.hkys.request.HkysUserCoinUpdate;
import com.red.circle.component.game.hkys.request.HkysUserProfileQuery;
import com.red.circle.component.game.hkys.response.HkysResponse;
import com.red.circle.component.game.hkys.response.HkysUserProfileResponse;
import com.red.circle.component.game.hkys.response.HyksErrorEnum;
import com.red.circle.component.game.hkys.response.UpdateUserCoinResponse;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.exception.ResponseException;
import com.red.circle.framework.core.security.UserCredential;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.date.LocalDateTimeUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.num.ArithmeticUtils;
import com.red.circle.tool.core.parse.DataTypeUtils;
import com.red.circle.tool.core.text.StringUtils;
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.cmd.GoldReceiptCmd.GoldReceiptCmdBuilder;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * Hkys 游戏服务 实现.
 *
 * @author pengliang on 2022/9/20
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class HkysServiceImpl implements GameHkysService {

  private final AuthClient authClient;
  private final RedisService redisService;
  private final WalletGoldClient walletGoldClient;
  private final UserProfileGateway userProfileGateway;
  private final EnumConfigCacheService enumConfigCacheService;
  private final UserProfileAppConvertor userProfileAppConvertor;

  @Override
  public HkysResponse<HkysUserProfileResponse> getUserProfile(HkysUserProfileQuery query) {

    if (!UserCredential.checkToken(query.getToken(), query.longUid())) {
      return new HkysResponse<HkysUserProfileResponse>()
          .setErrorCode(HyksErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
    }

    UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
        userProfileGateway.getByUserId(query.longUid())
    );
    if (Objects.isNull(userProfile)) {
      return new HkysResponse<HkysUserProfileResponse>()
          .setErrorCode(HyksErrorEnum.USER_NOT_FOUND.getErrorCode());
    }

    if (StringUtils.isBlank(userProfile.getUserAvatar())) {
      userProfile.setUserAvatar(
          enumConfigCacheService.getValue(EnumConfigKey.DEFAULT_USER_AVATAR,
              userProfile.getOriginSys()));
    }

    return new HkysResponse<HkysUserProfileResponse>()
        .setErrorCode(HyksErrorEnum.SUCCESS.getErrorCode())
        .setData(new HkysUserProfileResponse()
            .setUid(Objects.toString(userProfile.getId()))
            .setNickname(userProfile.getUserNickname())
            .setAvatar(userProfile.getUserAvatar())
            .setCoin(
                ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
                    .getDollarAmount().longValue())
        );
  }


  @Override
  public HkysResponse<UpdateUserCoinResponse> updateUserCoin(HkysUserCoinUpdate param) {
    long startTime = LocalDateTimeUtils.nowEpochMilli();
    Long userId = param.longUid();
    if (!UserCredential.checkToken(param.getToken(), userId)) {
      return new HkysResponse<UpdateUserCoinResponse>()
          .setErrorCode(HyksErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
    }

    if (!redisService.setIfAbsent("GHYKS:" + param.getOrderId(), 5,
        TimeUnit.MINUTES)) {
      log.warn("重复触发消息: {}", JacksonUtils.toJson(param));
      return new HkysResponse<UpdateUserCoinResponse>()
          .setErrorCode(406);
    }

    long walletStartTime = LocalDateTimeUtils.nowEpochMilli();

    try {

      WalletReceiptResDTO res = walletGoldClient.changeBalance(createReceipt(param)).getBody();

      long walletEndTime = LocalDateTimeUtils.nowEpochMilli() - walletStartTime;

      long totalTime = LocalDateTimeUtils.nowEpochMilli() - startTime;

      if (totalTime >= 500 || walletEndTime >= 500) {
        log.error("totalTime={},walletEndTim={},type={}"
            , totalTime
            , walletEndTime
            , param.getType());
      }


      return new HkysResponse<UpdateUserCoinResponse>()
          .setErrorCode(HyksErrorEnum.SUCCESS.getErrorCode())
          .setData(new UpdateUserCoinResponse()
              .setEventId(res.getAssetRecordId().toString())
              .setBalance(res.getBalance().getDollarAmount().stripTrailingZeros())
          );
    } catch (ResponseException ex) {
      if (Objects.equals(ex.getErrorCode(), WalletErrorCode.INSUFFICIENT_BALANCE.getCode())) {
        return new HkysResponse<UpdateUserCoinResponse>()
            .setErrorCode(4004);
      }
    } catch (Exception ex) {
      log.error("ex:{}", Throwables.getStackTraceAsString(ex));
    }

    return new HkysResponse<UpdateUserCoinResponse>()
        .setErrorCode(HyksErrorEnum.SERVER_ERROR.getErrorCode());
  }

  private GoldReceiptCmd createReceipt(HkysUserCoinUpdate param) {
    UserCredential userCredential = UserCredential.parseToken(param.getToken());
    return createGoldReceiptCmdBuilder(param.getType())
        .userId(param.longUid())
        .sysOrigin(userCredential.getSysOrigin())
        .origin(GoldOrigin.HKYS_GAME.name() + "_" + param.getGameId())
        .originDescribe(GoldOrigin.HKYS_GAME.getDesc() + "[" + param.getGameId() + "]")
        .eventId(param.getOrderId())
        .amount(ArithmeticUtils.toBigDecimal(param.getCoin()))
        .build();
  }

  private GoldReceiptCmdBuilder createGoldReceiptCmdBuilder(Integer type) {
    if (Objects.equals(type, 1)) {
      return GoldReceiptCmd.builder().appExpenditure();
    }

    if (Objects.equals(type, 2)) {
      return GoldReceiptCmd.builder().appIncome();
    }

    throw new IllegalArgumentException("3rd party param [type] error: " + type);
  }

  /**
   * 强制验证令牌.
   *
   * @return true 通过，false 不通过
   */
  private boolean checkTokenStrict(String token, Long userId) {

    UserCredential tokenContent = UserCredential.parseToken(token);

    UserCredential registerCredential = ResponseAssert.requiredSuccess(
        authClient.getUserCredential(DataTypeUtils.toLong(userId)));

    return UserCredential.checkToken(tokenContent, registerCredential)
        && Objects.equals(tokenContent.getUserId(), userId);
  }

}
