package shop.alien.config.redis;

import lombok.RequiredArgsConstructor;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Redis基础服务类
 */
@Component
@RequiredArgsConstructor
public class BaseRedisService {

    private final StringRedisTemplate stringRedisTemplate;

    /**
     * 添加List值, 向右
     *
     * @param key   键
     * @param value 值
     */
    public void setListRight(String key, String value) {
        stringRedisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * 取出并删除列表中的所有元素（原子操作）
     */
    public List<String> popBatchFromList(String key) {
        String luaScript =
                "local elements = redis.call('LRANGE', KEYS[1], 0, -1) " +
                        "redis.call('DEL', KEYS[1]) " +
                        "return elements ";

        RedisScript<List> script = RedisScript.of(luaScript, List.class);
        return stringRedisTemplate.execute(script, Collections.singletonList(key));
    }

    /**
     * 添加List, 所有
     *
     * @param key   键
     * @param value 值
     */
    public void setList(String key, List<String> value) {
        stringRedisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 从List左边取值并删除
     *
     * @param key 键
     * @return 删除的值
     */
    public String getListLeft(String key) {
        return stringRedisTemplate.opsForList().leftPop(key);
    }

    /**
     * 获取List所有
     *
     * @param key 键
     * @return List<String>
     */
    public List<String> getList(String key) {
        return stringRedisTemplate.opsForList().range(key, 0, -1);
    }


    /**
     * 添加Set
     *
     * @param key   键
     * @param value 值
     */
    public void setSetList(String key, String value) {
        stringRedisTemplate.opsForSet().add(key, value);
    }

    /**
     * 获取Set
     *
     * @param key 键
     * @return Set<String>
     */
    public Set<String> getSetList(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }

    /**
     * 添加String值, 不设置过期时间
     *
     * @param key   键
     * @param value 值
     */
    public void setString(String key, String value) {
        set(key, value, null);
    }

    /**
     * 添加String值, 并设置过期时间
     *
     * @param key     键
     * @param value   值
     * @param timeOut 时长(秒)
     */
    public void setString(String key, String value, Long timeOut) {
        set(key, value, timeOut);
    }

    /**
     * 判断是否存在指定key
     * @param key
     * @return
     */
    public boolean hasKey(String key) {
        Boolean exists = stringRedisTemplate.hasKey(key);
        return exists != null && exists;
    }
    
    /**
     * 添加String值, 如果key不存在则设置成功并返回true，否则返回false
     *
     * @param key     键
     * @param value   值
     * @param timeOut 时长(秒)
     * @return 是否设置成功
     */
    public Boolean setStringIfAbsent(String key, String value, Long timeOut) {
        return stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeOut, TimeUnit.SECONDS);
    }

    /**
     * 设置超时时间
     *
     * @param key     键
     * @param timeOut 时长(秒)
     */
    public void setTimeOut(String key, Long timeOut) {
        stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
    }

    /**
     * 传入object对象
     *
     * @param key     键
     * @param value   值
     * @param timeOut 时长(秒)
     */
    private void set(String key, Object value, Long timeOut) {
        if (value != null) {
            if (value instanceof String) {
                String setValue = (String) value;
                stringRedisTemplate.opsForValue().set(key, setValue);
            }
            //设置有效期
            if (timeOut != null) {
                stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 使用key查找redis信息
     *
     * @param key 键
     * @return
     */
    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /**
     * 使用key删除redis信息
     *
     * @param key 键
     */
    public void delete(String key) {
        stringRedisTemplate.delete(key);
    }

    /**
     * 添加地理信息
     *
     * @param point
     * @param content
     * @param type
     * @return
     */
    public Long inGeolocation(Point point, String content, String type) {
        return stringRedisTemplate.opsForGeo().add(type, point, content);
    }
}