package com.elab.cache.redis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.TypeReference;
import com.elab.cache.ICacheClient;
import com.elab.core.utils.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import redis.clients.jedis.Jedis;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * redis基础服务定义
 *
 * @author Liukx
 * @create 2018-03-05 19:00
 * @email liukx@elab-plus.com
 **/
public abstract class BaseRedisOperation extends BaseRedis {

    public Jedis getJedis() {
        Jedis jedis = getResource();
        if (ObjectUtils.isNotEmpty(this.password)) {
            jedis.auth(this.password);
        }
        jedis.select(dbIndex);
        logger.debug(" 获取redis连接 db  [" + dbIndex + "]");
        return jedis;
    }

    /**
     * 获取当前redis的连接资源
     *
     * @return
     */
    public abstract Jedis getResource();

    public void close(Jedis jedis) {
        try {
            jedis.close();
        } catch (RuntimeException e) {
            logger.error("关闭redis异常", e);
        }
        logger.debug(" 关闭 redis连接 db  [" + dbIndex + "]");
    }

    public abstract ICacheClient getCacheClient();

    /**
     * 将指定的key进行递减
     *
     * @param key 键
     * @param num 减去的值
     * @return
     */
    public Long decrby(String key, long num) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.decrBy(key, num);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, num + "");
        } finally {
            close(jedis);
        }
        return 0L;
    }

    /**
     * 将指定的key进行自增
     *
     * @param key 键
     * @param num 累加的值
     * @return
     */
    public Long incrby(String key, long num) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            return jedis.incrBy(key, num);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, num + "");
        } finally {
            close(jedis);
        }
        return 0L;
    }

    /**
     * 根据指定的key获取对象
     *
     * @param key   键 - 标识
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T get(String key, Class clazz) {
        String value = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            value = jedis.get(key);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        T t = (T) JSON.parseObject(value, clazz);
        return t;
    }

    /**
     * 根据缓存的key获取一个集合
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> getList(String key, Class clazz) {
        String value = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            value = jedis.get(key);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }

        if (StringUtils.isEmpty(value)) {
            return null;
        }

        List<T> tempList = JSON.parseObject(value, new TypeReference<List<T>>() {
        });
        List<T> list = JSONArray.parseArray(tempList.toString(), clazz);
        return list;
    }

    /**
     * 根据key获取Map对象
     *
     * @param key   缓存的key
     * @param clazz 需要转换的对象
     * @param <T>
     * @return
     */
    public <T> Map<String, T> getMap(String key, Class clazz) {
        String value = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            value = jedis.get(key);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        Map<String, T> map = JSON.parseObject(value, new TypeReference<Map<String, T>>() {
        });
        return map;
    }

    /**
     * 为指定的key设定有效时长
     *
     * @param key     键
     * @param seconds 有效时长 单位:秒
     * @return
     */
    public boolean expire(String key, int seconds) {
        Long expire = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            expire = jedis.expire(key, seconds);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, seconds);
        } finally {
            close(jedis);
        }
        if (expire > 0) {
            return true;
        }
        return false;
    }

    /**
     * 查看当前key 还有多少有效时间
     *
     * @param key
     * @return
     */
    public Long ttl(String key) {
        Long ttl = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            ttl = jedis.ttl(key);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        return ttl;
    }

    /**
     * 如果不存在则添加,存在则返回false
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean setNX(String key, Object value) {
        Long setex = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            setex = jedis.setnx(key, value.toString());
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        // 0 表示未设置 1表示已经设置了
        if (setex > 0) {
            return true;
        }
        return false;
    }

    /**
     * 删除缓存中的数据
     *
     * @param key 键
     */
    public void delete(String key) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            jedis.del(key);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
    }

    /**
     * 为缓存中的数据加锁,通常用于分布式锁
     *
     * @param key     key
     * @param value   值
     * @param timeout 超时时间 单位秒
     * @return
     */
    public boolean tryLock(String key, Object value, int timeout) {
        String lockKey = "locked#" + key;
        long startTime = System.currentTimeMillis();
        int sleepTimes = 0;

        while (!setNX(lockKey, value)) {
            try {
                Thread.sleep(1);
                sleepTimes++;
                if (sleepTimes % 100 == 0) {
                    //超时判断
                    if (System.currentTimeMillis() - startTime > timeout * 1000) {
                        return false;
                    }
                }
            } catch (InterruptedException e) {
                return false;
            }
        }
        return true;
    }

    public void unLock(String key) {
        String lockKey = "locked#" + key;
        delete(lockKey);
    }

    /**
     * <p>通过key 和offset 从指定的位置开始将原先value替换</p>
     * <p>下标从0开始,offset表示从offset下标开始替换</p>
     * <p>如果替换的字符串长度过小则会这样</p>
     * <p>example:</p>
     * <p>value : bigsea@zto.cn</p>
     * <p>str : abc </p>
     * <P>从下标7开始替换  则结果为</p>
     * <p>RES : bigsea.abc.cn</p>
     *
     * @param key
     * @param value
     * @param offset 下标位置
     * @return 返回替换后  value 的长度
     */
    public Long setRange(String key, String value, int offset) {
        Long count = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            count = jedis.setrange(key, offset, value);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, value);
        } finally {
            close(jedis);
        }
        return count;
    }

    /**
     * <p>通过下标 和key 获取指定下标位置的 value</p>
     *
     * @param key
     * @param startOffset 开始位置 从0 开始 负数表示从右边开始截取
     * @param endOffset
     * @return 如果没有返回null
     */
    public String getRange(String key, int startOffset, int endOffset) {
        String data = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            data = jedis.getrange(key, startOffset, endOffset);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        return data;
    }

    /**
     * <p>通过批量的key获取批量的value</p>
     *
     * @param keys string数组 也可以是一个key
     * @return 成功返回value的集合, 失败返回null的集合 ,异常返回空
     */
    public List<String> mget(String... keys) {
        List<String> list = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            list = jedis.mget(keys);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), keys);
        } finally {
            close(jedis);
        }
        return list;
    }

    /**
     * <p>批量的设置key:value,可以一个</p>
     * <p>example:</p>
     * <p>  obj.mset(new String[]{"key2","value1","key2","value2"})</p>
     *
     * @param keysvalues
     * @return 成功返回OK 失败 异常 返回 null
     */
    public String mset(String... keysvalues) {
        String data = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            data = jedis.mset(keysvalues);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), keysvalues, keysvalues);
        } finally {
            close(jedis);
        }
        return data;
    }

    /**
     * <p>批量的设置key:value,可以一个,如果key已经存在则会失败,操作会回滚</p>
     * <p>example:</p>
     * <p>  obj.msetnx(new String[]{"key2","value1","key2","value2"})</p>
     *
     * @param keysvalues
     * @return 成功返回1 失败返回0
     */
    public Long msetnx(String... keysvalues) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.msetnx(keysvalues);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), keysvalues, keysvalues);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * <p>通过key给Hash Field设置指定的值,如果key不存在,则先创建</p>
     *
     * @param key
     * @param field 字段
     * @param value
     * @return 如果存在返回0 异常返回null
     */
    public Long hset(String key, String field, String value) {
        Long count = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            count = jedis.hset(key, field, value);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, field, value);
        } finally {
            close(jedis);
        }
        return count;
    }

    /**
     * <p>Sets field in the hash stored at key to value
     * 通过key给field设置指定的值,如果key不存在则先创建,如果field已经存在,返回0</p>
     *
     * @param key
     * @param field
     * @param value
     * @return
     */
    public Long hsetnx(String key, String field, String value) {
        Long count = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            count = jedis.hsetnx(key, field, value);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, field, value);
        } finally {
            close(jedis);
        }
        return count;
    }

    /**
     * <p>通过key同时设置 hash的多个field</p>
     *
     * @param key
     * @param hash
     * @return 返回OK 异常返回null
     */
    public String hmset(String key, Map<String, String> hash) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hmset(key, hash);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, hash);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * <p>通过key 和 field 获取指定的 value</p>
     *
     * @param key
     * @param field
     * @return 没有返回null
     */
    public String hget(String key, String field) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hget(key, field);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, field);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * <p>通过key 和 fields 获取指定的value 如果没有对应的value则返回null</p>
     *
     * @param key
     * @param fields 可以使 一个String 也可以是 String数组
     * @return
     */
    public List<String> hmget(String key, String... fields) {
        List<String> result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.hmget(key, fields);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, fields);
        } finally {
            close(jedis);
        }
        return result;
    }


    /**
     * <p>通过key向list头部添加字符串</p>
     *
     * @param key
     * @param strs 可以是一个string 也可以是string数组
     * @return 返回list的value个数
     */
    public Long lpush(String key, String... strs) {
        Long result = 0L;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.lpush(key, strs);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, strs);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 获取指定list
     *
     * @param key        键
     * @param startIndex 开始下标
     * @param endIndex   结束下标  -1 代表获取所有
     * @return
     */
    public List<String> lrange(String key, int startIndex, int endIndex) {
        List<String> list = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            list = jedis.lrange(key, startIndex, endIndex);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, startIndex, endIndex);
        } finally {
            close(jedis);
        }
        return list;
    }


    /**
     * 获取所有list
     *
     * @param key 键
     * @return
     */
    public List<String> lrange(String key) {
        List<String> list = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            list = jedis.lrange(key, 0, -1);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        return list;
    }

    /**
     * 设定数据到缓存中
     *
     * @param key  键
     * @param data
     * @param <T>
     */
    public <T> void set(String key, T data) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            jedis.set(key, JSON.toJSONString(data));
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
    }

    public <T> void setList(String key, List<T> list) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            jedis.set(key, JSON.toJSONString(list));
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, list);
        } finally {
            close(jedis);
        }
    }

    /**
     * 添加一个map的值
     *
     * @param key 键
     * @param map Map集合
     * @param <T> 泛型任意类型
     */
    public <T> void setMap(String key, Map<String, T> map) {
        Jedis jedis = null;
        try {
            jedis = getJedis();
            jedis.set(key, JSON.toJSONString(map));
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, map);
        } finally {
            close(jedis);
        }
    }

    /**
     * 判断指定的key是否存在
     *
     * @param key
     * @return
     */
    public boolean exsit(String key) {
        boolean flag = false;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            flag = jedis.exists(key);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        return flag;
    }

    /**
     * 添加数据到缓存中
     *
     * @param key       键
     * @param value     值
     * @param validTime 有效时间  1秒=1000   -1 永久有效
     */
    public void set(String key, Object value, int validTime) {
        String data = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            data = JSON.toJSONString(value);
        } catch (Exception e) {
            data = value.toString();
        }
        try {
            jedis.set(key, data);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCachePutError(e, getCacheClient(), key, value);
        } finally {
            close(jedis);
        }
        expire(key, validTime);
    }

    /**
     * 获取缓存中的数据
     *
     * @param key 缓存的key
     * @return
     */
    public String get(String key) {
        String result = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            result = jedis.get(key);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), key);
        } finally {
            close(jedis);
        }
        return result;
    }

    /**
     * 根据前缀获取缓存的key集合
     *
     * @param prefixKey
     * @return
     */
    public Set<String> keys(String prefixKey) {
        Set<String> keyList = null;
        Jedis jedis = null;
        try {
            jedis = getJedis();
            keyList = jedis.keys(prefixKey);
        } catch (RuntimeException e) {
            getCacheErrorProcess().handleCacheGetError(e, getCacheClient(), prefixKey);
        } finally {
            close(jedis);
        }
        return keyList;
    }
}
