package com.ycwl.basic.util;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function;

/**
 * 带TTL（生存时间）的缓存Map工具类
 * 
 * @param <K> 键类型
 * @param <V> 值类型
 */
@Slf4j
public class TtlCacheMap<K, V> {
    
    /**
     * 缓存项包装类
     */
    private static class CacheItem<V> {
        private final V value;
        private final long expireTime;
        
        public CacheItem(V value, long ttlMillis) {
            this.value = value;
            this.expireTime = System.currentTimeMillis() + ttlMillis;
        }
        
        public V getValue() {
            return value;
        }
        
        public boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
        
        public long getRemainTtl() {
            return Math.max(0, expireTime - System.currentTimeMillis());
        }
    }
    
    private final ConcurrentHashMap<K, CacheItem<V>> cache;
    private final long defaultTtlMillis;
    private final ReentrantReadWriteLock lock;
    private final ScheduledExecutorService cleanupExecutor;
    
    // 统计信息
    private volatile long hitCount = 0;
    private volatile long missCount = 0;
    private volatile long expiredCount = 0;
    
    /**
     * 构造函数
     * 
     * @param defaultTtlMillis 默认TTL时间（毫秒）
     */
    public TtlCacheMap(long defaultTtlMillis) {
        this.cache = new ConcurrentHashMap<>();
        this.defaultTtlMillis = defaultTtlMillis;
        this.lock = new ReentrantReadWriteLock();
        this.cleanupExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
            Thread t = new Thread(r, "TtlCacheMap-Cleanup");
            t.setDaemon(true);
            return t;
        });
        
        // 启动定期清理任务，每分钟清理一次过期条目
        this.cleanupExecutor.scheduleWithFixedDelay(this::cleanupExpired, 60, 60, TimeUnit.SECONDS);
    }
    
    /**
     * 构造函数，使用默认TTL为10分钟
     */
    public TtlCacheMap() {
        this(TimeUnit.MINUTES.toMillis(10));
    }
    
    /**
     * 获取缓存值，如果不存在或过期则通过supplier创建
     * 
     * @param key 缓存键
     * @param valueSupplier 值提供器
     * @return 缓存值
     */
    public V computeIfAbsent(K key, Function<K, V> valueSupplier) {
        return computeIfAbsent(key, valueSupplier, defaultTtlMillis);
    }
    
    /**
     * 获取缓存值，如果不存在或过期则通过supplier创建
     * 
     * @param key 缓存键
     * @param valueSupplier 值提供器
     * @param ttlMillis TTL时间（毫秒）
     * @return 缓存值
     */
    public V computeIfAbsent(K key, Function<K, V> valueSupplier, long ttlMillis) {
        lock.readLock().lock();
        try {
            CacheItem<V> item = cache.get(key);
            if (item != null && !item.isExpired()) {
                hitCount++;
                return item.getValue();
            }
        } finally {
            lock.readLock().unlock();
        }
        
        // 缓存不存在或已过期，需要重新创建
        lock.writeLock().lock();
        try {
            // 双重检查，防止重复创建
            CacheItem<V> item = cache.get(key);
            if (item != null && !item.isExpired()) {
                hitCount++;
                return item.getValue();
            }
            
            if (item != null && item.isExpired()) {
                expiredCount++;
                cache.remove(key);
            }
            
            // 创建新值
            missCount++;
            V value = valueSupplier.apply(key);
            if (value != null) {
                cache.put(key, new CacheItem<>(value, ttlMillis));
            }
            return value;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 直接放入缓存
     * 
     * @param key 缓存键
     * @param value 缓存值
     */
    public void put(K key, V value) {
        put(key, value, defaultTtlMillis);
    }
    
    /**
     * 直接放入缓存
     * 
     * @param key 缓存键
     * @param value 缓存值
     * @param ttlMillis TTL时间（毫秒）
     */
    public void put(K key, V value, long ttlMillis) {
        lock.writeLock().lock();
        try {
            cache.put(key, new CacheItem<>(value, ttlMillis));
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 获取缓存值
     * 
     * @param key 缓存键
     * @return 缓存值，如果不存在或过期返回null
     */
    public V get(K key) {
        lock.readLock().lock();
        try {
            CacheItem<V> item = cache.get(key);
            if (item != null) {
                if (!item.isExpired()) {
                    hitCount++;
                    return item.getValue();
                } else {
                    // 异步清理过期项
                    cleanupExecutor.execute(() -> {
                        lock.writeLock().lock();
                        try {
                            CacheItem<V> expiredItem = cache.get(key);
                            if (expiredItem != null && expiredItem.isExpired()) {
                                cache.remove(key);
                                expiredCount++;
                            }
                        } finally {
                            lock.writeLock().unlock();
                        }
                    });
                }
            }
            missCount++;
            return null;
        } finally {
            lock.readLock().unlock();
        }
    }
    
    /**
     * 移除缓存项
     * 
     * @param key 缓存键
     * @return 被移除的值，如果不存在返回null
     */
    public V remove(K key) {
        lock.writeLock().lock();
        try {
            CacheItem<V> item = cache.remove(key);
            return item != null ? item.getValue() : null;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 清空所有缓存
     */
    public void clear() {
        lock.writeLock().lock();
        try {
            cache.clear();
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 检查缓存键是否存在且未过期
     * 
     * @param key 缓存键
     * @return true如果存在且未过期
     */
    public boolean containsKey(K key) {
        return get(key) != null;
    }
    
    /**
     * 获取缓存大小（包含过期项）
     * 
     * @return 缓存大小
     */
    public int size() {
        return cache.size();
    }
    
    /**
     * 检查缓存是否为空
     * 
     * @return true如果为空
     */
    public boolean isEmpty() {
        return cache.isEmpty();
    }
    
    /**
     * 手动触发过期清理
     * 
     * @return 清理的过期项数量
     */
    public int cleanupExpired() {
        lock.writeLock().lock();
        try {
            int cleanedCount = 0;
            var iterator = cache.entrySet().iterator();
            while (iterator.hasNext()) {
                var entry = iterator.next();
                if (entry.getValue().isExpired()) {
                    iterator.remove();
                    cleanedCount++;
                    expiredCount++;
                }
            }
            if (cleanedCount > 0) {
                log.debug("清理了 {} 个过期缓存项", cleanedCount);
            }
            return cleanedCount;
        } finally {
            lock.writeLock().unlock();
        }
    }
    
    /**
     * 获取缓存统计信息
     * 
     * @return 统计信息字符串
     */
    public String getStats() {
        long total = hitCount + missCount;
        double hitRate = total > 0 ? (double) hitCount / total * 100 : 0;
        
        return String.format(
            "TtlCacheMap Stats: size=%d, hits=%d, misses=%d, expired=%d, hitRate=%.2f%%",
            cache.size(), hitCount, missCount, expiredCount, hitRate
        );
    }
    
    /**
     * 重置统计信息
     */
    public void resetStats() {
        hitCount = 0;
        missCount = 0;
        expiredCount = 0;
    }
    
    /**
     * 获取剩余TTL时间
     * 
     * @param key 缓存键
     * @return 剩余TTL毫秒数，如果不存在或已过期返回0
     */
    public long getRemainTtl(K key) {
        lock.readLock().lock();
        try {
            CacheItem<V> item = cache.get(key);
            return item != null ? item.getRemainTtl() : 0;
        } finally {
            lock.readLock().unlock();
        }
    }
    
    /**
     * 关闭清理线程池，释放资源
     */
    public void shutdown() {
        cleanupExecutor.shutdown();
        try {
            if (!cleanupExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
                cleanupExecutor.shutdownNow();
            }
        } catch (InterruptedException e) {
            cleanupExecutor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}