package com.ssyf.plugs.cache.cache;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.ssyf.common.core.util.SimpleLockerUtil;
import com.ssyf.plugs.cache.properties.CacheProperties;
import org.springframework.cache.support.AbstractValueAdaptingCache;
import org.springframework.cache.support.NullValue;

import java.security.SecureRandom;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * 缓存
 * @author huwei
 * @date 2022/11/29 13:49
 */
public abstract class AbstractCache extends AbstractValueAdaptingCache{

    private static final int DAY_MINUTES = 24 * 60;

    protected CacheProperties cacheProperties;

    private String uniqueKey = IdUtil.simpleUUID();

    /**
     * 多级缓存的名称
     */
    private String name;


    protected AbstractCache(String name , CacheProperties cacheProperties) {
        super(cacheProperties.isNullValue());
        this.name = name;
        this.cacheProperties = cacheProperties;
    }

    protected long getExpire(String cacheName ,Object key){
        long expire = this.cacheProperties.getExpire();
        if(this.cacheProperties.isRandomExpire()){
            int minute = new SecureRandom().nextInt(DAY_MINUTES);
            long randomExpire = TimeUnit.MINUTES.toSeconds(minute);
            expire = randomExpire > expire ? randomExpire : expire + randomExpire;
        }
        return expire;
    }

    @Override
    public <T> T get(Object key, Callable<T> valueLoader) {
        Object value = lookup(key);
        if (ObjectUtil.isNotEmpty(value)) {
            return (T) value;
        }
        Lock lock = SimpleLockerUtil.getLocker(key.toString() ,AbstractCache.class.getSimpleName() ,"get");
        lock.lock();
        try {
            //二次判断
            value = lookup(key);
            if (ObjectUtil.isNotEmpty(value)) {
                return (T) value;
            }
            value = valueLoader.call();
            Object storeValue = toStoreValue(value);
            put(key, storeValue);
            return (T) value;
        } catch (Exception e) {
            throw new ValueRetrievalException(key, valueLoader, e);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        Object oldValue;
        Lock lock = SimpleLockerUtil.getLocker(key.toString() ,AbstractCache.class.getSimpleName() ,"putIfAbsent");
        lock.lock();
        try {
            oldValue = lookup(key);
            if (ObjectUtil.isEmpty(oldValue)) {
                put(key, value);
            }
        } finally {
            lock.unlock();
        }
        return toValueWrapper(oldValue);
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this;
    }

    @Override
    protected Object fromStoreValue(Object storeValue) {
        if (super.isAllowNullValues() && (storeValue == NullValue.INSTANCE || storeValue instanceof NullValue)) {
            return null;
        }
        return storeValue;
    }

    /**
     * 清除多级缓存值
     * @param key
     */
    public void clearLocal(Object key){
        this.evict(key);
    }

    public String getUniqueKey(){
        return this.uniqueKey;
    }
}
