package com.tzld.piaoquan.growth.common.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

@Service
public class RedisUtils {

    private final static Logger log = LoggerFactory.getLogger(RedisUtils.class);

    public final static Long DEFAULT_EXPIRE_TIME = 7L * 24 * 60 * 60; // 默认过期7天,单位秒

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    public boolean containsKey(String key) {
        if (StringUtils.isBlank(key)) {
            log.error("containsKey is empty key:" + key);
            return Boolean.FALSE;
        }
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    public String getString(String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        return Objects.isNull(obj) ? null : obj.toString();
    }

    public Long getLong(String key) {
        long longVal = 0L;
        Object obj = redisTemplate.opsForValue().get(key);
        if (Objects.nonNull(obj)) {
            try {
                longVal = Long.parseLong(obj.toString());
            } catch (Exception e) {
                e.printStackTrace();
                return longVal;
            }

        }
        return longVal;
    }

    public void setValueWithExpire(String key, String value, Long expireTime) {
        redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 仅当 key 仍存在时更新值和过期时间，避免异步续期复活已过期或已删除的 key。
     */
    public boolean setValueWithExpireIfPresent(String key, String value, Long expireTime) {
        String lua = "if redis.call('EXISTS', KEYS[1]) == 1 then "
                + "redis.call('SET', KEYS[1], ARGV[1], 'EX', ARGV[2]) return 1 else return 0 end";
        DefaultRedisScript<Long> script = new DefaultRedisScript<>(lua, Long.class);
        Long result = redisTemplate.execute(script, Collections.singletonList(key), value, String.valueOf(expireTime));
        return Long.valueOf(1L).equals(result);
    }

    public void setIncrementValue(String key, long value, Date date) {
        Long expireTime;
        if (date != null) {
            expireTime = (date.getTime() - System.currentTimeMillis()) / 1000;
            if (expireTime < 0) {
                expireTime = DEFAULT_EXPIRE_TIME;
            }
        } else { // date为null
            expireTime = DEFAULT_EXPIRE_TIME;
        }
        // 只在第一次进行设置过期时间
        if (!containsKey(key)) {
            redisTemplate.opsForValue().increment(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().increment(key, value);
        }
    }

    /**
     * 原子递增并返回递增后的值，仅在 key 首次创建时设置过期时间（避免竞态导致永不过期）
     */
    public Long incrWithExpire(String key, long expireSeconds) {
        String lua = "local v = redis.call('INCR', KEYS[1]) "
                + "if v == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end "
                + "return v";
        DefaultRedisScript<Long> script = new DefaultRedisScript<>(lua, Long.class);
        return redisTemplate.execute(script, Collections.singletonList(key), String.valueOf(expireSeconds));
    }

    public void setIncrementValue(String key, Integer value, Long expireTime) {
        incrWithExpire(key, expireTime);
    }

    public void setDecrementValue(String key, double value) {
        redisTemplate.opsForValue().increment(key, -value);
    }

    public void setIncrementValue(String key, double value, Date date) {
        Long expireTime;
        if (date != null) {
            expireTime = (date.getTime() - System.currentTimeMillis()) / 1000;
            if (expireTime < 0) {
                expireTime = DEFAULT_EXPIRE_TIME;
            }
        } else { // date为null
            expireTime = DEFAULT_EXPIRE_TIME;
        }
        // 只在第一次进行设置过期时间
        if (!containsKey(key)) {
            redisTemplate.opsForValue().increment(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
        } else {
            redisTemplate.opsForValue().increment(key, value);
        }
    }

    public int getInteger(String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        if (Objects.isNull(obj)) {
            return 0;
        }
        return Integer.valueOf(obj.toString());
    }

    public Integer getIntegerO(String key) {
        Object obj = redisTemplate.opsForValue().get(key);
        if (Objects.isNull(obj)) {
            return null;
        }
        return Integer.valueOf(obj.toString());
    }

    public void putDealyQueueMsg(String key, String value) {
        redisTemplate.opsForZSet().add(key, value, System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);
    }

    public Set<String> processDelayQueue(String key) {
        long currentTimeMillis = System.currentTimeMillis();
        Set<String> values = redisTemplate.opsForZSet().rangeByScore(key, 0, currentTimeMillis);
        if (!CollectionUtils.isEmpty(values)) {
            redisTemplate.opsForZSet().removeRangeByScore(key, 0, currentTimeMillis);
        }
        return values;
    }

    public void addVal(String key, String val) {
        redisTemplate.opsForValue().set(key, val);
    }

    public Long getKeyExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    public void expire(String key, Long time) {
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    public Long countExistingKeys(List<String> keys) {
        return redisTemplate.countExistingKeys(keys);
    }

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

    public boolean set(String key, String 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;
        }
    }

    /**
     * Redis 分布式锁
     *
     * @param key        锁键
     * @param value      锁值，可以为随机数或者 UUID 等唯一标识符
     * @param expireTime 锁过期时间，单位为秒
     * @return true：获取锁成功，false：获取锁失败
     */
    public boolean tryLock(String key, String value, long expireTime) {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
        if (result != null && result) {
            // 获取锁成功
            return true;
        }
        return false;
    }

    /**
     * 带重试的尝试获取分布式锁
     *
     * @param key              锁key
     * @param value            锁value
     * @param expireSeconds    锁超时时间（秒）
     * @param maxRetry         最大重试次数
     * @param retryIntervalMs  每次重试间隔（毫秒）
     * @return true：获取锁成功，false：达到最大重试次数仍未获取到锁
     */
    public boolean tryLockWithRetry(String key, String value, long expireSeconds, int maxRetry, long retryIntervalMs) {
        for (int i = 0; i < maxRetry; i++) {
            if (tryLock(key, value, expireSeconds)) {
                return true;
            }
            try {
                Thread.sleep(retryIntervalMs);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("tryLockWithRetry 被中断，放弃获取锁，key：{}", key);
                return false;
            }
        }
        return false;
    }

    public void listLeftPush(String key, String value) {
        redisTemplate.opsForList().leftPush(key, value);

    }

    public String listRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 从列表中移除指定值，count=0 表示移除所有匹配项
     */
    public void listRemove(String key, String value) {
        redisTemplate.opsForList().remove(key, 0, value);
    }

    /**
     * 获取列表所有元素
     */
    public List<String> listRange(String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    public Double getDouble(String key) {
        try {
            String val = redisTemplate.opsForValue().get(key);
            if (StringUtils.isNotBlank(val)) {
                return Double.valueOf(val);
            }
        } catch (Exception e) {
            log.error("getDouble error redis key:" + key + "----" + e);
        }
        return null;
    }

    public String get(String key) {
        String val = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(val)) {
            return val;
        }
        return null;
    }

    public void sAdd(String key, String val) {
        redisTemplate.opsForSet().add(key, val);
    }

    public Set<String> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }


    public Boolean sIsMember(String key, String val) {
        return redisTemplate.opsForSet().isMember(key, val);
    }

    public void setBit(String key, long val, boolean flag) {
        redisTemplate.opsForValue().setBit(key, val, flag);
    }

    public void setBitList(String key, Set<Long> indexs, boolean flag) {
        RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
        redisTemplate.executePipelined((RedisCallback<Object>) redisConnection -> {
            indexs.forEach(x -> {
                redisConnection.setBit(key.getBytes(), x, flag);
            });
            return null;
        }, serializer);
    }

    public Boolean getBitMap(String key, long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    public List<String> mGet(List<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }
}
