package com.sqx.modules.redisService.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sqx.common.utils.RedisKeys;
import com.sqx.common.utils.RedisUtils;
import com.sqx.modules.discSpinning.entity.DiscSpinningAmount;
import com.sqx.modules.discSpinning.service.DiscSpinningAmountService;
import com.sqx.modules.redisService.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import java.util.Map;

@Service
public class RedisServiceImpl implements RedisService {
    @Value("${limit.urlRate}")
    private Integer urlLimitRate;
    @Value("${limit.ipJumpLimit}")
    private Integer ipJumpLimit;

    @Lazy
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private DiscSpinningAmountService amountService;
    private final StringRedisTemplate redisTemplate;

    public RedisServiceImpl(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void setDiscSpinningAmounts(String key) {
        List<DiscSpinningAmount> amounts = amountService.list(new QueryWrapper<DiscSpinningAmount>().eq("status", 1).orderByAsc("max_amount"));
        Map<Integer, List<DiscSpinningAmount>> map =
                amounts.stream().collect(Collectors.groupingBy(
                        disc -> disc.getNum() == null ? 0 : disc.getNum()
                ));
        redisUtils.set(key, map);
    }

    @Override
    public void setFreeWatchTime(Long userId, Integer second, boolean isPermanently) {
        Date now = DateUtil.date();
        Date tomorrow = DateUtil.beginOfDay(DateUtil.offsetDay(now, 1));
        String freeWatchKey = RedisKeys.getFreeWatchKey(userId, isPermanently);
        if (isPermanently) {
            String data = redisUtils.get(freeWatchKey);
            if (StrUtil.isBlank(data)) {
                redisUtils.set(freeWatchKey, second, -1);
            }else {
                Long expire = redisUtils.getExpire(freeWatchKey);
                if (expire == null) {
                    expire = -1L;
                }else {
                    expire += Long.valueOf(second);
                }
                redisUtils.set(freeWatchKey, second + Long.parseLong(data), expire);

            }
        }else {
            long expire = DateUtil.between(now, tomorrow, DateUnit.SECOND);
//            redisUtils.setIfAbsent(freeWatchKey, DateUtil.offsetSecond(now, second).getTime(), seconds);
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("expireTime", -1);
            jsonObject.put("second", second);
            redisUtils.setIfAbsent(freeWatchKey, jsonObject.toJSONString(), expire);
        }
    }


    @Override
    public Boolean getFreeWatchTimeIsExpire(Long userId) {
        String freeWatchKey = RedisKeys.getFreeWatchKey(userId, true);
        String permanentlyFreeWatch = redisUtils.get(freeWatchKey);

        String watchKey = RedisKeys.getFreeWatchKey(userId, false);
        String payFreeWatchInfo = redisUtils.get(watchKey);
        Long expireTime = -1L;
        JSONObject jsonObject = null;
        if (StrUtil.isNotBlank(payFreeWatchInfo)) {
            jsonObject = JSONObject.parseObject(payFreeWatchInfo);
            expireTime = jsonObject.getLong("expireTime");
        }

        if ((StrUtil.isNotBlank(permanentlyFreeWatch) && redisUtils.isExpiredSet(freeWatchKey)) || (StrUtil.isNotBlank(permanentlyFreeWatch) && DateUtil.current(false) >= expireTime)) {
            if (StrUtil.isBlank(permanentlyFreeWatch)) {
                return null;
            }

            if (!redisUtils.isExpiredSet(freeWatchKey)) {
                redisUtils.expire(freeWatchKey, Long.parseLong(permanentlyFreeWatch));
            }
            return false;
        }else {
            if (StrUtil.isBlank(payFreeWatchInfo)) {
                return null;
            }

            Integer second = jsonObject.getInteger("second");
            if (expireTime == -1) {
                DateTime now = DateUtil.date();
                jsonObject.put("expireTime",  DateUtil.offsetSecond(now, second).getTime());
                Date tomorrow = DateUtil.beginOfDay(DateUtil.offsetDay(now, 1));
                long expire = DateUtil.between(now, tomorrow, DateUnit.SECOND);

                redisUtils.set(watchKey, jsonObject.toJSONString(), expire);
                return false;
            }else {
                return DateUtil.current(false) > expireTime;
            }
        }
    }

    @Override
    public Long getFreeWatchRemainTime(Long userId, boolean isPermanently) {
        String key = RedisKeys.getFreeWatchKey(userId, isPermanently);
        if (isPermanently) {
            String permanentlyFreeInfo = redisUtils.get(key);
            if (StrUtil.isBlank(permanentlyFreeInfo)) {
                return 0L;
            }

            Long expire = redisUtils.getExpire(key);
            return expire == null ? Long.valueOf(permanentlyFreeInfo) : expire;
        }


        String payFreeInfo = redisUtils.get(RedisKeys.getFreeWatchKey(userId, false));
        if (StrUtil.isBlank(payFreeInfo)) {
            return 0L;
        }

        JSONObject jsonObject = JSONObject.parseObject(payFreeInfo);
        Integer expireTime = jsonObject.getInteger("expireTime");
        Long second = jsonObject.getLong("second");

        return expireTime == -1 ? second : expireTime > DateUtil.current(false) ? expireTime - DateUtil.current(false) : 0L;
    }


    public boolean checkIpJumpLimit(long userId, String ip) {
        String userKey = "user:" + userId + ":last_ip"; // 存储用户上次的 IP 地址
        String lastIp = redisTemplate.opsForValue().get(userKey);

        // 获取用户跳动的历史 IP 地址集合
        String jumpHistoryKey = "user:" + userId + ":ip_history"; // 记录用户跳动过的 IP 地址
        Set<String> jumpHistory = redisTemplate.opsForSet().members(jumpHistoryKey);

        // 记录 IP 跳动次数
        String ipTimestampKey = "user:" + userId + ":ip_timestamp"; // 记录每个 IP 跳动的过期时间
        String userJumpCountKey = "user:" + userId + ":jump_count"; // 用户跳动的总次数

        // 新增用于记录所有 IP 请求时间和次数的 Hash
        String ipRequestsKey = "user:" + userId + ":ip_requests"; // 用来记录 IP 的请求次数和时间


        // 如果用户之前没有记录过 IP，说明是第一次请求，直接保存
        if (lastIp == null) {
            redisTemplate.opsForValue().set(userKey, ip);

            // 将当前 IP 添加到跳动历史中，设置 60 秒过期时间
            redisTemplate.opsForSet().add(jumpHistoryKey, ip);
            redisTemplate.expire(jumpHistoryKey, 1, TimeUnit.MINUTES);

            // 设置该 IP 的过期时间为 60 秒，表示跳动记录会在 60 秒后过期
            redisTemplate.opsForValue().set(ipTimestampKey + ":" + ip, "1", 1, TimeUnit.MINUTES);

            // 记录用户 IP 请求的次数和时间
            String ipData = DateUtil.date() + "|1"; // 时间和请求次数
            redisTemplate.opsForHash().put(ipRequestsKey, ip, ipData);

            return false;
        }

        // 如果当前 IP 和上次 IP 不一致，说明发生了 IP 跳动
        if (!lastIp.equals(ip)) {
            // 判断当前 IP 是否在历史跳动的 IP 中
            if (jumpHistory != null && jumpHistory.contains(ip)) {
                // 如果当前 IP 存在于历史跳动记录中，认为是多节点登录，增加跳动次数
                String s = redisTemplate.opsForValue().get(userJumpCountKey);
                Integer currentJumpCount = s == null ? null : Integer.parseInt(s);
                if (currentJumpCount == null) {
                    currentJumpCount = 0;
                }
                currentJumpCount++;

                // 更新跳动次数
                redisTemplate.opsForValue().set(userJumpCountKey, String.valueOf(currentJumpCount));

                // 如果跳动次数超过限制，触发封禁
                if (currentJumpCount >= ipJumpLimit) {
                    return true;
                }
            }

            // 检查用户是否跳动过多次
            if (jumpHistory != null && jumpHistory.size() >= ipJumpLimit) {
                // 如果历史 IP 地址数超过最大跳动次数，触发封禁
                return true;
            }

            // 将当前 IP 添加到跳动历史中，设置 60 秒过期时间
            redisTemplate.opsForSet().add(jumpHistoryKey, ip);
            redisTemplate.expire(jumpHistoryKey, 1, TimeUnit.MINUTES);

            // 更新用户的上次 IP 地址
            redisTemplate.opsForValue().set(userKey, ip);

            // 增加该 IP 的跳动计数，并设置过期时间为 60 秒
            redisTemplate.opsForValue().increment(ipTimestampKey + ":" + ip, 1);
            redisTemplate.expire(ipTimestampKey + ":" + ip, 1, TimeUnit.MINUTES);

            // 记录该 IP 请求的次数和时间
            String ipData = DateUtil.date() + "|1"; // 时间和请求次数
            redisTemplate.opsForHash().put(ipRequestsKey, ip, ipData);
        } else {
            // 如果 IP 没有变化，更新该 IP 的请求次数和时间
            String currentCount = (String) redisTemplate.opsForHash().get(ipRequestsKey, ip);
            String[] ipData = currentCount != null ? currentCount.split("\\|") : new String[]{"", "0"};
            int newCount = Integer.parseInt(ipData[1]) + 1; // 增加请求次数

            // 更新新的时间和请求次数
            String updatedIpData = DateUtil.date() + "|" + newCount;
            redisTemplate.opsForHash().put(ipRequestsKey, ip, updatedIpData);
        }

        return false;
    }

    @Override
    public boolean isSetUserState(long userId) {
        String userKey = "user:" + userId + ":state"; // 存储用户上次的 IP 地址
        String key = redisUtils.get(userKey);
        if (StrUtil.isBlank(key)) {
            DateTime date = DateUtil.date();
            Date tomorrow = DateUtil.beginOfDay(DateUtil.offsetDay(date, 1));
            long expire = DateUtil.between(date, tomorrow, DateUnit.SECOND);
            redisUtils.set(userKey, userId, expire);
            return false;
        }
        return true;
    }

    // 记录用户访问 URL 的次数和 IP 地址
    public void recordUrlVisitCountWithIp(long userId, String url, String ip) {
        String userUrlKey = "user:" + userId + ":url:" + url; // 存储用户访问特定 URL 的数据

        // 获取用户访问该 URL 的访问记录
        Map<Object, Object> urlAccessInfo = redisTemplate.opsForHash().entries(userUrlKey);

        // 记录访问次数的键名和每个 IP 地址的计数
        String ipKey = "ip:" + ip;
        int currentIpVisitCount = urlAccessInfo.containsKey(ipKey) ? Integer.parseInt(urlAccessInfo.get(ipKey).toString()) : 0;

        // 更新该 IP 的访问计数
        redisTemplate.opsForHash().put(userUrlKey, ipKey, String.valueOf(currentIpVisitCount + 1));

        // 更新总的访问次数（不区分 IP）
        int totalVisitCount = urlAccessInfo.containsKey("total") ? Integer.parseInt(urlAccessInfo.get("total").toString()) : 0;
        redisTemplate.opsForHash().put(userUrlKey, "total", String.valueOf(totalVisitCount + 1));
    }

    @Override
    public boolean isRecordUserOnLineTime(long userId) {
        // Redis 键值为 "user:{userId}:last_call_time"，存储的是用户最后一次调用接口的时间戳
        String lastCallTimeKey = "user:" + userId + ":last_call_time";

        // 获取 Redis 中存储的上次更新时间
        String lastCallTimeStr = redisTemplate.opsForValue().get(lastCallTimeKey);
        // 如果 Redis 中没有记录过，表示这是用户第一次调用接口
        if (lastCallTimeStr == null) {
            // 更新 Redis 中记录的最后调用时间
            redisTemplate.opsForValue().set(lastCallTimeKey, String.valueOf(DateUtil.date().getTime()), 10, TimeUnit.MINUTES);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public int getCourseWeekViewCount(long courseId) {
        String key = "course:viewCount:" + courseId;
        String info = redisUtils.get(key);
        if (StrUtil.isBlank(info)) {
            // 获取当前时间
            DateTime now = DateUtil.date();
            // 获取下周一的日期
            DateTime nextMonday = DateUtil.nextWeek();
            // 计算距离下周一还有多少秒
            long seconds = nextMonday.getTime() - now.getTime();
            redisUtils.set(key, 1, seconds);
            return 1;
        }
        int i = Integer.parseInt(info) + 1;
        redisUtils.set(key, i, -1);
        return i;
    }

    @Override
    public int getCourseWeekPayCount(long courseId) {
        String key = "course:payCount:" + courseId;
        String info = redisUtils.get(key);
        if (StrUtil.isBlank(info)) {
            // 获取当前时间
            DateTime now = DateUtil.date();
            // 获取下周一的日期
            DateTime nextMonday = DateUtil.nextWeek();
            // 计算距离下周一还有多少秒
            long seconds = nextMonday.getTime() - now.getTime();
            redisUtils.set(key, 1, seconds);
            return 1;
        }
        int i = Integer.parseInt(info) + 1;
        redisUtils.set(key, i, -1);
        return i;
    }
}
