package com.qmth.boot.core.cache.config;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.qmth.boot.core.cache.service.CacheConfigRegistration;
import com.qmth.boot.core.cache.service.CacheService;
import com.qmth.boot.core.cache.service.CustomizeCacheConfiguration;
import com.qmth.boot.core.cache.service.impl.CacheServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.lang.Nullable;

import java.time.Duration;

/**
 * 自定义缓存配置，缺省启用Caffeine作为单实例缓存实现
 */
@Configuration
@EnableCaching
@ComponentScan("com.qmth.boot.core.cache")
public class CacheAutoConfiguration {

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

    @Bean
    @Primary
    @ConditionalOnMissingBean(name = "cacheManager")
    public CacheManager cacheManager(CacheManager localCacheManager) {
        return localCacheManager;
    }

    @Bean
    public CacheService localCacheService(CacheManager localCacheManager) {
        return new CacheServiceImpl(localCacheManager);
    }

    @Bean
    public CacheService cacheService(CacheManager cacheManager) {
        return new CacheServiceImpl(cacheManager);
    }

    @Bean
    public CacheManager localCacheManager(CacheProperties cacheProperties,
            @Nullable CustomizeCacheConfiguration customizeCacheConfiguration) {
        final CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        //默认设置
        cacheManager.setAllowNullValues(cacheProperties.isAllowNullValue());
        cacheManager.setCaffeine(buildCaffeineConfig(cacheProperties.getExpireAfterWrite()));
        //自定义缓存设置
        if (customizeCacheConfiguration != null) {
            customizeCacheConfiguration.register(new CacheConfigRegistration() {

                @Override
                public CacheConfigRegistration setCacheConfig(String name, Duration expireAfterWrite) {
                    log.info("Customize cache configuration: name={}, expireAfterWrite={}", name,
                            expireAfterWrite.toString());
                    cacheManager.registerCustomCache(name, buildCaffeineConfig(expireAfterWrite).build());
                    return this;
                }
            });
        }
        log.info("Local cache manager inited.");
        return cacheManager;
    }

    private Caffeine<Object, Object> buildCaffeineConfig(Duration expireAfterWrite) {
        //默认开启softValues
        Caffeine<Object, Object> caffeine = Caffeine.newBuilder().softValues().recordStats();
        //写入失效时长大于0时才启用
        if (expireAfterWrite.toMillis() > 0) {
            caffeine = caffeine.expireAfterWrite(expireAfterWrite);
        }
        return caffeine;
    }
}
