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

import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
import com.red.circle.component.game.hotgame.request.HotGameUserCoinUpdate;
import com.red.circle.component.game.hotgame.request.HotGameUserProfileQuery;
import com.red.circle.component.game.hotgame.response.HotGameCoin;
import com.red.circle.component.game.hotgame.response.HotGameErrorEnum;
import com.red.circle.component.game.hotgame.response.HotGameResponse;
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.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.other.app.convertor.user.UserProfileAppConvertor;
import com.red.circle.other.app.dto.clientobject.game.HotGameUpdateRequest;
import com.red.circle.other.app.dto.cmd.game.GameLxwlUpdateBalanceCmd;
import com.red.circle.other.app.enums.violation.GameOriginEnum;
import com.red.circle.other.app.service.game.override.domain.HotGameUserProfile;
import com.red.circle.other.app.service.game.override.service.HotGameService;
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.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.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.sequence.IdWorkerUtils;
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.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * hotgame 游戏服务 实现.
 *
 * @author pengliang on 2023/4/17
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class HotGameServiceImpl implements HotGameService {

    private final RedisService redisService;
    private final WalletGoldClient walletGoldClient;
    private final UserProfileGateway userProfileGateway;
    private final EnumConfigCacheService enumConfigCacheService;
    private final UserProfileAppConvertor userProfileAppConvertor;
    private final TaskMqMessage taskMqMessage;
    private final ImGroupClient imGroupClient;
    private final GameListConfigService gameListConfigService;
    private final GameRankingGateway gameRankingGateway;
    private final GameListCacheService gameListCacheService;

    @Override
    public HotGameResponse<HotGameUserProfile> getUserProfile(HotGameUserProfileQuery query) {

        if (!UserCredential.checkToken(query.getToken(), query.longUid())) {
            return new HotGameResponse<HotGameUserProfile>()
                    .setErrorCode(HotGameErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
        }

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

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

//        Integer vipLevel = getVipLevel(query.getUid());
        return new HotGameResponse<HotGameUserProfile>()
                .setErrorCode(HotGameErrorEnum.SUCCESS.getErrorCode())
                .setData(HotGameUserProfile.builder()
                        .vipLevel(0)
                        .uid(Objects.toString(userProfile.getId()))
                        .nickname(userProfile.getUserNickname())
                        .avatar(userProfile.getUserAvatar())
                        .coin(
                                ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
                                        .getDollarAmount().longValue()).build()
                );
    }

    public Integer getVipLevel(String userId) {
        final String REDIS_KEY_PREFIX = "HOTGAME:";
        final String REDIS_KEY_RATIO = REDIS_KEY_PREFIX + "RATIO";
        final String REDIS_KEY_VIP = REDIS_KEY_PREFIX + "VIP";
        try {
            // 1. 检查大盘返奖比
            Object ratioObj = redisService.redisTemplate().opsForValue().get(REDIS_KEY_RATIO);
            BigDecimal ratio = BigDecimal.ZERO;
            if (ratioObj != null) {
                ratio = new BigDecimal(ratioObj.toString());
            }
            if (ratio.compareTo(BigDecimal.valueOf(100)) > 0) {
                return 0;
            }

            // 2. 按VIP等级从高到低查询
            Object vip3Obj = redisService.redisTemplate().opsForHash().get(REDIS_KEY_VIP, "VIP3");
            if (vip3Obj != null) {
                List<String> vip3Users = JSON.parseArray(vip3Obj.toString(), String.class);
                if (vip3Users.contains(userId)) {
                    return 3;
                }
            }

            Object vip2Obj = redisService.redisTemplate().opsForHash().get(REDIS_KEY_VIP, "VIP2");
            if (vip2Obj != null) {
                List<String> vip2Users = JSON.parseArray(vip2Obj.toString(), String.class);
                if (vip2Users.contains(userId)) {
                    return 2;
                }
            }

            Object vip1Obj = redisService.redisTemplate().opsForHash().get(REDIS_KEY_VIP, "VIP1");
            if (vip1Obj != null) {
                List<String> vip1Users = JSON.parseArray(vip1Obj.toString(), String.class);
                if (vip1Users.contains(userId)) {
                    return 1;
                }
            }

            return 0;
        } catch (Exception e) {
            System.out.println("获取VIP等级异常: " + e.getMessage());
            return 0;
        }

    }


    @Override
    public HotGameResponse<HotGameCoin> updateUserCoin(HotGameUserCoinUpdate param) {
        long startTime = LocalDateTimeUtils.nowEpochMilli();
        Long userId = param.longUid();
        if (!UserCredential.checkToken(param.getToken(), userId)) {
            return new HotGameResponse<HotGameCoin>()
                    .setData(new HotGameCoin()
                            .setBalanceCoin(0L)
                            .setResponseId(IdWorkerUtils.getIdStr())
                    )
                    .setErrorCode(HotGameErrorEnum.AUTHENTICATION_FAILED.getErrorCode());
        }

        if (!redisService.setIfAbsent("GHOT:" + param.getOrderId(), 5,
                TimeUnit.MINUTES)) {
            log.warn("重复触发消息: {}", JacksonUtils.toJson(param));
            return new HotGameResponse<HotGameCoin>()
                    .setData(new HotGameCoin()
                            .setBalanceCoin(0L)
                            .setResponseId("0")
                    ).setErrorCode(406);
        }
        try {
            long walletStartTime = LocalDateTimeUtils.nowEpochMilli();

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

            long walletEndTime = LocalDateTimeUtils.nowEpochMilli() - walletStartTime;

            long totalTime = LocalDateTimeUtils.nowEpochMilli() - startTime;

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

            String sysOrigin = UserCredential.parseToken(param.getToken()).getSysOrigin();
            GameListConfig gameListConfig = gameListCacheService.getByGameIdWithCache(
                    param.getGameId(), 
                    GameOriginEnum.HOTGAME.name(), 
                    sysOrigin, 
                    () -> gameListConfigService.getByGameId(param.getGameId(), GameOriginEnum.HOTGAME.name(), sysOrigin)
            );
            sendBroadcast(param, gameListConfig);
            //累计游戏排行榜
            incGameRankingRecord(param, gameListConfig);
            return new HotGameResponse<HotGameCoin>()
                    .setData(new HotGameCoin()
                            .setBalanceCoin(res.getBalance().getDollarAmount().longValue())
                            .setResponseId(Objects.toString(res.getAssetRecordId()))
                    )
                    .setErrorCode(HotGameErrorEnum.SUCCESS.getErrorCode());

        } catch (ResponseException ex) {
            if (Objects.equals(ex.getErrorCode(), WalletErrorCode.INSUFFICIENT_BALANCE.getCode())) {
                return new HotGameResponse<HotGameCoin>()
                        .setErrorCode(HotGameErrorEnum.INSUFFICIENT_BALANCE.getErrorCode());
            }
        } catch (Exception ex) {
            log.error("ex:{}", Throwables.getStackTraceAsString(ex));
        }

        return new HotGameResponse<HotGameCoin>()
                .setErrorCode(HotGameErrorEnum.SERVER_ERROR.getErrorCode());
    }

    private void incGameRankingRecord(HotGameUserCoinUpdate request, GameListConfig gameListConfig) {
        if (request.getType() != 2 || gameListConfig == null || request.getCoin() <= 0) {
            return;
        }

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

    private void sendBroadcast(HotGameUserCoinUpdate 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.getCoin() >= cost.longValue()) {
            UserProfileDTO userProfile = userProfileAppConvertor.toUserProfileDTO(
                    userProfileGateway.getByUserId(DataTypeUtils.toLong(request.getUid())));

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

            imGroupClient.sendMessageBroadcast(
                    BroadcastGroupMsgBodyCmd.builder()
                            .toPlatform(SysOriginPlatformEnum.LIKEI)
                            .type(GroupMessageTypeEnum.GAME_BAISHUN_WIN)
                            .data(build)
                            .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);
    }

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

}
