package com.lesingle.edu.common.util;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis 工具类
 * <p>
 * 封装常用的 Redis 操作，包括 String、Hash、List、Set、ZSet 操作
 * </p>
 *
 * @author reading-platform
 * @since 2026-03-13
 */
@Component
@RequiredArgsConstructor
public class RedisUtils {

    private final RedisTemplate<String, Object> redisTemplate;

    // ============================= String 操作 =============================

    /**
     * 设置字符串值
     *
     * @param key   键
     * @param value 值
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 设置字符串值及过期时间
     *
     * @param key     键
     * @param value   值
     * @param timeout 过期时间
     * @param unit    时间单位
     */
    public void set(String key, Object value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 获取字符串值
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 获取字符串值并转换为指定类型
     *
     * @param key   键
     * @param clazz 目标类型
     * @param <T>   类型参数
     * @return 值
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        Object value = redisTemplate.opsForValue().get(key);
        if (value == null) {
            return null;
        }
        if (clazz.isInstance(value)) {
            return (T) value;
        }
        return null;
    }

    /**
     * 删除键
     *
     * @param key 键
     * @return 是否删除成功
     */
    public boolean delete(String key) {
        Boolean result = redisTemplate.delete(key);
        return result != null && result;
    }

    /**
     * 批量删除键
     *
     * @param keys 键集合
     * @return 删除的数量
     */
    public long delete(Collection<String> keys) {
        Long result = redisTemplate.delete(keys);
        return result != null ? result : 0;
    }

    /**
     * 检查键是否存在
     *
     * @param key 键
     * @return 是否存在
     */
    public boolean exists(String key) {
        Boolean result = redisTemplate.hasKey(key);
        return result != null && result;
    }

    /**
     * 设置过期时间
     *
     * @param key     键
     * @param timeout 过期时间
     * @param unit    时间单位
     * @return 是否设置成功
     */
    public boolean expire(String key, long timeout, TimeUnit unit) {
        Boolean result = redisTemplate.expire(key, timeout, unit);
        return result != null && result;
    }

    /**
     * 获取剩余过期时间
     *
     * @param key  键
     * @param unit 时间单位
     * @return 剩余时间
     */
    public Long getExpire(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key, unit);
    }

    /**
     * 自增 1
     *
     * @param key 键
     * @return 自增后的值
     */
    public long increment(String key) {
        Long result = redisTemplate.opsForValue().increment(key);
        return result != null ? result : 0;
    }

    /**
     * 自增指定值
     *
     * @param key  键
     * @param delta 增量
     * @return 自增后的值
     */
    public long increment(String key, long delta) {
        Long result = redisTemplate.opsForValue().increment(key, delta);
        return result != null ? result : 0;
    }

    /**
     * 自减 1
     *
     * @param key 键
     * @return 自减后的值
     */
    public long decrement(String key) {
        Long result = redisTemplate.opsForValue().decrement(key);
        return result != null ? result : 0;
    }

    /**
     * 自减指定值
     *
     * @param key  键
     * @param delta 减量
     * @return 自减后的值
     */
    public long decrement(String key, long delta) {
        Long result = redisTemplate.opsForValue().decrement(key, delta);
        return result != null ? result : 0;
    }

    // ============================= Hash 操作 =============================

    /**
     * 设置 Hash 字段值
     *
     * @param key   键
     * @param field 字段
     * @param value 值
     */
    public void hashSet(String key, String field, Object value) {
        redisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 获取 Hash 字段值
     *
     * @param key   键
     * @param field 字段
     * @return 值
     */
    public Object hashGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 获取 Hash 字段值并转换为指定类型
     *
     * @param key   键
     * @param field 字段
     * @param clazz 目标类型
     * @param <T>   类型参数
     * @return 值
     */
    @SuppressWarnings("unchecked")
    public <T> T hashGet(String key, String field, Class<T> clazz) {
        Object value = redisTemplate.opsForHash().get(key, field);
        if (value == null) {
            return null;
        }
        if (clazz.isInstance(value)) {
            return (T) value;
        }
        return null;
    }

    /**
     * 批量设置 Hash 字段值
     *
     * @param key 键
     * @param map 字段值映射
     */
    public void hashSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * 获取所有 Hash 字段值
     *
     * @param key 键
     * @return 字段值映射
     */
    public Map<Object, Object> hashGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 删除 Hash 字段
     *
     * @param key    键
     * @param fields 字段数组
     * @return 删除的数量
     */
    public long hashDelete(String key, Object... fields) {
        Long result = redisTemplate.opsForHash().delete(key, fields);
        return result != null ? result : 0;
    }

    /**
     * 检查 Hash 字段是否存在
     *
     * @param key   键
     * @param field 字段
     * @return 是否存在
     */
    public boolean hashHasKey(String key, String field) {
        Boolean result = redisTemplate.opsForHash().hasKey(key, field);
        return result != null && result;
    }

    // ============================= List 操作 =============================

    /**
     * 左侧推入列表
     *
     * @param key   键
     * @param value 值
     * @return 列表长度
     */
    public long listLeftPush(String key, Object value) {
        Long result = redisTemplate.opsForList().leftPush(key, value);
        return result != null ? result : 0;
    }

    /**
     * 右侧推入列表
     *
     * @param key   键
     * @param value 值
     * @return 列表长度
     */
    public long listRightPush(String key, Object value) {
        Long result = redisTemplate.opsForList().rightPush(key, value);
        return result != null ? result : 0;
    }

    /**
     * 批量右侧推入列表
     *
     * @param key    键
     * @param values 值集合
     * @return 列表长度
     */
    public long listRightPushAll(String key, Collection<Object> values) {
        Long result = redisTemplate.opsForList().rightPushAll(key, values);
        return result != null ? result : 0;
    }

    /**
     * 获取列表指定范围的元素
     *
     * @param key   键
     * @param start 起始索引
     * @param end   结束索引
     * @return 元素列表
     */
    public List<Object> listRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 获取列表长度
     *
     * @param key 键
     * @return 列表长度
     */
    public long listSize(String key) {
        Long result = redisTemplate.opsForList().size(key);
        return result != null ? result : 0;
    }

    /**
     * 移除并获取列表第一个元素
     *
     * @param key 键
     * @return 元素值
     */
    public Object listLeftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移除并获取列表最后一个元素
     *
     * @param key 键
     * @return 元素值
     */
    public Object listRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    // ============================= Set 操作 =============================

    /**
     * 添加元素到集合
     *
     * @param key    键
     * @param values 值数组
     * @return 添加的数量
     */
    public long setAdd(String key, Object... values) {
        Long result = redisTemplate.opsForSet().add(key, values);
        return result != null ? result : 0;
    }

    /**
     * 获取集合所有元素
     *
     * @param key 键
     * @return 元素集合
     */
    public Set<Object> setMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 移除集合元素
     *
     * @param key    键
     * @param values 值数组
     * @return 移除的数量
     */
    public long setRemove(String key, Object... values) {
        Long result = redisTemplate.opsForSet().remove(key, values);
        return result != null ? result : 0;
    }

    /**
     * 获取集合大小
     *
     * @param key 键
     * @return 集合大小
     */
    public long setSize(String key) {
        Long result = redisTemplate.opsForSet().size(key);
        return result != null ? result : 0;
    }

    /**
     * 检查元素是否在集合中
     *
     * @param key   键
     * @param value 值
     * @return 是否存在
     */
    public boolean setIsMember(String key, Object value) {
        Boolean result = redisTemplate.opsForSet().isMember(key, value);
        return result != null && result;
    }

    // ============================= ZSet 操作 =============================

    /**
     * 添加元素到有序集合
     *
     * @param key   键
     * @param value 值
     * @param score 分数
     * @return 是否添加成功
     */
    public boolean zSetAdd(String key, Object value, double score) {
        Boolean result = redisTemplate.opsForZSet().add(key, value, score);
        return result != null && result;
    }

    /**
     * 批量添加元素到有序集合
     *
     * @param key    键
     * @param tuples 值 - 分数对
     * @return 添加的数量
     */
    public long zSetAddBatch(String key, Set<RedisZSetTuple> tuples) {
        if (CollectionUtils.isEmpty(tuples)) {
            return 0;
        }
        Set<org.springframework.data.redis.core.ZSetOperations.TypedTuple<Object>> typedTuples = new java.util.HashSet<>();
        for (RedisZSetTuple tuple : tuples) {
            typedTuples.add(org.springframework.data.redis.core.ZSetOperations.TypedTuple.of(tuple.getValue(), tuple.getScore()));
        }
        Long result = redisTemplate.opsForZSet().add(key, typedTuples);
        return result != null ? result : 0;
    }

    /**
     * 获取有序集合指定范围的元素
     *
     * @param key   键
     * @param start 起始索引
     * @param end   结束索引
     * @return 元素集合
     */
    public Set<Object> zSetRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取元素的分数
     *
     * @param key   键
     * @param value 值
     * @return 分数
     */
    public Double zSetScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 移除有序集合元素
     *
     * @param key    键
     * @param values 值数组
     * @return 移除的数量
     */
    public long zSetRemove(String key, Object... values) {
        Long result = redisTemplate.opsForZSet().remove(key, values);
        return result != null ? result : 0;
    }

    /**
     * 获取有序集合大小
     *
     * @param key 键
     * @return 集合大小
     */
    public long zSetSize(String key) {
        Long result = redisTemplate.opsForZSet().size(key);
        return result != null ? result : 0;
    }

    /**
     * 获取元素在有序集合中的排名（从小到大）
     *
     * @param key   键
     * @param value 值
     * @return 排名（从 0 开始）
     */
    public Long zSetRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 获取元素在有序集合中的排名（从大到小）
     *
     * @param key   键
     * @param value 值
     * @return 排名（从 0 开始）
     */
    public Long zSetReverseRank(String key, Object value) {
        return redisTemplate.opsForZSet().reverseRank(key, value);
    }

    // ============================= 辅助类 =============================

    /**
     * ZSet 元素封装类
     */
    @lombok.Data
    @lombok.RequiredArgsConstructor
    public static class RedisZSetTuple {
        private final Object value;
        private final Double score;
    }
}
