package com.changhu.support.redis.utils;

import cn.hutool.extra.spring.SpringUtil;
import com.changhu.common.exception.MessageException;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author 20252
 * @createTime 2024/7/23 下午5:15
 * @desc RedisTemplateUtil...
 */
@Slf4j
public class RedisTemplateUtil {

    public static final RedisTemplate<String, Object> redisTemplate = SpringUtil.getBean("redisTemplate");

    // ============================= 通用 ============================

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(默认秒)
     */
    public static boolean expire(@NotNull String key, @NotNull Long time) {
        return expire(key, time, TimeUnit.SECONDS);
    }

    /**
     * 指定缓存失效时间
     *
     * @param key      键
     * @param time     时间
     * @param timeUnit 时间单位
     */
    public static boolean expire(@NotNull String key, @NotNull Long time, TimeUnit timeUnit) {
        try {
            if (time < 0) {
                return false;
            }
            return Boolean.TRUE.equals(redisTemplate.expire(key, time, timeUnit));
        } catch (Exception e) {
            log.error("指定缓存key:【{}】 失效时间失败:{}", key, e);
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(默认为秒) 返回0代表为永久有效
     */
    public static Long getExpire(@NotNull String key) {
        return getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key      键 不能为null
     * @param timeUnit 时间单位
     * @return 过期时间
     */
    public static Long getExpire(@NotNull String key, TimeUnit timeUnit) {
        try {
            return redisTemplate.getExpire(key, timeUnit);
        } catch (Exception e) {
            log.error("获取key:【{}】 过期时间失败:{}", key, e);
            return null;
        }
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public static boolean hasKey(@NotNull String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("判断key:【{}】是否存在失败:{}", key, e);
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param keys 可以传一个值 或多个
     */
    public static Long del(@NotNull String... keys) {
        return del(Arrays.asList(keys));
    }

    /**
     * 删除缓存（List格式）
     *
     * @param keys list集合
     * @return 被删数量
     */
    public static Long del(@NotNull List<String> keys) {
        try {
            return redisTemplate.delete(keys);
        } catch (Exception e) {
            log.error("删除缓存:【{}】 失败:{}", keys, e);
            return null;
        }
    }

    /**
     * 删除以 prefix 为前缀的所有缓存
     * 如：prefix = abc，则可以删除 abc abc123 abc_ed abc_234_ecd
     *
     * @param prefix 前缀
     * @return 被删数量
     */
    public static Long delByPrefix(String... prefix) {
        return del(Arrays.stream(prefix)
                .map(s -> s + "*")
                .toList());
    }

    /**
     * string 操作相关
     */
    public static class STRING {
        @SuppressWarnings("unchecked")
        public static <V> V get(@NotNull String key) {
            try {
                return (V) redisTemplate.opsForValue().get(key);
            } catch (Exception e) {
                // redis不可用时
                log.error("STRING获取key:【{}】 失败:{}", key, e);
                return null;
            }
        }

        /**
         * 普通缓存放入
         *
         * @param key   键
         * @param value 值
         * @return true成功 false失败
         */
        public static boolean set(@NotNull String key, Object value) {
            return set(key, value, -1);
        }

        /**
         * 普通缓存放入并设置时间
         *
         * @param key   键
         * @param value 值
         * @param time  时间(time要大于0 如果time小于等于0 将设置无限期) 默认为秒
         * @return true成功 false 失败
         */
        public static boolean set(@NotNull String key, Object value, long time) {
            return set(key, value, time, TimeUnit.SECONDS);
        }

        /**
         * 普通缓存放入并设置时间
         *
         * @param key      键
         * @param value    值
         * @param time     时间(time要大于0 如果time小于等于0 将设置无限期)
         * @param timeUnit 时间单位
         * @return true成功 false 失败
         */
        public static boolean set(@NotNull String key, Object value, long time, TimeUnit timeUnit) {
            try {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, timeUnit);
                } else {
                    redisTemplate.opsForValue().set(key, value);
                }
                return true;
            } catch (Exception e) {
                log.error("STRING设置key:【{}】 失败:{}", key, e);
                return false;
            }
        }

        /**
         * 递增
         *
         * @param key   键
         * @param delta 要增加几(大于0)
         */
        public static Long incr(@NotNull String key, long delta) {
            try {
                if (delta < 0) {
                    throw new MessageException("递增因子必须大于0");
                }
                return redisTemplate.opsForValue().increment(key, delta);
            } catch (Exception e) {
                log.error("STRING递增key:【{}】 失败:{}", key, e);
                return null;
            }
        }

        /**
         * 递减
         *
         * @param key   键
         * @param delta 要减少几(小于0)
         */
        public static Long decr(@NotNull String key, long delta) {
            try {
                if (delta < 0) {
                    throw new MessageException("递减因子必须大于0");
                }
                return redisTemplate.opsForValue().increment(key, -delta);
            } catch (Exception e) {
                log.error("STRING递减key:【{}】 失败:{}", key, e);
                return null;
            }
        }

    }

    /**
     * hash 操作相关
     */
    public static class HASH {
        /**
         * 从hash表中取出指定项
         *
         * @param key  键
         * @param item 项
         */
        @SuppressWarnings("unchecked")
        public static <V> V hGet(@NotNull String key, @NotNull String item) {
            try {
                return (V) redisTemplate.opsForHash().get(key, item);
            } catch (Exception e) {
                log.error("HASH hget key:【{}】 失败:{}", key, e);
                return null;
            }
        }

        /**
         * 向一张hash表中放入数据,如果不存在将创建
         *
         * @param key   键
         * @param item  项
         * @param value 值
         * @return true 成功 false失败
         */
        public static boolean hSet(@NotNull String key, String item, Object value) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                return true;
            } catch (Exception e) {
                log.error("HASH hset key:【{}】 失败:{}", key, e);
                return false;
            }
        }
    }

}
