package com.muyu.common.cache.abs;

import com.muyu.common.cache.HashCache;
import com.muyu.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * @author DongZl
 * @description: hash缓存抽象类
 * @Date 2024-3-29 下午 07:40
 */
public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {

    @Autowired
    private RedisService redisService;

    /**
     * 编码
     *
     * @param key ID
     *
     * @return 键
     */
    @Override
    public String encode (K key) {
        return keyPre() + key;
    }

    /**
     * 编码
     * @param hashKey ID
     * @return 键
     */
    @Override
    public String encodeHashKey (HK hashKey) {
        return hashKey.toString();
    }

    /**
     * 通过Key获取所有的map
     * @param key 数据库键
     * @return 所有集合Map
     */
    @Override
    public Map<HK, HV> get (K key) {
        // 获取为null的情况
        Map<String, HV> cacheMap = redisService.getCacheMap(encode(key));
        if (cacheMap == null || cacheMap.isEmpty()){
            Map<HK, HV> dataMap = getData(key);
            if (dataMap != null && !dataMap.isEmpty()){
                cacheMap = encodeMap(dataMap);
            }else {
                cacheMap = encodeMap(defaultValue());
            }
            redisService.setCacheMap(encode(key), cacheMap);
        }
        return decodeMap(cacheMap);
    }

    /**
     * 通过键和hashKey获取数据库hashValue
     *
     * @param key     键
     * @param hashKey hash键
     *
     * @return hash值
     */
    @Override
    public HV get (K key, HK hashKey) {
        HV hashValue = redisService.getCacheMapValue(encode(key), encodeHashKey(hashKey));
        if (hashValue == null){
            HV dataValue = getData(key, hashKey);
            hashValue = dataValue != null ? dataValue : defaultHashValue();
            put(key, hashKey, hashValue);
        }
        return hashValue;
    }

    /**
     * 通过键和hashKey获取数据库hashValue
     *
     * @param key         键
     * @param hashKeyList hash键集合
     *
     * @return hash值
     */
    @Override
    public List<HV> get (K key, HK... hashKeyList) {
        List<String> encodeHashKeyList = Arrays.stream(hashKeyList).map(this::encodeHashKey).toList();
        return redisService.getMultiCacheMapValue(encode(key), encodeHashKeyList);
    }

    /**
     * 获取hash值集合
     *
     * @param key 键
     *
     * @return hash值集合
     */
    @Override
    public List<HV> getToList (K key) {
        Map<HK, HV> hkhvMap = get(key);
        return hkhvMap.values().stream().toList();
    }

    /**
     * 存储数据
     *
     * @param key redis键
     * @param map hashMap集合
     */
    @Override
    public void put (K key, Map<HK, HV> map) {
        redisService.setCacheMap(encode(key), encodeMap(map));
    }

    /**
     * 存储数据
     *
     * @param key      redis键
     * @param dataList 数据值
     * @param hashKey  hash键
     */
    @Override
    public void put (K key, List<HV> dataList, Function<HV, HK> hashKey) {
        Map<HK, HV> dataMap = new HashMap<>();
        dataList.forEach((data) -> dataMap.put(hashKey.apply(data), data));
        redisService.setCacheMap(encode(key), encodeMap(dataMap));
    }

    /**
     * 存储数据
     *
     * @param key       redis键
     * @param hashKey   hash键
     * @param hashValue hash值
     */
    @Override
    public void put (K key, HK hashKey, HV hashValue) {
        redisService.setCacheMapValue(encode(key), encodeHashKey(hashKey), hashValue);
    }

    /**
     * 通过redis键删除
     *
     * @param key hash键
     */
    @Override
    public void remove (K key) {
        redisService.deleteObject(encode(key));
    }

    /**
     * 通过redis键和hash键删除
     *
     * @param key     redis键
     * @param hashKey hash键
     */
    @Override
    public void remove (K key, HK hashKey) {
        redisService.deleteCacheMapValue(encode(key), encodeHashKey(hashKey));
    }

    /**
     * 判断redis中hashKey是否存在
     *
     * @param key     redis键
     * @param hashKey hash键
     */
    @Override
    public boolean hasKey (K key, HK hashKey) {
        return redisService.hashKey(encode(key), encodeHashKey(hashKey));
    }

    /**
     * 原始数据转编码数据
     * @param dataMap 原始数据
     * @return 编码数据
     */
    private Map<String, HV> encodeMap(Map<HK, HV> dataMap){
        Map<String, HV> encodeDataMap = new HashMap<>();
        dataMap.forEach((hashKey, HashValue) -> encodeDataMap.put(encodeHashKey(hashKey), HashValue));
        return encodeDataMap;
    }

    /**
     * 编码数据转原始数据
     * @param encodeDataMap 编码数据
     * @return 原始数据
     */
    private Map<HK, HV> decodeMap(Map<String, HV> encodeDataMap){
        Map<HK, HV> dataMap = new HashMap<>();
        encodeDataMap.forEach((hashKey, hashValue) -> dataMap.put(decodeHashKey(hashKey), hashValue));
        return dataMap;
    }

    /**
     * 通过键获取所有的hash数据
     * @param key 键
     * @return
     */
    public abstract Map<HK, HV> getData(K key);

    /**
     * 通过缓存键和hash键获取hash值
     * @param key 缓存键
     * @param hashKey hash键
     * @return hash值
     */
    public abstract HV getData(K key, HK hashKey);

    /**
     * 默认值
     */
    public abstract Map<HK, HV> defaultValue();
    public abstract HV defaultHashValue();
}
