package xtools.boot.cache.redis.base;

import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import xtools.base.config.BaseParams;
import xtools.core.ArrUtils;
import xtools.core.CollectionUtils;
import xtools.core.StringUtils;

import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;

/**
 * <p>Title : RedisService</p>
 * <p>Description : RedisService</p>
 * <p>DevelopTools : Idea_x64_v2026.1</p>
 * <p>DevelopSystem : macOS Sequoia 15.7.5</p>
 * <p>Company : org.xujun</p>
 *
 * @author : XuJun
 * @version : 5.0.0
 * @date : 2026/01/01 09:30
 */
@Component
public class RedisService implements BaseParams {

    /**
     * RedisTemplate
     **/
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * StringRedisTemplate
     **/
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 获取组合 Key
     *
     * @param keys 多个 Key
     * @return 组合 Key
     */
    public String getKey(String... keys) {
        if (ArrUtils.isEmpty(keys)) {
            return null;
        }
        StringJoiner sj = new StringJoiner(CP_COLON);
        for (String key : keys) {
            sj.add(key);
        }
        return sj.toString();
    }

    /**
     * 整数 值递增一
     *
     * @param key Key
     * @return 递增后的值
     */
    public Long incr(String key) {
        return stringRedisTemplate.opsForValue().increment(key);
    }

    /**
     * 保存数据
     *
     * @param key  Key
     * @param data 数据
     */
    public void set(String key, Object data) {
        this.set(key, data, null, null);
    }

    /**
     * 保存数据
     *
     * @param key     Key
     * @param data    数据
     * @param timeout 过期时间
     */
    public void set(String key, Object data, Long timeout) {
        this.set(key, data, timeout, TimeUnit.SECONDS);
    }

    /**
     * 保存数据
     *
     * @param key     Key
     * @param data    数据
     * @param timeout 过期时间
     * @param unit    时间单位
     */
    public void set(String key, Object data, Long timeout, TimeUnit unit) {
        // 参数校验
        if (StringUtils.isEmpty(key) || data == null) {
            return;
        }
        // 序列化数据
        String serializeData;
        if (data instanceof String) {
            serializeData = data.toString();
        } else {
            serializeData = JSON.toJSONString(data);
        }
        if (StringUtils.isEmpty(serializeData)) {
            return;
        }
        if (unit == null) {
            unit = TimeUnit.SECONDS;
        }
        // 判断是否有过期时间
        if (timeout == null || timeout <= 0) {
            stringRedisTemplate.opsForValue().set(key, serializeData);
        } else {
            stringRedisTemplate.opsForValue().set(key, serializeData, timeout, unit);
        }
    }

    /**
     * 保存数据(如果key存在则保存失败)
     *
     * @param key  Key
     * @param data 数据
     * @return 保存结果
     */
    public Boolean setNx(String key, Object data) {
        return this.setNx(key, data, null, null);
    }

    /**
     * 保存数据(如果key存在则保存失败)
     *
     * @param key     Key
     * @param data    数据
     * @param timeout 有效时间
     * @return 保存结果
     */
    public Boolean setNx(String key, Object data, Long timeout) {
        return this.setNx(key, data, timeout, TimeUnit.SECONDS);
    }

    /**
     * 保存数据(如果key存在则保存失败)
     *
     * @param key     Key
     * @param data    数据
     * @param timeout 有效时间
     * @param unit    时间单位
     * @return 保存结果
     */
    public Boolean setNx(String key, Object data, Long timeout, TimeUnit unit) {
        // 参数校验
        if (StringUtils.isEmpty(key) || data == null) {
            return false;
        }
        // 序列化数据
        String serializeData;
        if (data instanceof String) {
            serializeData = data.toString();
        } else {
            serializeData = JSON.toJSONString(data);
        }
        if (StringUtils.isEmpty(serializeData)) {
            return false;
        }
        if (unit == null) {
            unit = TimeUnit.SECONDS;
        }
        // 判断是否有过期时间
        if (timeout == null || timeout <= 0) {
            return stringRedisTemplate.opsForValue().setIfAbsent(key, serializeData);
        } else {
            return stringRedisTemplate.opsForValue().setIfAbsent(key, serializeData, timeout, unit);
        }
    }

    /**
     * 获取数据
     *
     * @param <T>   Class 类型
     * @param key   Key
     * @param clazz Class
     * @return 数据
     */
    public <T> T get(String key, Class<T> clazz) {
        // 参数校验
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        // 从缓存获取数据
        String data = stringRedisTemplate.opsForValue().get(key);
        if (StringUtils.isEmpty(data)) {
            return null;
        }
        return JSON.to(clazz, data);
    }

    /**
     * 删除 key 对应的数据
     *
     * @param key Key
     * @return 删除结果
     */
    public Boolean del(String key) {
        // 参数校验
        if (StringUtils.isEmpty(key)) {
            return false;
        }
        return stringRedisTemplate.delete(key);
    }

    /**
     * 更改 Key
     *
     * @param oldKey 旧Key
     * @param newKey 新Key
     */
    public void rename(String oldKey, String newKey) {
        // 参数校验
        if (StringUtils.isEmpty(oldKey) || StringUtils.isEmpty(newKey)) {
            return;
        }
        stringRedisTemplate.rename(oldKey, newKey);
    }

    /**
     * 设置key的过期时间(秒)
     *
     * @param key     Key
     * @param timeout 时间(秒)
     * @return 设置结果
     */
    public Boolean expire(String key, long timeout) {
        // 参数校验
        if (StringUtils.isEmpty(key) || timeout <= 0) {
            return false;
        }
        return stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 获取key的过期时间(秒)
     *
     * @param key Key
     * @return 过期时间(秒)
     */
    public Long getExpire(String key) {
        // 参数校验
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        return stringRedisTemplate.getExpire(key);
    }

    /**
     * 取消对 Key 过期时间的设置
     *
     * @param key Key
     * @return true or false
     */
    public Boolean persist(String key) {
        // 参数校验
        if (StringUtils.isEmpty(key)) {
            return false;
        }
        return stringRedisTemplate.persist(key);
    }

    /**
     * 查找所有匹配给定的模式的键
     *
     * @param pattern key的表达式,*表示多个,?表示一个
     * @return 全部符合表达式记录
     */
    public Set<String> getByPattern(String pattern) {
        // 参数校验
        if (StringUtils.isEmpty(pattern)) {
            return null;
        }
        return stringRedisTemplate.keys(pattern);
    }

    /**
     * 尝试获取锁
     *
     * @param key        锁的key
     * @param value      请求标识(可用UUID)
     * @param expireTime 过期时间(秒)
     * @return 是否获取成功
     */
    public boolean tryLock(String key, String value, long expireTime) {
        Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(result);
    }

    /**
     * 释放锁(使用Lua脚本保证原子性)
     *
     * @param key   锁的key
     * @param value 请求标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String key, String value) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);

        Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
        return Long.valueOf(CP_NUM1).equals(result);
    }

    /**
     * 保存 Hash 数据
     *
     * @param key Key
     * @param map Hash 数据
     * @return 保存结果
     */
    public boolean hashPutAll(String key, Map<Object, String> map) {
        // 参数校验
        if (StringUtils.isEmpty(key)) {
            return false;
        }
        if (CollectionUtils.isEmpty(map)) {
            return false;
        }
        stringRedisTemplate.opsForHash().putAll(key, map);
        return true;
    }

    /**
     * 保存 Hash 数据
     *
     * @param key     key
     * @param hashKey hashKey
     * @param data    数据
     * @return 保存结果
     */
    public boolean hashPut(String key, Object hashKey, Object data) {
        // 参数校验
        if (StringUtils.isEmpty(key) || Objects.isNull(hashKey)) {
            return false;
        }
        // 序列化数据
        String serializeData;
        if (data instanceof String) {
            serializeData = data.toString();
        } else {
            serializeData = JSON.toJSONString(data);
        }
        if (StringUtils.isEmpty(serializeData)) {
            return false;
        }
        stringRedisTemplate.opsForHash().putIfAbsent(key, hashKey, serializeData);
        return true;
    }

    /**
     * 保存 Hash 数据
     *
     * @param key        key
     * @param hashKey    hashKey
     * @param data       数据
     * @param expireTime 过期时间(秒)
     * @return 保存结果
     */
    public boolean hashPut(String key, Object hashKey, Object data, long expireTime) {
        // 参数校验
        if (StringUtils.isEmpty(key) || Objects.isNull(hashKey)) {
            return false;
        }
        // 序列化数据
        String serializeData;
        if (data instanceof String) {
            serializeData = data.toString();
        } else {
            serializeData = JSON.toJSONString(data);
        }
        if (StringUtils.isEmpty(serializeData)) {
            return false;
        }
        String script =
                "redis.call('HSET', KEYS[1], ARGV[1], ARGV[2]) " +
                        "redis.call('HEXPIRE', KEYS[1], ARGV[3], 'FIELDS', 1, ARGV[1]) " +
                        "return 1";
        Long result = stringRedisTemplate.execute(
                new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(key),
                hashKey, serializeData, String.valueOf(expireTime)
        );
        return Long.valueOf(CP_NUM1).equals(result);
    }

    /**
     * 获取 Hash 中所有数据
     *
     * @param <T>   结果类型
     * @param key   key
     * @param clazz 结果类型
     * @return 数据集
     */
    public <T> T hashEntries(String key, Class<T> clazz) {
        // 参数校验
        if (StringUtils.isEmpty(key)) {
            return null;
        }
        Map<Object, Object> data = stringRedisTemplate.opsForHash().entries(key);
        if (CollectionUtils.isEmpty(data)) {
            return null;
        }
        return JSON.to(clazz, data);
    }

    /**
     * 获取 Hash 中指定 Key 的数据
     *
     * @param <T>     结果类型
     * @param key     key
     * @param hashKey hashKey
     * @param clazz   结果类型
     * @return 数据
     */
    public <T> T hashGet(String key, Object hashKey, Class<T> clazz) {
        // 参数校验
        if (StringUtils.isEmpty(key) || Objects.isNull(hashKey)) {
            return null;
        }
        Object data = stringRedisTemplate.opsForHash().get(key, hashKey);
        return JSON.to(clazz, data);
    }

    /**
     * 删除 Hash 中指定 Key 的数据
     *
     * @param key      key
     * @param hashKeys hashKey数组
     * @return 删除结果
     */
    public Long hashDelete(String key, Object... hashKeys) {
        if (StringUtils.isBlank(key) || ArrUtils.isEmpty(hashKeys)) {
            return 0L;
        }
        return stringRedisTemplate.opsForHash().delete(key, hashKeys);
    }

    /**
     * 判断 Hash 中是否存在指定 Key 的数据
     *
     * @param key     key
     * @param hashKey hashKey
     * @return 存在结果
     */
    public boolean hashExists(String key, Object hashKey) {
        if (StringUtils.isBlank(key) || Objects.isNull(hashKey)) {
            return false;
        }
        return stringRedisTemplate.opsForHash().hasKey(key, hashKey);
    }

}
