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

import com.red.circle.auth.inner.endpoint.AuthClient;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.game.baishun.GameBaishunService;
import com.red.circle.component.game.baishun.error.BaishunErrorCode;
import com.red.circle.component.game.baishun.request.BaishunChangeCurrencyRequest;
import com.red.circle.component.game.baishun.request.BaishunTokenRequest;
import com.red.circle.component.game.baishun.request.BaishunTokenUpdateRequest;
import com.red.circle.component.game.baishun.request.BaishunUserProfileRequest;
import com.red.circle.component.game.baishun.response.BaishunChangeCurrencyResponse;
import com.red.circle.component.game.baishun.response.BaishunResponse;
import com.red.circle.component.game.baishun.response.BaishunTokenResponse;
import com.red.circle.component.game.baishun.response.BaishunUserProfileResponse;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
import com.red.circle.external.inner.model.cmd.message.BroadcastGroupMsgBodyCmd;
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.dto.ReqSysOrigin;
import com.red.circle.framework.core.exception.ResponseException;
import com.red.circle.framework.core.security.UserCredential;
import com.red.circle.mq.business.model.event.task.TaskApprovalEvent;
import com.red.circle.mq.rocket.business.producer.TaskMqMessage;
import com.red.circle.order.inner.model.enums.MonthlyRechargeType;
import com.red.circle.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.cmd.activity.RankingDataUpdateCmd;
import com.red.circle.other.app.dto.cmd.task.RoomDailyTaskProgressUpdateCmd;
import com.red.circle.other.app.enums.violation.GameOriginEnum;
import com.red.circle.other.app.service.activity.ActivityRechargeTicketService;
import com.red.circle.other.app.service.activity.RankingActivityService;
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
import com.red.circle.other.app.service.task.TaskService;
import com.red.circle.other.app.util.DateTimeAsiaRiyadhUtils;
import com.red.circle.other.app.util.OfficialNoticeUtils;
import com.red.circle.other.domain.game.GameRankingIncrementCmd;
import com.red.circle.other.domain.gateway.game.GameRankingGateway;
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
import com.red.circle.other.domain.ranking.RankingActivityType;
import com.red.circle.other.domain.ranking.RankingCycleType;
import com.red.circle.other.domain.ranking.RankingDimension;
import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService;
import com.red.circle.other.infra.database.cache.service.other.GameListCacheService;
import com.red.circle.other.infra.database.rds.entity.sys.GameListConfig;
import com.red.circle.other.infra.database.rds.service.sys.GameListConfigService;
import com.red.circle.other.inner.enums.config.EnumConfigKey;
import com.red.circle.other.inner.enums.task.RoomDailyTaskCode;
import com.red.circle.other.inner.model.dto.user.UserProfileDTO;
import com.red.circle.tool.core.date.DateUtils;
import com.red.circle.tool.core.date.LocalDateTimeUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.json.JacksonUtils;
import com.red.circle.tool.core.parse.DataTypeUtils;
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.cmd.GoldReceiptCmd.GoldReceiptCmdBuilder;
import com.red.circle.wallet.inner.model.dto.WalletReceiptResDTO;
import com.red.circle.wallet.inner.model.enums.GoldOrigin;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * baishun游戏服务 实现.
 *
 * @author pengliang on 2023/9/13
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GameBaishunServiceImpl implements GameBaishunService {

  private final AuthClient authClient;
  private final RedisService redisService;
  private final WalletGoldClient walletGoldClient;
  private final UserProfileGateway userProfileGateway;
  private final BaishunRuntimeConfigResolver baishunRuntimeConfigResolver;
  private final TaskMqMessage taskMqMessage;
  private final UserProfileAppConvertor userProfileAppConvertor;
  private final EnumConfigCacheService enumConfigCacheService;
  private final GameListConfigService gameListConfigService;
  private final ImGroupClient imGroupClient;
  private final RankingActivityService rankingActivityService;
  private final GameRankingGateway gameRankingGateway;
  private final GameListCacheService gameListCacheService;
  private final TaskService taskService;
  private final GameActivityService gameActivityService;
  private final ActivityRechargeTicketService activityRechargeTicketService;
  private final RoomDailyTaskProgressService roomDailyTaskProgressService;
  @Value("${red-circle.game-rank.gameid}")
  private String gameId;

  @Override
  public BaishunResponse<BaishunTokenResponse> getToken(BaishunTokenRequest request) {
    log.info("getToken: {}", request);
    if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
      log.info("getToken: {}", "验证签名失败");
      return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
      /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
        return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
      }*/
    }

    return getToken(request.getUserId());
  }

  @Override
  public BaishunResponse<BaishunUserProfileResponse> getUserProfile(BaishunUserProfileRequest request) {
    log.info("getUserProfile: {}", request);
    if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
      /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
        return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
      }*/
      log.info("getToken: {}", "验证签名失败");
      return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
    }

    if (!checkTokenStrict(request)) {
      return BaishunResponse.fail(BaishunErrorCode.USER_ACCOUNT_LIMIT);
    }

    UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
        userProfileGateway.getByUserId(DataTypeUtils.toLong(request.getUserId())));
    if (Objects.isNull(userProfile)) {
      return BaishunResponse.fail(BaishunErrorCode.READ_DATE_ERROR);
    }

    return BaishunResponse.success(new BaishunUserProfileResponse()
        .setUserId(request.getUserId())
        .setUserName(userProfile.getUserNickname())
        .setUserAvatar(userProfile.getUserAvatar())
        .setBalance(ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
            .getDollarAmount())
    );
  }

  @Override
  public BaishunResponse<BaishunTokenResponse> updateToken(BaishunTokenUpdateRequest request) {
    log.info("updateToken: {}", request);
    if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
      log.info("getToken: {}", "验证签名失败");
      return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
      /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
        return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
      }*/
    }

    if (!UserCredential.checkToken(request.getToken(), DataTypeUtils.toLong(request.getUserId()))) {
      return BaishunResponse.fail(BaishunErrorCode.USER_ACCOUNT_LIMIT);
    }

    return getToken(request.getUserId());
  }

  @Override
  public BaishunResponse<BaishunChangeCurrencyResponse> changeBalance(BaishunChangeCurrencyRequest request) {
    log.info("changeBalance: {}", request);
    if (!request.checkSignature(resolveRuntimeConfig(request.getAppId()).getAppKey())) {
      log.info("getToken: {}", "验证签名失败");
      return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
      /*if(!request.checkSignature(gameBaiShunTwoFunProperties.getAppKey())) {
        return BaishunResponse.fail(BaishunErrorCode.SIGNATURE_ERROR);
      }*/
    }

    if (!UserCredential.checkToken(request.getToken(), DataTypeUtils.toLong(request.getUserId()))) {
      return BaishunResponse.fail(BaishunErrorCode.USER_ACCOUNT_LIMIT);
    }

    if (Objects.equals(request.getCurrencyDiff(), 0L)) {
      return BaishunResponse.fail(BaishunErrorCode.DATE_ERROR);
    }

    if (!redisService.setIfAbsent("GBAISHUN:" + request.getOrderId(), 5,
        TimeUnit.MINUTES)) {
      log.warn("重复触发消息: {}", JacksonUtils.toJson(request));
      return null;
    }

    try {
      WalletReceiptResDTO receiptRes = walletGoldClient.changeBalance(createReceipt(request))
          .getBody();

      String sysOrigin = UserCredential.parseToken(request.getToken()).getSysOrigin();

      //游玩一句游戏结束 每日任务  baishun
      Long userId = DataTypeUtils.toLong(request.getUserId());
      // 检查是否未完成任务
      Boolean taskStatus = taskService.checkTaskStatus(userId, 4L, LocalDateTimeUtils.nowFormat("yyyy-MM-dd"));
      if (taskStatus) {
        taskMqMessage.sendTask(TaskApprovalEvent.builder()
                .taskId(4)
                .userId(userId)
                .day(LocalDateTimeUtils.nowFormat("yyyy-MM-dd"))
                .currencyDiff(request.getCurrencyDiff())
                .sysOrigin(sysOrigin)
                .gameName(GoldOrigin.BAISHUN_GAME.name())
                .gameId(Long.parseLong(request.getGameId()))
                .build());
      }

        GameListConfig gameListConfig = gameListCacheService.getByGameIdWithCache(
                request.getGameId(), 
                GameOriginEnum.BAISHUN.name(), 
                sysOrigin, 
                () -> gameListConfigService.getByGameId(request.getGameId(), GameOriginEnum.BAISHUN.name(), sysOrigin)
        );

        // request.getCurrencyDiff() > 0 是收入  request.getCurrencyDiff() < 0 是支出
        if (Objects.nonNull(gameListConfig)) {
            sendBroadcast(request, gameListConfig);

            //游戏参与打榜活动
            incGameRankingActivity(request);

            //累计游戏排行榜
            incGameRankingRecord(request, gameListConfig);

            //处理游戏获得任务
            if (request.getCurrencyDiff() > 0) {
                gameActivityService.handleGameSpendTask(userId, Math.abs(request.getCurrencyDiff()));

              // 累加用户获得的金币并发送抽奖券
//              activityRechargeTicketService.accumulatedRechargeAndAddTicket(userId, request.getCurrencyDiff(), MonthlyRechargeType.UNDEFINED);
            } else {
              updateRoomDailyTask(request.getCurrencyDiff(), userId);
            }
        }

      log.warn("{} 发送游戏获奖通知--金币数量{}", GoldOrigin.BAISHUN_GAME.name(), request.getCurrencyDiff());
      return BaishunResponse.success(new BaishunChangeCurrencyResponse()
              .setCurrencyBalance(receiptRes.getBalance().getDollarAmount().stripTrailingZeros()),
          Objects.toString(receiptRes.getAssetRecordId()));
    } catch (ResponseException ex) {
      if (Objects.equals(ex.getErrorCode(), 5000)) {
        return BaishunResponse.fail(BaishunErrorCode.INSUFFICIENT_ASSETS);
      }
    } catch (Exception ex) {
      return BaishunResponse.fail(BaishunErrorCode.SERVER_ERROR, ex.getMessage());
    }




    return BaishunResponse.fail(BaishunErrorCode.SERVER_ERROR);
  }

  private BaishunRuntimeConfig resolveRuntimeConfig(String appId) {
    return baishunRuntimeConfigResolver.resolveByApp(appId, null);
  }

  private void updateRoomDailyTask(Long coins, Long userId) {
    RoomDailyTaskCode taskType = RoomDailyTaskCode.PERSONAL_GAME_CONSUME;
    String redisKey = "room:daily:task:progress:" + taskType.name() + ":" + userId + ":" + ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
    Long total = redisService.increment(redisKey, Math.abs(coins.intValue()));
    redisService.expire(redisKey, DateTimeAsiaRiyadhUtils.getSecondsUntilMidnight(), TimeUnit.SECONDS);
    roomDailyTaskProgressService.updateTaskProgress(
            new RoomDailyTaskProgressUpdateCmd()
                    .setUserId(userId)
                    .setTaskCode(taskType.name())
                    .setProgressValue(total.intValue())
    );
  }

    private void sendBroadcast(BaishunChangeCurrencyRequest request, GameListConfig gameListConfig) {
      if (gameListConfig == null) {
          return;
      }

        BigDecimal cost = enumConfigCacheService
            .getValueBigDecimal(EnumConfigKey.GAME_BROADCAST_AMOUNT, gameListConfig.getSysOrigin()) ;
        if (cost == null || cost.compareTo(BigDecimal.ZERO) <= 0) {
          cost = BigDecimal.valueOf(5000L);
        }

        if (request.getCurrencyDiff() >= cost.longValue()) {
          UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
              userProfileGateway.getByUserId(DataTypeUtils.toLong(request.getUserId())));

            Map<Object, Object> build = OfficialNoticeUtils.buildUserProfile(userProfile);
            build.put("gameUrl", gameListConfig.getCover());
            build.put("currencyDiff", request.getCurrencyDiff());

            imGroupClient.sendMessageBroadcast(
                BroadcastGroupMsgBodyCmd.builder()
                    .toPlatform(SysOriginPlatformEnum.LIKEI)
                    .type(GroupMessageTypeEnum.GAME_BAISHUN_WIN)
                    .data(build)
                    .build()
            );
        }
    }

    private void incGameRankingRecord(BaishunChangeCurrencyRequest request, GameListConfig gameListConfig) {
        if (gameListConfig == null || request.getCurrencyDiff() <= 0) {
              return;
        }

        GameRankingIncrementCmd build = GameRankingIncrementCmd.builder()
                .gameOrigin(GameOriginEnum.BAISHUN.name())
                .gameId(gameListConfig.getGameId())
                .gameName(gameListConfig.getName())
                .gameCover(gameListConfig.getCover())
                .gameUrl(gameListConfig.getGameCode())
                .sysOrigin(gameListConfig.getSysOrigin())
                .amount(request.getCurrencyDiff())
                .periodType("ALL")
                .build();
        gameRankingGateway.incrementPrizeAmount(build);
    }

    private void incGameRankingActivity(BaishunChangeCurrencyRequest request) {
        if (request.getCurrencyDiff() > 0) {
            RankingDataUpdateCmd receiverCmd = new RankingDataUpdateCmd();
            receiverCmd.setUserId(DataTypeUtils.toLong(request.getUserId()));
            receiverCmd.setUserSex(1);
            receiverCmd.setActivityType(RankingActivityType.KING_GAMES.getCode());
            receiverCmd.setCycleType(RankingCycleType.WEEKLY.getCode());
            receiverCmd.setCycleKey(ZonedDateTimeAsiaRiyadhUtils.nowWeekMondayToInt().toString());
            receiverCmd.setDimension(RankingDimension.GAME_WIN.getCode());
            receiverCmd.setIncrementQuantity(request.getCurrencyDiff());
            receiverCmd.setBizNo(request.getGameRoundId());
            receiverCmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
            rankingActivityService.accumulateRankingData(receiverCmd);

          //游戏王日榜
          gameActivityService.incKingGameDaily(DataTypeUtils.toLong(request.getUserId()), request.getCurrencyDiff(), request.getGameId());
        }
    }

    private GoldReceiptCmd createReceipt(BaishunChangeCurrencyRequest param) {
    UserCredential userCredential = UserCredential.parseToken(param.getToken());
    return createGoldReceiptCmdBuilder(param.getCurrencyDiff())
        .userId(DataTypeUtils.toLong(param.getUserId()))
        .sysOrigin(userCredential.getSysOrigin())
        .origin(GoldOrigin.BAISHUN_GAME.name() + "_" + param.getGameId())
        .originDescribe(GoldOrigin.BAISHUN_GAME.getDesc() + "[" + param.getGameId() + "]")
        .eventId(param.getOrderId())
        .amount(toPositiveBigDecimal(param.getCurrencyDiff()))
        .build();
  }

  private GoldReceiptCmdBuilder createGoldReceiptCmdBuilder(Long currencyDiff) {
    return currencyDiff > 0
        ? GoldReceiptCmd.builder().appIncome()
        : GoldReceiptCmd.builder().appExpenditure();
  }

  private BigDecimal toPositiveBigDecimal(Long currencyDiff) {
    return new BigDecimal(currencyDiff.toString().replace("-", ""));
  }

  private BaishunResponse<BaishunTokenResponse> getToken(String userId) {
    UserCredential accessToken = ResponseAssert.requiredSuccess(
        authClient.getUserCredential(DataTypeUtils.toLong(userId))
    );

    if (Objects.isNull(accessToken)) {
      return BaishunResponse.fail(BaishunErrorCode.READ_DATE_ERROR);
    }

    return BaishunResponse.success(new BaishunTokenResponse()
        .setToken(accessToken.token())
        .setExpireDate(Objects.nonNull(accessToken.getExpireTime())
            ? accessToken.getExpireTime()
            : DateUtils.nowMinusHours(2).getTime()));
  }

  private boolean checkTokenStrict(BaishunUserProfileRequest request) {
    UserCredential accessToken = ResponseAssert.requiredSuccess(
        authClient.getUserCredential(DataTypeUtils.toLong(request.getUserId())));

    if (Objects.isNull(accessToken)) {
      return false;
    }

    if (!Objects.equals(accessToken.getUserId(), DataTypeUtils.toLong(request.getUserId()))) {
      return false;
    }

    return Objects.equals(accessToken.token(), request.getToken());
  }

}
