package com.CUST.brain.common.utils;

import com.CUST.brain.common.constant.RedisKeyConstants;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类
 */
@Slf4j
@Component
public class RedisUtils {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private static ObjectMapper objectMapper;

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        RedisUtils.objectMapper = objectMapper;
    }

    private static final String LOCK_SCRIPT = 
        "local expire = tonumber(ARGV[2]) " +
        "if not expire then redis.log(redis.LOG_WARNING, 'expire is nil or not a number: ' .. tostring(ARGV[2])) return 0 end " +
        "if expire <= 0 then redis.log(redis.LOG_WARNING, 'expire <= 0: ' .. tostring(expire)) return 0 end " +
        "local set_result = redis.call('set', KEYS[1], ARGV[1], 'NX', 'PX', expire) " +
        "redis.log(redis.LOG_DEBUG, 'set result: ' .. tostring(set_result)) " +
        "return set_result == 'OK' and 1 or 0";

    private static final String UNLOCK_SCRIPT = 
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "    return redis.call('del', KEYS[1]) " +
        "else " +
        "    return 0 " +
        "end";

    private static final String RENEW_SCRIPT = 
        "local expire = tonumber(ARGV[2]) " +
        "if not expire then redis.log(redis.LOG_WARNING, 'expire is nil or not a number: ' .. tostring(ARGV[2])) return 0 end " +
        "if expire <= 0 then redis.log(redis.LOG_WARNING, 'expire <= 0: ' .. tostring(expire)) return 0 end " +
        "if redis.call('get', KEYS[1]) == ARGV[1] then " +
        "    local pexpire_result = redis.call('pexpire', KEYS[1], expire) " +
        "    redis.log(redis.LOG_DEBUG, 'pexpire result: ' .. tostring(pexpire_result)) " +
        "    return pexpire_result == 1 and 1 or 0 " +
        "else " +
        "    return 0 " +
        "end";

    /**
     * 设置缓存（对象自动转为JSON字符串）
     * @param key 键
     * @param value 值
     * @param timeout 过期时间（秒）
     */
    public void set(String key, Object value, long timeout) {
        try {
            if (value instanceof String) {
                redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
            } else {
                String json = objectMapper.writeValueAsString(value);
                redisTemplate.opsForValue().set(key, json, timeout, TimeUnit.SECONDS);
            }
        } catch (JsonProcessingException e) {
            log.error("[RedisUtils] set序列化异常", e);
            throw new RuntimeException("Redis序列化异常", e);
        }
    }

    /**
     * 获取缓存并反序列化为指定类型（JSON字符串）
     */
    public <T> T get(String key, Class<T> clazz) {
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj == null) return null;
        if (clazz == String.class) {
            return clazz.cast(obj);
        }
        try {
            return objectMapper.readValue(obj.toString(), clazz);
        } catch (Exception e) {
            log.error("[RedisUtils] get反序列化异常", e);
            throw new RuntimeException("Redis反序列化异常", e);
        }
    }

    /**
     * 获取缓存（原始String）
     */
    public String get(String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        return obj == null ? null : obj.toString();
    }

    /**
     * 删除缓存
     *
     * @param key 键
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return 是否存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     *
     * @param key 键
     * @param timeout 过期时间（秒）
     * @return 是否成功
     */
    public Boolean expire(String key, long timeout) {
        return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 获取过期时间
     *
     * @param key 键
     * @return 过期时间（秒）
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 批量删除缓存
     */
    public Long delete(Collection<String> keys) {
        return redisTemplate.delete(keys);
    }

    /**
     * 递增
     */
    public Long increment(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     */
    public Long decrement(String key, long delta) {
        return redisTemplate.opsForValue().decrement(key, delta);
    }

    /**
     * 获取Hash中的属性并反序列化为指定类型
     */
    public <T> T hGet(String key, String hashKey, Class<T> clazz) {
        Object obj = redisTemplate.opsForHash().get(key, hashKey);
        return objectMapper.convertValue(obj, clazz);
    }

    /**
     * 向Hash中放入一个属性
     */
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * 向Hash中放入一个属性并设置过期时间
     */
    public void hSet(String key, String hashKey, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForHash().put(key, hashKey, value);
        redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 直接设置整个Hash
     */
    public void hSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 直接设置整个Hash并设置过期时间
     */
    public void hSetAll(String key, Map<String, Object> map, long timeout, TimeUnit unit) {
        redisTemplate.opsForHash().putAll(key, map);
        redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取整个Hash并反序列化为指定类型Map
     */
    public <T> Map<String, T> hGetAll(String key, Class<T> clazz) {
        Map<Object, Object> map = redisTemplate.opsForHash().entries(key);
        Map<String, T> result = new HashMap<>();
        for (Map.Entry<Object, Object> entry : map.entrySet()) {
            result.put(String.valueOf(entry.getKey()), objectMapper.convertValue(entry.getValue(), clazz));
        }
        return result;
    }

    /**
     * 删除Hash中的属性
     */
    public Long hDelete(String key, Object... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }

    /**
     * 判断Hash中是否存在该属性
     */
    public Boolean hHasKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * Hash递增
     */
    public Long hIncrement(String key, String hashKey, long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    /**
     * Hash递减
     */
    public Long hDecrement(String key, String hashKey, long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
    }

    /**
     * 获取Set中的所有值
     */
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 判断Set中是否存在value
     */
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 向Set中放入数据
     */
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * 向Set中放入数据并设置过期时间
     */
    public Long sAdd(String key, long timeout, TimeUnit unit, Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        redisTemplate.expire(key, timeout, unit);
        return count;
    }

    /**
     * 获取Set的长度
     */
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 移除Set中的值
     */
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 获取List缓存并反序列化为指定类型List
     */
    public <T> List<T> getList(String key, Class<T> clazz) {
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj instanceof List) {
            List<?> list = (List<?>) obj;
            List<T> result = new ArrayList<>();
            for (Object item : list) {
                result.add(objectMapper.convertValue(item, clazz));
            }
            return result;
        }
        return Collections.emptyList();
    }

    /**
     * 获取List中的值并反序列化为指定类型
     */
    public <T> List<T> lRange(String key, long start, long end, Class<T> clazz) {
        List<Object> list = redisTemplate.opsForList().range(key, start, end);
        List<T> result = new ArrayList<>();
        for (Object item : list) {
            result.add(objectMapper.convertValue(item, clazz));
        }
        return result;
    }

    /**
     * 获取List的长度
     */
    public Long lSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /**
     * 获取List中指定索引的值
     */
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 向List中放入数据
     */
    public Long lRightPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 向List中放入数据并设置过期时间
     */
    public Long lRightPush(String key, Object value, long timeout, TimeUnit unit) {
        Long size = redisTemplate.opsForList().rightPush(key, value);
        redisTemplate.expire(key, timeout, unit);
        return size;
    }

    /**
     * 向List中批量放入数据
     */
    public Long lRightPushAll(String key, Object... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 向List中批量放入数据并设置过期时间
     */
    public Long lRightPushAll(String key, long timeout, TimeUnit unit, Object... values) {
        Long size = redisTemplate.opsForList().rightPushAll(key, values);
        redisTemplate.expire(key, timeout, unit);
        return size;
    }

    /**
     * 从List中移除数据
     */
    public Long lRemove(String key, long count, Object value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }

    /**
     * 获取分布式锁
     */
    public boolean tryLock(String key, String value, long timeout, TimeUnit unit) {
        // 参数校验
        if (!StringUtils.hasText(key)) {
            log.warn("【分布式锁】tryLock失败，key为空");
            return false;
        }
        
        // 确保key是有效的字符串
        String safeKey = key.trim().replaceAll("[^a-zA-Z0-9_:.-]", "_");
        if (!StringUtils.hasText(safeKey)) {
            log.warn("【分布式锁】tryLock失败，key无效，原始key={}", key);
            return false;
        }
        
        // 确保value是有效的字符串
        String safeValue = value == null ? UUID.randomUUID().toString() : value.trim();
        if (!StringUtils.hasText(safeValue)) {
            log.warn("【分布式锁】tryLock失败，value无效，key={}", safeKey);
            return false;
        }
        
        // 参数修正
        if (timeout <= 0) {
            timeout = 10; // 默认10秒
        }
        if (unit == null) {
            unit = TimeUnit.SECONDS;
        }
        long millis = unit.toMillis(timeout);
        if (millis <= 0) {
            millis = 10000; // 默认10秒
        }
        
        log.info("【分布式锁】tryLock开始，key={}, value={}, timeout={}, unit={}, millis={}, thread={}", 
                safeKey, safeValue, timeout, unit, millis, Thread.currentThread().getName());
        
        try {
            DefaultRedisScript<Long> script = new DefaultRedisScript<>(LOCK_SCRIPT, Long.class);
            List<String> keys = Collections.singletonList(safeKey);
            // 确保 millis 是有效的正整数
            if (millis <= 0) {
                log.warn("【分布式锁】tryLock失败，timeout必须大于0，millis={}", millis);
                return false;
            }
            String millisStr = String.valueOf(millis);
            log.debug("【分布式锁】tryLock参数，key={}, value={}, millisStr={}", safeKey, safeValue, millisStr);
            String[] args = new String[]{safeValue, millisStr};
            
            // 先检查 key 是否存在
            Boolean exists = redisTemplate.hasKey(safeKey);
            log.debug("【分布式锁】tryLock前检查key是否存在，key={}, exists={}", safeKey, exists);
            
            // 尝试直接使用 set 命令
            Boolean setResult = redisTemplate.opsForValue().setIfAbsent(safeKey, safeValue, millis, TimeUnit.MILLISECONDS);
            log.debug("【分布式锁】直接set命令结果，key={}, result={}", safeKey, setResult);
            
            if (Boolean.TRUE.equals(setResult)) {
                log.info("【分布式锁】tryLock成功（直接set），key={}, value={}", safeKey, safeValue);
                return true;
            }
            
            // 如果直接set失败，尝试使用脚本
            Long result = redisTemplate.execute(script, keys, args);
            boolean lockResult = (result != null && result == 1L);
            
            // 检查执行后的状态
            if (!lockResult) {
                String currentValue = (String) redisTemplate.opsForValue().get(safeKey);
                Long ttl = redisTemplate.getExpire(safeKey, TimeUnit.MILLISECONDS);
                log.debug("【分布式锁】tryLock失败后的状态，key={}, currentValue={}, ttl={}", safeKey, currentValue, ttl);
            }
            
            log.info("【分布式锁】tryLock结束，key={}, value={}, result={}, thread={}", 
                    safeKey, safeValue, lockResult, Thread.currentThread().getName());
            return lockResult;
        } catch (Exception e) {
            log.error("【分布式锁】tryLock异常，key={}, value={}, error={}", safeKey, safeValue, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 释放分布式锁
     */
    public boolean unlock(String key, String value) {
        // 参数校验
        if (!StringUtils.hasText(key)) {
            log.warn("【分布式锁】unlock失败，key为空");
            return false;
        }
        
        // 确保key是有效的字符串
        String safeKey = key.trim().replaceAll("[^a-zA-Z0-9_:.-]", "_");
        if (!StringUtils.hasText(safeKey)) {
            log.warn("【分布式锁】unlock失败，key无效，原始key={}", key);
            return false;
        }
        
        // 确保value是有效的字符串
        String safeValue = value == null ? "" : value.trim();
        if (!StringUtils.hasText(safeValue)) {
            log.warn("【分布式锁】unlock失败，value无效，key={}", safeKey);
            return false;
        }
        
        log.info("【分布式锁】unlock开始，key={}, value={}, thread={}", 
                safeKey, safeValue, Thread.currentThread().getName());
        
        try {
            DefaultRedisScript<Long> script = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
            List<String> keys = Collections.singletonList(safeKey);
            String[] args = new String[]{safeValue};
            
            Long result = redisTemplate.execute(script, keys, args);
            boolean unlockResult = (result != null && result == 1L);
            
            log.info("【分布式锁】unlock结束，key={}, value={}, result={}, thread={}", 
                    safeKey, safeValue, unlockResult, Thread.currentThread().getName());
            return unlockResult;
        } catch (Exception e) {
            log.error("【分布式锁】unlock异常，key={}, value={}, error={}", safeKey, safeValue, e.getMessage());
            return false;
        }
    }

    /**
     * 续期分布式锁
     */
    public boolean renewLock(String key, String value, long timeout, TimeUnit unit) {
        // 参数校验
        if (!StringUtils.hasText(key)) {
            log.warn("【分布式锁】renewLock失败，key为空");
            return false;
        }
        
        // 确保key是有效的字符串
        String safeKey = key.trim().replaceAll("[^a-zA-Z0-9_:.-]", "_");
        if (!StringUtils.hasText(safeKey)) {
            log.warn("【分布式锁】renewLock失败，key无效，原始key={}", key);
            return false;
        }
        
        // 确保value是有效的字符串
        String safeValue = value == null ? "" : value.trim();
        if (!StringUtils.hasText(safeValue)) {
            log.warn("【分布式锁】renewLock失败，value无效，key={}", safeKey);
            return false;
        }
        
        // 参数修正
        if (timeout <= 0) {
            timeout = 10; // 默认10秒
        }
        if (unit == null) {
            unit = TimeUnit.SECONDS;
        }
        long millis = unit.toMillis(timeout);
        if (millis <= 0) {
            millis = 10000; // 默认10秒
        }
        
        log.info("【分布式锁】renewLock开始，key={}, value={}, timeout={}, unit={}, millis={}, thread={}", 
                safeKey, safeValue, timeout, unit, millis, Thread.currentThread().getName());
        
        try {
            DefaultRedisScript<Long> script = new DefaultRedisScript<>(RENEW_SCRIPT, Long.class);
            List<String> keys = Collections.singletonList(safeKey);
            // 确保 millis 是有效的正整数
            if (millis <= 0) {
                log.warn("【分布式锁】renewLock失败，timeout必须大于0，millis={}", millis);
                return false;
            }
            String millisStr = String.valueOf(millis);
            log.debug("【分布式锁】renewLock参数，key={}, value={}, millisStr={}", safeKey, safeValue, millisStr);
            String[] args = new String[]{safeValue, millisStr};
            
            Long result = redisTemplate.execute(script, keys, args);
            boolean renewResult = (result != null && result == 1L);
            
            log.info("【分布式锁】renewLock结束，key={}, value={}, result={}, thread={}", 
                    safeKey, safeValue, renewResult, Thread.currentThread().getName());
            return renewResult;
        } catch (Exception e) {
            log.error("【分布式锁】renewLock异常，key={}, value={}, error={}", safeKey, safeValue, e.getMessage());
            return false;
        }
    }

    /**
     * 批量删除指定前缀的key
     */
    public void deleteByPattern(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 获取缓存统计信息
     */
    public Map<String, Object> getStats() {
        Map<String, Object> stats = new HashMap<>();
        try {
            Properties info = redisTemplate.getConnectionFactory().getConnection().info();
            stats.put("usedMemory", info.getProperty("used_memory"));
            stats.put("connectedClients", info.getProperty("connected_clients"));
            stats.put("totalConnectionsReceived", info.getProperty("total_connections_received"));
            stats.put("totalCommandsProcessed", info.getProperty("total_commands_processed"));
            stats.put("instantaneousOpsPerSec", info.getProperty("instantaneous_ops_per_sec"));
            stats.put("keyspaceHits", info.getProperty("keyspace_hits"));
            stats.put("keyspaceMisses", info.getProperty("keyspace_misses"));
        } catch (Exception e) {
            log.error("获取Redis统计信息失败", e);
        }
        return stats;
    }

    /**
     * 获取缓存命中率
     */
    public double getHitRate() {
        try {
            Properties info = redisTemplate.getConnectionFactory().getConnection().info();
            long hits = Long.parseLong(info.getProperty("keyspace_hits", "0"));
            long misses = Long.parseLong(info.getProperty("keyspace_misses", "0"));
            long total = hits + misses;
            return total > 0 ? (double) hits / total : 0;
        } catch (Exception e) {
            log.error("获取Redis命中率失败", e);
            return 0;
        }
    }

    /**
     * 定期清理TTL为-1的死锁key（建议用定时任务调用）
     */
    public void cleanDeadLocks(String lockPrefix) {
        Set<String> keys = redisTemplate.keys(lockPrefix + "*");
        if (keys != null && !keys.isEmpty()) {
            for (String key : keys) {
                Long ttl = redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
                if (ttl == null || ttl == -1) {
                    redisTemplate.delete(key);
                    log.warn("【分布式锁】发现并清理死锁key：{}", key);
                }
            }
        }
    }
} 