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

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.dto.ReqSysOrigin;
import com.red.circle.framework.core.exception.ResponseException;
import com.red.circle.framework.core.security.UserCredential;
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.game.override.domain.HotGameUserProfile;
import com.red.circle.other.app.service.game.override.service.HotGameService;
import com.red.circle.other.app.service.task.RoomDailyTaskProgressService;
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.activity.UserActivityRechargeService;
import com.red.circle.other.infra.database.rds.dto.game.WeeklyVipInfo;
import com.red.circle.other.infra.database.rds.service.game.GameVipLevelWeeklyService;
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.game.VipLevelRule;
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.LocalDateTimeUtils;
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
import com.red.circle.tool.core.date.ZonedId;
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.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
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;

@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;
    private final RankingActivityService rankingActivityService;
    private final GameActivityService gameActivityService;
    private final UserActivityRechargeService userActivityRechargeService;
    private final GameVipLevelWeeklyService gameVipLevelWeeklyService;
    private final ActivityRechargeTicketService activityRechargeTicketService;
    private final RoomDailyTaskProgressService roomDailyTaskProgressService;
    
    @Value("${red-circle.game-rank.gameid}")
    private String gameId;
    @Value("${red-circle.game-rank.endTime}")
    private String endTime;

    private static final Long VIP_ACTIVITY_ID = 1235571533988298753L;
    private static final String VIP_LEVEL_CACHE_KEY = "GAME:VIP:LEVEL:";

    @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.longUid(), userProfile.getCountryCode());
        return new HotGameResponse<HotGameUserProfile>()
                .setErrorCode(HotGameErrorEnum.SUCCESS.getErrorCode())
                .setData(HotGameUserProfile.builder()
                        .vipLevel(vipLevel)
                        .uid(Objects.toString(userProfile.getId()))
                        .nickname(userProfile.getUserNickname())
                        .avatar(userProfile.getUserAvatar())
                        .coin(
                                ResponseAssert.requiredSuccess(walletGoldClient.getBalance(userProfile.getId()))
                                        .getDollarAmount().longValue()).build()
                );
    }

    public Integer getVipLevel(Long userId, String countryCode) {
        LocalDateTime now = LocalDateTime.now();
        // 巴基斯坦返回0 其他正常
        if ("PK".equals(countryCode)) {
            return 0;
        }

        // 【新增】检查是否为每月的 1-7号 或 16-22号
//        int dayOfMonth = now.getDayOfMonth();
//        if ((dayOfMonth >= 1 && dayOfMonth <= 7) || (dayOfMonth >= 16 && dayOfMonth <= 22)) {
//            return 0;
//        }

        try {
            String cacheKey = VIP_LEVEL_CACHE_KEY + userId;
            Object cachedLevel = redisService.redisTemplate().opsForValue().get(cacheKey);
            if (cachedLevel != null) {
                return Integer.parseInt(cachedLevel.toString());
            }

            LocalDate firstRechargeDate = userActivityRechargeService.getFirstRechargeDate(userId, VIP_ACTIVITY_ID);
            if (firstRechargeDate == null) {
                redisService.redisTemplate().opsForValue().set(cacheKey, 0, 1, TimeUnit.DAYS);
                return 0;
            }

            int[] weekDurations = VipLevelRule.calculateWeekDurations(userId, firstRechargeDate);
            int week1Days = weekDurations[0];
            int week2Days = weekDurations[1];
            int week3Days = weekDurations[2];

            LocalDate today = ZonedDateTimeAsiaRiyadhUtils.now().toLocalDate();
            long daysSinceFirst = ChronoUnit.DAYS.between(firstRechargeDate, today);
            long completedCycles = daysSinceFirst / 21;
            long dayInCycle = daysSinceFirst % 21;

            int normalizedWeek;
            int weekInCycle;
            LocalDate weekStartDate;
            LocalDate weekEndDate;

            if (dayInCycle < week1Days) {
                normalizedWeek = 1;
                weekInCycle = 1;
                weekStartDate = firstRechargeDate.plusDays(completedCycles * 21);
                weekEndDate = weekStartDate.plusDays(week1Days - 1);
            } else if (dayInCycle < week1Days + week2Days) {
                normalizedWeek = 2;
                weekInCycle = 2;
                weekStartDate = firstRechargeDate.plusDays(completedCycles * 21 + week1Days);
                weekEndDate = weekStartDate.plusDays(week2Days - 1);
            } else {
                normalizedWeek = 3;
                weekInCycle = 3;
                weekStartDate = firstRechargeDate.plusDays(completedCycles * 21 + week1Days + week2Days);
                weekEndDate = weekStartDate.plusDays(week3Days - 1);
            }

            LocalDate cycleStartDate = firstRechargeDate.plusDays(completedCycles * 21);
            LocalDate cycleEndDate = cycleStartDate.plusDays(20);

            WeeklyVipInfo lastWeekInfo = gameVipLevelWeeklyService.getLastWeekInfo(userId, VIP_ACTIVITY_ID, weekStartDate);

            BigDecimal lastWeekInherited = calculateLastWeekInherited(lastWeekInfo, weekInCycle, completedCycles);

            BigDecimal thisWeekRecharge = userActivityRechargeService.getRechargeAmountByDateRange(
                    userId, VIP_ACTIVITY_ID, weekStartDate, weekEndDate
            );

            int displayLevel = VipLevelRule.calculateVipLevelWithInheritance(
                    normalizedWeek, thisWeekRecharge, lastWeekInherited
            );
            // 应用保底逻辑
            int startLevel = calculateStartLevel(lastWeekInfo, weekInCycle, completedCycles);
            displayLevel = Math.max(displayLevel, startLevel);

            BigDecimal thisWeekInherited = thisWeekRecharge.add(lastWeekInherited);

            gameVipLevelWeeklyService.saveOrUpdateWeeklyLevel(
                    userId, VIP_ACTIVITY_ID, (int) (completedCycles * 3 + normalizedWeek),
                    weekStartDate, weekEndDate, displayLevel, thisWeekRecharge,
                    normalizedWeek, thisWeekInherited
            );

            redisService.redisTemplate().opsForValue().set(cacheKey, displayLevel, 1, TimeUnit.DAYS);

            return displayLevel;

        } catch (Exception e) {
            log.error("获取VIP等级异常, userId:{}, error:{}", userId, e.getMessage(), e);
            return 0;
        }

    }

    private BigDecimal calculateLastWeekInherited(WeeklyVipInfo lastWeekInfo, int weekInCycle, long completedCycles) {
        BigDecimal lastWeekInherited = Optional.ofNullable(lastWeekInfo)
                .map(WeeklyVipInfo::getInheritedAmount)
                .orElse(BigDecimal.ZERO);

        // 第二个周期及以后，第一周需要除3再除2
        if (completedCycles > 0 && weekInCycle == 1) {
            return lastWeekInherited
                    .divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP)
                    .divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
        }

        if (completedCycles > 0 && weekInCycle == 2) {
            BigDecimal lastCycleInherited = lastWeekInherited.subtract(Optional.ofNullable(lastWeekInfo).map(WeeklyVipInfo::getRechargeAmount).orElse(BigDecimal.ZERO)).max(BigDecimal.ZERO);
            return lastWeekInherited.subtract(lastCycleInherited).divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
        }

        // 原有逻辑：第二周除2，第三周除2再除2
        if (weekInCycle == 2) {
            return lastWeekInherited.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
        } else if (weekInCycle == 3) {
            return lastWeekInherited.divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP)
                    .divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
        }

        return lastWeekInherited;
    }

    private Integer calculateStartLevel(WeeklyVipInfo lastWeekInfo, int weekInCycle, long completedCycles) {
        // 只在新周期的第一周判断
        if (completedCycles > 0 && weekInCycle == 1 && lastWeekInfo != null) {
            // 检查上周期是否达到过V3
            if (lastWeekInfo.getFinalLevel() != null && lastWeekInfo.getFinalLevel() >= 3) {
                return 2; // 保底V2
            }
        }
        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)
            );
            // type = 1 支出 type = 2 收入

            sendBroadcast(param, gameListConfig);
            //累计游戏排行榜
            incGameRankingRecord(param, gameListConfig);
            //游戏参与打榜活动
            incGameRankingActivity(param);

            //FruitParty
            incGameRankingTmpActivity(param);

            //处理游戏获得任务
            if (param.getType() == 2) {
                gameActivityService.handleGameSpendTask(userId, param.getCoin());

                // 累加用户获得的金币并发送抽奖券
//                activityRechargeTicketService.accumulatedRechargeAndAddTicket(userId, Math.abs(param.getCoin()), MonthlyRechargeType.UNDEFINED);
            }

            // 处理游戏消费人任务
            else {
                updateRoomDailyTask(param.getCoin(), userId);
            }

            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 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 incGameRankingTmpActivity(HotGameUserCoinUpdate request) {
        if (ZonedDateTimeAsiaRiyadhUtils.now().isAfter(getFinishTime())) {
            return;
        }

        if (gameId != null && gameId.equals(request.getGameId()) && request.getType() == 2 && request.getCoin() > 0) {
            RankingDataUpdateCmd receiverCmd = new RankingDataUpdateCmd();
            receiverCmd.setUserId(DataTypeUtils.toLong(request.getUid()));
            receiverCmd.setUserSex(1);
            receiverCmd.setActivityType(RankingActivityType.FRUIT_PARTY.getCode());
            receiverCmd.setCycleType(RankingCycleType.WEEKLY.getCode());
            receiverCmd.setCycleKey("20260129");
            receiverCmd.setDimension(RankingDimension.GAME_WIN.getCode());
            receiverCmd.setIncrementQuantity(request.getCoin());
            receiverCmd.setBizNo(request.getGameId());
            receiverCmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
            rankingActivityService.accumulateRankingData(receiverCmd);
        }
    }

    private ZonedDateTime getFinishTime() {
        return ZonedDateTime.of(LocalDateTime.parse(endTime,
                DateTimeFormatter.ISO_LOCAL_DATE_TIME),
                ZonedId.ASIA_RIYADH.getZonedId());
    }

    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 incGameRankingActivity(HotGameUserCoinUpdate param) {
        if (param.getType() != null && param.getType() == 2 && param.getCoin() > 0) {
            RankingDataUpdateCmd receiverCmd = new RankingDataUpdateCmd();
            receiverCmd.setUserId(DataTypeUtils.toLong(param.getUid()));
            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(param.getCoin());
            receiverCmd.setBizNo(param.getGameId());
            receiverCmd.setReqSysOrigin(ReqSysOrigin.of("LIKEI"));
            rankingActivityService.accumulateRankingData(receiverCmd);

            //游戏王日榜
            gameActivityService.incKingGameDaily(DataTypeUtils.toLong(param.getUid()), param.getCoin(), param.getGameId());
        }
    }

    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.getType() != null && request.getType() == 2 && 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();
    }

}
