package cn.axzo.msg.center.common.redis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
 * jedis 管理
 * @author Smile
 */
@Service
public class AxzoJedisManager implements IAxzoJedisManager {

    /**
     *  redis 工具
     */
    private RedisTemplate redisTemplate;


    @Autowired
    public void setRedisTemplate (RedisTemplate redisTemplate) {
        JSON.DEFAULT_GENERATE_FEATURE = SerializerFeature.config(
                JSON.DEFAULT_GENERATE_FEATURE, SerializerFeature.SkipTransientField, false);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
//        GenericFastJsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//        jackson2JsonRedisSerializer.setObjectMapper();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        this.redisTemplate = redisTemplate;
    }
    /**
     * 设置
     * @param key
     * @param value
     * @return
     */
    @Override
    public boolean set(final String key, Object value) {
        return set(key, value, 0);
    }

    @Override
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean setIfPresent(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().setIfPresent(key, value, time, TimeUnit.SECONDS);
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public <T> T get(String key, INullValueHandler<T> handler, long time) {
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj != null) {
            return (T)obj;
        } else {
            if (handler != null) {
                obj = handler.handle();
                if (!ObjectUtils.isEmpty(obj)) {
                    if (time > 0) {
                        set(key, obj, time);
                    } else {
                        set(key, obj);
                    }
                    return (T)obj;
                }
            }
        }
        return null;
    }


    @Override
    public <T> T get(String key, INullValueHandler<T> handler) {
       return get(key, handler, 0);
    }

    @Override
    public <T> T get(String key) {
        return get(key,  null);
    }

    @Override
    public boolean del(String key) {
        return redisTemplate.delete(key);
    }

    @Override
    public Long ssl(String key) {
        return redisTemplate.opsForValue().getOperations().getExpire(key);
    }


    /****
     * 通过pipeline进行批量设置
     * 报了一个错误，参考下面的文章得到了问题的解决方案：
     * https://blog.csdn.net/myfwjy/article/details/100776426
     * 《Redis使用Pipeline时对象序列化失败org.springframework.data.redis.serializer.SerializationException》
     * @param map
     */
    @Override
    public void setPipe(Map<String, Object> map) {

        RedisSerializer keySerializer = redisTemplate.getKeySerializer();
        RedisSerializer valueSerializer = redisTemplate.getValueSerializer();

        List list = redisTemplate.executePipelined((RedisCallback<String>) connection -> {
            Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Entry<String, Object> next = iterator.next();

                connection.set(keySerializer.serialize(next.getKey()), valueSerializer.serialize(next.getValue()));
            }
            return null;
            //加不加下面的这一行代码应该都可以
        }, redisTemplate.getValueSerializer());
    }

  @Override
    public List<Object> getPipe(List<String> keys) {
      List<Object> list = redisTemplate.executePipelined((RedisCallback<?>) connection -> {
        for (String s : keys) {
          connection.get(s.getBytes());
        }
        return null;
      });
//
//
//      List<Object> list1 = redisTemplate.opsForValue().multiGet(keys);
//      System.out.println("multiGet获取结果" + list1);
      return list;
    }


    @Override
    public void setHash(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    @Override
    public Map<String, Object> getHash(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public void del(List<String> keys) {
        redisTemplate.delete(keys);
    }

    @Override
    public Set<String> scanKeys(String keyWords) {
        return scanPatternKey(keyWords + "*");
    }

    @Override
    public Set<String> scanPatternKey(String pattern) {
        return (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
            Set<String> keysTmp = new HashSet<>();
            Cursor<byte[]> cursor = connection.scan(
                    new ScanOptions.ScanOptionsBuilder().match(
                            pattern
                    ).count(10000).build()
            );

            while (cursor.hasNext()) {
                keysTmp.add(new String(cursor.next()));
            }

            return keysTmp;
        });
    }

}
