package org.example.Utils;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;

/**
 * 缓存操作工具类
 * 说明：基于ConcurrentHashMap实现线程安全的缓存，支持滑动过期和绝对过期策略
 */
public class CacheUtil {
    private static final int DEFAULT_MAX_ENTRIES = 5000;
    private static volatile int maxEntries = DEFAULT_MAX_ENTRIES;
    private static final AtomicLong accessSequence = new AtomicLong();

    // 用于确保线程安全的锁对象
    private static final Object locker = new Object();

    // 缓存存储的核心Map，键为缓存键，值为封装了缓存内容和过期信息的对象
    private static final ConcurrentHashMap<String, CacheItem> cache = new ConcurrentHashMap<>();

    // 定时任务线程池，用于清理过期的缓存项
    private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    static {
        // 每5分钟执行一次缓存清理任务
        scheduler.scheduleAtFixedRate(CacheUtil::cleanExpiredItems, 5, 5, TimeUnit.MINUTES);

        // 注册JVM关闭钩子，确保程序退出时关闭线程池
        Runtime.getRuntime().addShutdownHook(new Thread(scheduler::shutdown));
    }

    /**
     * 缓存项内部类，封装缓存值和过期时间信息
     */
    private static class CacheItem {
        Object value;
        Long absoluteExpiration; // 绝对过期时间戳（毫秒），null表示无绝对过期
        Long slidingExpiration;  // 滑动过期时间（毫秒），null表示无滑动过期
        Long lastAccessTime;     // 最后访问时间戳（毫秒）
        long lastAccessOrder;

        CacheItem(Object value, Long absoluteExpiration, Long slidingExpiration) {
            this.value = value;
            this.absoluteExpiration = absoluteExpiration;
            this.slidingExpiration = slidingExpiration;
            this.lastAccessTime = System.currentTimeMillis();
            this.lastAccessOrder = accessSequence.incrementAndGet();
        }

        /**
         * 检查缓存项是否已过期
         */
        boolean isExpired() {
            long now = System.currentTimeMillis();

            // 检查绝对过期
            if (absoluteExpiration != null && now >= absoluteExpiration) {
                return true;
            }

            // 检查滑动过期
            if (slidingExpiration != null && (now - lastAccessTime) >= slidingExpiration) {
                return true;
            }

            return false;
        }

        /**
         * 更新最后访问时间（用于滑动过期策略）
         */
        void updateAccessTime() {
            this.lastAccessTime = System.currentTimeMillis();
            this.lastAccessOrder = accessSequence.incrementAndGet();
        }

        long effectiveExpirationTime() {
            long absolute = absoluteExpiration == null ? Long.MAX_VALUE : absoluteExpiration;
            long sliding = slidingExpiration == null ? Long.MAX_VALUE : lastAccessTime + slidingExpiration;
            return Math.min(absolute, sliding);
        }
    }

    /**
     * 从缓存中获取指定键的值
     *
     * @param key 缓存键
     * @return 缓存值，如果不存在或已过期则返回null
     */
    public static Object get(String key) {
        CacheItem item = cache.get(key);

        if (item == null || item.isExpired()) {
            if (item != null) {
                cache.remove(key); // 移除过期项
            }
            return null;
        }

        // 如果有滑动过期策略，更新访问时间
        item.updateAccessTime();

        return item.value;
    }

   //新增一个get方法
   public static Object get(String key,Class<?> type){
        CacheItem item = cache.get(key);
       if (item == null || item.isExpired()) {
           if (item != null) {
               cache.remove(key);
           }
           return null;
       }
       item.updateAccessTime();
       return item.value;
   }

    /**
     * 向缓存中设置值，使用Duration表示滑动过期时间
     *
     * @param key                缓存键
     * @param val                缓存值
     * @param slidingExpiration  滑动过期时间（Duration类型，null表示不使用滑动过期）
     * @param absoluteExpiration 绝对过期时间（Date类型，null表示不使用绝对过期）
     */
    public static void set(String key, Object val, Duration slidingExpiration, Date absoluteExpiration) {
        // 校验参数合法性（键为空或值为null时不设置缓存）
        if (val == null || key == null || key.trim().isEmpty()) {
            return;
        }

        // 处理绝对过期时间：转换为毫秒时间戳（null表示无绝对过期）
        Long absoluteExpirationMillis = null;
        if (absoluteExpiration != null) {
            absoluteExpirationMillis = absoluteExpiration.getTime();
        }

        // 处理滑动过期时间：转换为毫秒（null表示无滑动过期）
        Long slidingExpirationMillis = null;
        if (slidingExpiration != null) {
            // 确保滑动过期时间为正数（避免无效值）
            slidingExpirationMillis = slidingExpiration.isNegative() ? null : slidingExpiration.toMillis();
        }

        // 存入缓存
        synchronized (locker) {
            cleanExpiredItems();
            cache.put(key, new CacheItem(val, absoluteExpirationMillis, slidingExpirationMillis));
            enforceMaxEntries();
        }
    }

    /**
     * 获取缓存项，如果不存在则通过getCacheValue函数获取并缓存
     *
     * @param key                缓存键
     * @param getCacheValue      获取缓存值的函数
     * @param slidingExpiration  滑动过期时间（Duration类型，null表示不使用滑动过期）
     * @param absoluteExpiration 绝对过期时间（Date类型，null表示不使用绝对过期）
     * @param enable             是否启用缓存
     * @return 缓存值
     */
    public static Object getCacheItem(String key, Function<Void, Object> getCacheValue,
                                      Duration slidingExpiration, Date absoluteExpiration, Boolean enable) {
        // 校验获取值的函数不能为null
        Objects.requireNonNull(getCacheValue, "getCacheValue cannot be null");

        // 检查是否启用缓存
        boolean isCacheEnabled = determineCacheEnabled(enable);

        // 不启用缓存或键为空，直接调用函数获取值
        if (!isCacheEnabled || isInvalidKey(key)) {
            return getCacheValue.apply(null);
        }

        // 尝试从缓存获取
        Object val = get(key);
        if (val == null) {
            // 双重检查锁定，确保线程安全
            synchronized (locker) {
                val = get(key);
                if (val == null) {
                    val = getCacheValue.apply(null);
                    // 调用优化后的set方法，传入Duration参数
                    set(key, val, slidingExpiration, absoluteExpiration);
                }
            }
        }

        return val;
    }

    /**
     * 判定缓存是否启用
     */
    private static boolean determineCacheEnabled(Boolean enable) {
        if (enable != null) {
            return enable;
        }
        // 从配置获取缓存开关状态，默认不启用
        String cacheConfig = WebConfigUtil.get("Cache", "false");
        return "true".equalsIgnoreCase(cacheConfig);
    }

    /**
     * 判定键是否无效（null或空字符串）
     */
    private static boolean isInvalidKey(String key) {
        return key == null || key.trim().isEmpty();
    }

    /**
     * 移除指定键的缓存
     *
     * @param key 缓存键
     */
    public static void remove(String key) {
        if (key != null) {
            cache.remove(key);
        }
    }

    /**
     * 清空所有缓存
     */
    public static void clearCache() {
        cache.clear();
    }

    public static void setMaxEntries(int configuredMaxEntries) {
        synchronized (locker) {
            maxEntries = Math.max(1, configuredMaxEntries);
            enforceMaxEntries();
        }
    }

    public static void setMaxEntriesForTests(int configuredMaxEntries) {
        setMaxEntries(configuredMaxEntries);
    }

    public static int sizeForTests() {
        cleanExpiredItems();
        return cache.size();
    }

    public static void resetForTests() {
        synchronized (locker) {
            cache.clear();
            maxEntries = DEFAULT_MAX_ENTRIES;
            accessSequence.set(0);
        }
    }

    /**
     * 清理指定用户的缓存
     *
     * @param username 用户名
     * @param userId   用户ID
     */
    public static void clearUserCache(String username, String userId) {
        List<String> keysToRemove = new ArrayList<>();

        // 收集包含用户ID和用户名的缓存键
        for (String key : cache.keySet()) {
            if (key.contains(userId) && key.contains(username)) {
                keysToRemove.add(key);
            }
        }

        // 移除匹配的缓存项
        for (String key : keysToRemove) {
            cache.remove(key);
        }
    }

    /**
     * 获取符合过滤条件的缓存项
     *
     * @param filter 过滤函数
     * @return 符合条件的缓存项列表
     */
    public static List<Map.Entry<String, Object>> getCaches(Function<Map.Entry<String, Object>, Boolean> filter) {
        List<Map.Entry<String, Object>> result = new ArrayList<>();

        // 转换为包含实际值的Entry列表
        for (Map.Entry<String, CacheItem> entry : cache.entrySet()) {
            // 跳过过期项
            if (entry.getValue().isExpired()) {
                continue;
            }

            Map.Entry<String, Object> valueEntry = new AbstractMap.SimpleEntry<>(
                    entry.getKey(), entry.getValue().value
            );

            // 应用过滤条件
            if (filter == null || filter.apply(valueEntry)) {
                result.add(valueEntry);
            }
        }

        return result;
    }

    /**
     * 清理所有过期的缓存项
     */
    private static void cleanExpiredItems() {
        List<String> expiredKeys = new ArrayList<>();

        // 收集过期的缓存键
        for (Map.Entry<String, CacheItem> entry : cache.entrySet()) {
            if (entry.getValue().isExpired()) {
                expiredKeys.add(entry.getKey());
            }
        }

        // 移除过期项
        for (String key : expiredKeys) {
            cache.remove(key);
        }
    }

    private static void enforceMaxEntries() {
        while (cache.size() > maxEntries) {
            cache.entrySet().stream()
                    .min(Comparator
                            .comparingLong((Map.Entry<String, CacheItem> entry) -> entry.getValue().effectiveExpirationTime())
                            .thenComparingLong(entry -> entry.getValue().lastAccessOrder))
                    .ifPresent(entry -> cache.remove(entry.getKey()));
        }
    }
}


//package org.example.Utils;
//
//import com.fasterxml.jackson.core.JsonProcessingException;
//import com.fasterxml.jackson.databind.MapperFeature;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.stereotype.Component;
//
//import jakarta.annotation.PostConstruct;
//import jakarta.annotation.Resource;
//import java.time.Duration;
//import java.util.*;
//import java.util.concurrent.TimeUnit;
//import java.util.function.Function;
//
///**
// * 缓存操作工具类（基于 Redis 实现，完全匹配原本地缓存行为，无配置键残留）
// * 说明：支持滑动过期、绝对过期策略，线程安全；缓存数据存储于 Redis，重启项目不丢失，清理时无残留
// */
//@Component
//public class CacheUtil {
//    /**
//     * Redis 客户端（Spring Boot 自动配置注入，用于操作 Redis 缓存）
//     */
//    @Resource
//    private StringRedisTemplate stringRedisTemplate;
//
//    /**
//     * Jackson 序列化工具（用于 Java 对象与 JSON 字符串的相互转换）
//     */
//    public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
//    // 静态代码块初始化配置（如果没有，新增一个）
//    static {
//        // 🔥 核心配置：反序列化时忽略字段大小写
//        OBJECT_MAPPER.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
//
//        // 保留你原有的其他配置（如日期格式化等，如果有的话）
//        // OBJECT_MAPPER.setDateFormat(...);
//    }
//
//    /**
//     * 静态持有者（解决静态工具类注入 Spring Bean 的问题）
//     */
//    private static CacheUtil instance;
//
//    /**
//     * 初始化静态持有者
//     */
//    @PostConstruct
//    public void init() {
//        instance = this;
//        instance.stringRedisTemplate = this.stringRedisTemplate;
//    }
//
//    /**
//     * 用于确保线程安全的锁对象
//     */
//    private static final Object locker = new Object();
//
//    /**
//     * 从缓存中获取指定键的值
//     *
//     * @param key 缓存键（非空、非空字符串）
//     * @return 缓存值（若不存在或已过期则返回 null）
//     */
//    public static Object get(String key) {
//        if (isInvalidKey(key)) {
//            return null;
//        }
//        String redisKey = getRedisKey(key);
//
//        // 1. 从 Redis 获取 JSON 字符串
//        String jsonValue = instance.stringRedisTemplate.opsForValue().get(redisKey);
//        if (jsonValue == null) {
//            return null;
//        }
//
//        // 2. 处理滑动过期：重置 Redis 键的过期时间（若存在滑动过期配置）
//        Long ttl = instance.stringRedisTemplate.getExpire(redisKey, TimeUnit.MILLISECONDS);
//        if (ttl != null && ttl > 0) {
//            instance.stringRedisTemplate.expire(redisKey, ttl, TimeUnit.MILLISECONDS);
//        }
//
//        // 3. JSON 反序列化为 Java 对象
//        try {
//            return OBJECT_MAPPER.readValue(jsonValue, Object.class);
//        } catch (JsonProcessingException e) {
//            remove(key); // 反序列化失败时移除无效缓存
//            return null;
//        }
//    }
//
//    /**
//     * 向缓存中设置值，支持滑动过期和绝对过期策略
//     *
//     * @param key                缓存键（非空、非空字符串）
//     * @param val                缓存值（非 null）
//     * @param slidingExpiration  滑动过期时间（Duration 类型，null 表示不使用滑动过期；访问时会重置过期时间）
//     * @param absoluteExpiration 绝对过期时间（Date 类型，null 表示不使用绝对过期；到点强制过期）
//     */
//    public static void set(String key, Object val, Duration slidingExpiration, Date absoluteExpiration) {
//        if (val == null || isInvalidKey(key)) {
//            return;
//        }
//        String redisKey = getRedisKey(key);
//
//        try {
//            String jsonValue = OBJECT_MAPPER.writeValueAsString(val);
//
//            // 优先处理绝对过期
//            if (absoluteExpiration != null) {
//                long absoluteExpireMillis = absoluteExpiration.getTime() - System.currentTimeMillis();
//                if (absoluteExpireMillis > 0) {
//                    instance.stringRedisTemplate.opsForValue().set(redisKey, jsonValue, absoluteExpireMillis, TimeUnit.MILLISECONDS);
//                    return;
//                }
//            }
//
//            // 处理滑动过期
//            if (slidingExpiration != null && !slidingExpiration.isNegative()) {
//                long slidingExpireMillis = slidingExpiration.toMillis();
//                instance.stringRedisTemplate.opsForValue().set(redisKey, jsonValue, slidingExpireMillis, TimeUnit.MILLISECONDS);
//                return;
//            }
//
//            // 无过期策略：永久存储
//            instance.stringRedisTemplate.opsForValue().set(redisKey, jsonValue);
//
//        } catch (JsonProcessingException e) {
//            // 序列化失败时不存储缓存
//        }
//    }
//
//    /**
//     * 获取缓存项，若不存在则通过函数获取并缓存（支持缓存穿透处理）
//     *
//     * @param key                缓存键（非空、非空字符串）
//     * @param getCacheValue      获取缓存值的函数（不可为 null）
//     * @param slidingExpiration  滑动过期时间（Duration 类型，null 表示不使用滑动过期）
//     * @param absoluteExpiration 绝对过期时间（Date 类型，null 表示不使用绝对过期）
//     * @param enable             是否启用缓存（null 则从配置读取，默认不启用）
//     * @return 缓存值（若缓存未命中则调用函数获取并缓存）
//     */
//    public static Object getCacheItem(String key, Function<Void, Object> getCacheValue,
//                                      Duration slidingExpiration, Date absoluteExpiration, Boolean enable) {
//        Objects.requireNonNull(getCacheValue, "getCacheValue 函数不可为 null");
//        boolean isCacheEnabled = determineCacheEnabled(enable);
//
//        if (!isCacheEnabled || isInvalidKey(key)) {
//            return getCacheValue.apply(null);
//        }
//
//        Object val = get(key);
//        if (val == null) {
//            synchronized (locker) {
//                val = get(key);
//                if (val == null) {
//                    val = getCacheValue.apply(null);
//                    set(key, val, slidingExpiration, absoluteExpiration);
//                }
//            }
//        }
//        return val;
//    }
//
//    /**
//     * 移除指定键的缓存
//     *
//     * @param key 缓存键（可 null，null 时无操作）
//     */
//    public static void remove(String key) {
//        if (key == null) {
//            return;
//        }
//        instance.stringRedisTemplate.delete(getRedisKey(key));
//    }
//
//    /**
//     * 清空所有缓存（所有以 "cache:" 为前缀的 Redis 键）
//     */
//    public static void clearCache() {
//        Set<String> cacheKeys = instance.stringRedisTemplate.keys("cache:*");
//        if (cacheKeys != null && !cacheKeys.isEmpty()) {
//            instance.stringRedisTemplate.delete(cacheKeys);
//        }
//    }
//
//    /**
//     * 清理指定用户的缓存（匹配包含用户名和用户 ID 的缓存键）
//     *
//     * @param username 用户名（可 null）
//     * @param userId   用户 ID（可 null）
//     */
//    public static void clearUserCache(String username, String userId) {
//        if (username == null && userId == null) {
//            return;
//        }
//
//        String pattern = "cache:*";
//        if (username != null && userId != null) {
//            pattern = "cache:*" + userId + "*" + username + "*";
//        } else if (userId != null) {
//            pattern = "cache:*" + userId + "*";
//        } else if (username != null) {
//            pattern = "cache:*" + username + "*";
//        }
//
//        Set<String> userCacheKeys = instance.stringRedisTemplate.keys(pattern);
//        if (userCacheKeys != null && !userCacheKeys.isEmpty()) {
//            instance.stringRedisTemplate.delete(userCacheKeys);
//        }
//    }
//
//    /**
//     * 获取符合过滤条件的缓存项列表
//     *
//     * @param filter 过滤函数（可 null，null 时返回所有有效缓存项）
//     * @return 符合条件的缓存项列表（键为原始缓存键，值为缓存对象）
//     */
//    public static List<Map.Entry<String, Object>> getCaches(Function<Map.Entry<String, Object>, Boolean> filter) {
//        List<Map.Entry<String, Object>> result = new ArrayList<>();
//        Set<String> cacheKeys = instance.stringRedisTemplate.keys("cache:*");
//        if (cacheKeys == null || cacheKeys.isEmpty()) {
//            return result;
//        }
//
//        for (String key : cacheKeys) {
//            String jsonValue = instance.stringRedisTemplate.opsForValue().get(key);
//            if (jsonValue == null) {
//                continue;
//            }
//
//            try {
//                Object value = OBJECT_MAPPER.readValue(jsonValue, Object.class);
//                String originalKey = key.replace("cache:", "");
//                Map.Entry<String, Object> entry = new AbstractMap.SimpleEntry<>(originalKey, value);
//
//                if (filter == null || filter.apply(entry)) {
//                    result.add(entry);
//                }
//            } catch (JsonProcessingException e) {
//                continue;
//            }
//        }
//        return result;
//    }
//
//    // ------------------------------ 内部工具方法 ------------------------------
//
//    /**
//     * 生成带前缀的 Redis 键（避免与其他 Redis 数据冲突）
//     *
//     * @param originalKey 原始缓存键
//     * @return 带 "cache:" 前缀的 Redis 键
//     */
//    private static String getRedisKey(String originalKey) {
//        return "cache:" + originalKey;
//    }
//
//    /**
//     * 判定缓存是否启用（优先取入参，否则从配置读取）
//     *
//     * @param enable 入参启用标识（可 null）
//     * @return true 表示启用缓存，false 表示禁用
//     */
//    private static boolean determineCacheEnabled(Boolean enable) {
//        if (enable != null) {
//            return enable;
//        }
//        String cacheConfig = WebConfigUtil.get("Cache", "false");
//        return "true".equalsIgnoreCase(cacheConfig);
//    }
//
//    /**
//     * 判定键是否无效（null 或空字符串）
//     *
//     * @param key 缓存键
//     * @return true 表示键无效，false 表示键有效
//     */
//    private static boolean isInvalidKey(String key) {
//        return key == null || key.trim().isEmpty();
//    }
//}
