package cn.exploring.engine.server.db.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<Serializable, Object> redisTemplate;

    @Value("${projectRedis.expireDay}")
    private String projectRedisExpireDay;

    /***
     * 获取所有的key
     */
    public Set<Serializable> getKeys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 写入缓存
     *
     * @param key   键
     * @param value 值
     * @return 写入是否成功
     */
    public boolean setCache(final String key, Object value) {
        boolean res = false;
        try {
            ValueOperations<Serializable, Object> opsForValue = redisTemplate.opsForValue();
            opsForValue.set(key, value);
            res = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 写入缓存设置失效时间
     *
     * @param key        键
     * @param vaule      值
     * @param expireTime 失效时间
     * @param timeType   失效时间类型
     * @return 是否写入成功
     */
    public boolean setCache(final String key, Object vaule, Long expireTime, TimeUnit timeType) {
        boolean res = false;
        try {
            ValueOperations<Serializable, Object> opsForValue = redisTemplate.opsForValue();
            opsForValue.set(key, vaule);
            redisTemplate.expire(key, expireTime, timeType);
            res = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }


    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public Object getCache(final String key) {
        ValueOperations<Serializable, Object> opsForValue = redisTemplate.opsForValue();
        return opsForValue.get(key);
    }

    /**
     * 删除对应的值
     *
     * @param key
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }


    /**
     * 判断缓存中是否对应的值
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 批量删除keys
     *
     * @param pattern
     */
    public void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }


    /**
     * 哈希 添加  注意：hmSet方法，
     * isForce :false 只有当值不存在时存入 有值是忽略
     * true 无论如何都存入
     *
     * @param key
     * @param hashKey
     * @param value
     * @return 原先旧值
     */
    public Object hmSet(String key, Object hashKey, Object value, boolean update) {
        Object obj = null;
        try {
            HashOperations<Serializable, Object, Object> opsForHash = redisTemplate.opsForHash();
            obj = opsForHash.get(key, hashKey);
            if (obj != null && update) {
                opsForHash.delete(key, hashKey);
            }
            opsForHash.putIfAbsent(key, hashKey, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 获取所有hashMap 键值
     */
    public Map<Object, Object> hmGetKeys(Serializable key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 删除键下 对map键值
     */
    public long hmDelete(Serializable key, Object... mapKeys) {
        return redisTemplate.opsForHash().delete(key, mapKeys);
    }

    /**
     * 批量添加
     */
    public void hmSets(Serializable key, Map<String, Object> maps) {
        redisTemplate.opsForHash().putAll(key, maps);
    }

    /**
     * 哈希 获取数据
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey) {
        HashOperations<Serializable, Object, Object> hm = redisTemplate.opsForHash();
        return hm.get(key, hashKey);
    }

    /**
     * 列表添加
     *
     * @param key
     * @param value
     */
    public void lPush(String key, Object value) {
        ListOperations<Serializable, Object> lt = redisTemplate.opsForList();
        lt.rightPush(key, value);
    }

    /**
     * 列表获取
     * 阻塞获取 当这段时间类有值时，获得该值，如果过了这段时间还没有值 返回为null
     */
    public Object lPopInBlock(String key, long overtime, TimeUnit time) {
        ListOperations<Serializable, Object> lt = redisTemplate.opsForList();
        return lt.leftPop(key, overtime, time);
    }

    /**
     * 列表获取    直接获取列表值
     */
    public Object lPopNoBlock(String key) {
        ListOperations<Serializable, Object> lt = redisTemplate.opsForList();
        return lt.leftPop(key);
    }

    /**
     * 获取列表
     *
     * @param key
     * @param begin
     * @param end
     * @return
     */
    public List<Object> lRange(String key, long begin, long end) {
        ListOperations<Serializable, Object> lt = redisTemplate.opsForList();
        return lt.range(key, begin, end);
    }

    /**
     * 集合添加
     *
     * @param key
     * @param value
     */
    public void addSetData(String key, Object value) {
        redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 集合获取
     *
     * @param key
     * @return
     */
    public Object getSetMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 有序集合添加
     *
     * @param key
     * @param value
     * @param score
     */
    public void addZSetData(String key, Object value, double score) {
        redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param beginScore
     * @param endScore
     * @return
     */
    public Set<Object> getZSetRangeByScourc(String key, double beginScore, double endScore) {
        return redisTemplate.opsForZSet().rangeByScore(key, beginScore, endScore);
    }

    /**
     * 泛型读取缓存
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> T getCache(final String key, final Class<T> clazz) {
        Object obj = getCache(key);
        return obj == null ? null : (T)obj;
    }

    /**
     * 存入对象
     *
     * @param key        键
     * @param vaule      值
     * @param expireTime 失效时间
     * @param timeType   失效时间类型
     * @return 是否写入成功
     */
    private boolean setCacheObj(final String key, final Object vaule, final Long expireTime, final TimeUnit timeType) {
        boolean res = false;
        try {
            ValueOperations<Serializable, Object> opsForValue = redisTemplate.opsForValue();
            opsForValue.set(key, vaule);
            redisTemplate.expire(key, expireTime, timeType);
            res = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return res;
    }

    /**
     * 从缓存（1=guava，2=redis）中取对象
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getObjFrom1And2Cache(final String key, final Class<T> clazz) {
        Object obj = GuavaCacheUtil.get(key);
        if (obj == null) {
            T t = getCache(key, clazz);
            if (t == null) {
                return null;
            } else {
                GuavaCacheUtil.put(key, t);
                return t;
            }
        } else {
            return (T)obj;
        }
    }

    /**
     * 将对象数据存入（1=guava，2=redis）缓存中
     * @param key
     * @param obj
     * @param expireTime
     * @param timeType
     * @return
     */
    public boolean setObjTo1And2Cache(final String key, final Object obj, final Long expireTime, final TimeUnit timeType) {
        GuavaCacheUtil.put(key, obj);
        return setCacheObj(key, obj, expireTime, timeType);
    }

    /**
     * 将对象数据存入（1=guava，2=redis）缓存中。上一个方面的重载方法，expireTime取配置中值为默认值，和imeType取TimeUnit.Days为默认值
     * @param key
     * @param obj
     * @return
     */
    public boolean setObjTo1And2Cache(final String key, final Object obj) {
        GuavaCacheUtil.put(key, obj);
        return setCacheObj(key, obj, Long.parseLong(projectRedisExpireDay), TimeUnit.DAYS);
    }

    /**
     * 从缓存（1=guava，2=redis）中删除对象
     * @param key
     */
    public void removeObjFrom1And2Cache(final String key) {
        GuavaCacheUtil.remove(key);
        remove(key);
    }

}
