package com.apobates.forum.member.storage.ehcache;

import com.apobates.forum.member.entity.MemberGroupEnum;
import com.apobates.forum.member.entity.MemberRoleEnum;
import com.apobates.forum.member.entity.MemberStatusEnum;
import com.apobates.forum.member.storage.MetaConfig;
import com.apobates.forum.member.storage.OnlineMemberStorage;
import com.apobates.forum.member.storage.cookie.CookieMetaConfig;
import com.apobates.forum.member.storage.core.MemberSessionBean;
import com.apobates.forum.member.storage.core.MemberSessionBeanConverter;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.CookieUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.jcache.JCacheCacheManager;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;
/**
 * 使用Ehcache存储会员在线信息
 * @see //www.ehcache.org/documentation/3.5/107.html
 * @author xiaofanku
 * @since 20201001
 */
public class OnlineMemberEhcacheStorage implements OnlineMemberStorage {
    @Autowired
    private CookieMetaConfig metaConfig;
    @Autowired
    private JCacheCacheManager cacheManager;
    private final static String EHC_STORE = "olMemberSession";
    private final static Logger logger = LoggerFactory.getLogger(OnlineMemberEhcacheStorage.class);

    @PostConstruct
    public void init(){
        logger.info("OnlineMemberEhcacheStorage postconstruct init");
        MutableConfiguration<String,MemberSessionBean> configuration =
                new MutableConfiguration<String,MemberSessionBean>()
                        .setTypes(String.class, MemberSessionBean.class)
                        .setStoreByValue(true)
                        .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_DAY));
        try {
            cacheManager.getCacheManager().createCache(EHC_STORE, configuration);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @PreDestroy
    public void close(){
        logger.info("OnlineMemberEhcacheStorage postconstruct close");
        try {
            cacheManager.getCacheManager().destroyCache(EHC_STORE);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void store(MemberSessionBean memberSessionBean, HttpServletRequest request, HttpServletResponse response) {
        String passStub = rndStub();
        Optional<String> afterPart = hash(passStub, memberSessionBean.getIpAddr()); //前缀:hashValue
        //过期日期
        LocalDateTime expireDate = LocalDateTime.now().plusDays(1L);
        Map<String,String> val = MemberSessionBeanConverter.toMap(memberSessionBean, expireDate);
        if (afterPart.isPresent() && null != val && !val.isEmpty()) {
            String ecKey = metaConfig.getName() + ":" + afterPart.get();
            CacheManager cm = cacheManager.getCacheManager();
            Cache<String, MemberSessionBean> cache = cm.getCache(EHC_STORE, String.class, MemberSessionBean.class);
            try{
                cache.put(ecKey, memberSessionBean);
            }catch(Exception e){
                e.printStackTrace();
                if(logger.isDebugEnabled()){
                    logger.debug("[OM][EHC][save]"+e.getMessage(), e);
                }
            }
            serializeCookie(passStub, expireDate, request, response, metaConfig.getName(), metaConfig.getPath(), metaConfig.getDomain(), metaConfig.isHttps());
        }
    }

    @Override
    public void delete(HttpServletRequest request, HttpServletResponse response) {
        Optional<String> afterPart = getEhcacheKey(request); //前缀:hashValue
        if (afterPart.isPresent()) {
            String ecKey = metaConfig.getName() + ":" + afterPart.get();
            CacheManager cm = cacheManager.getCacheManager();
            Cache<String, MemberSessionBean> cache = cm.getCache(EHC_STORE, String.class, MemberSessionBean.class);
            try{
                cache.remove(ecKey);
            }catch(Exception e){
                e.printStackTrace();
                if(logger.isDebugEnabled()){
                    logger.debug("[OM][EHC][del]"+e.getMessage(), e);
                }
            }
            expireCookie(request, response, metaConfig.getName(), metaConfig.getPath(), metaConfig.getDomain());
        }
    }

    private Optional<String> getEhcacheKey(HttpServletRequest request){
        Cookie cookie = CookieUtils.queryCookie(request, metaConfig.getName()).orElse(null);
        if (null == cookie) {
            return Optional.empty();
        }
        String passStub = cookie.getValue();
        return hash(passStub, Commons.getRequestIp(request)); //前缀:hashValue
    }

    @Override
    public Optional<MemberSessionBean> getInstance(HttpServletRequest request, String sentinel) {
        Optional<String> afterPart = getEhcacheKey(request); //前缀:hashValue
        if (!afterPart.isPresent()) {
            return Optional.empty();
        }
        String ecKey = metaConfig.getName() + ":" + afterPart.get();
        CacheManager cm = cacheManager.getCacheManager();
        Cache<String, MemberSessionBean> cache = cm.getCache(EHC_STORE, String.class, MemberSessionBean.class);
        try{
            MemberSessionBean msb = cache.get(ecKey);
            return Optional.ofNullable(msb);
        }catch(Exception e){
            e.printStackTrace();
            if(logger.isDebugEnabled()){
                logger.debug("[OM][EHC][get]"+e.getMessage(), e);
            }
        }
        return Optional.empty();
    }

    @Override
    public void refresh(HttpServletRequest request, HttpServletResponse response, MemberStatusEnum status, MemberGroupEnum group, MemberRoleEnum role) {
        Optional<String> afterPart = getEhcacheKey(request); //前缀:hashValue
        if (!afterPart.isPresent()) {
            return;
        }
        String ecKey = metaConfig.getName() + ":" + afterPart.get();
        CacheManager cm = cacheManager.getCacheManager();
        Cache<String, MemberSessionBean> cache = cm.getCache(EHC_STORE, String.class, MemberSessionBean.class);
        try{
            MemberSessionBean msb = cache.get(ecKey);
            cache.put(ecKey, msb.refact(group, role, status));
        }catch(Exception e){
            e.printStackTrace();
            if(logger.isDebugEnabled()){
                logger.debug("[OM][EHC][refresh]"+e.getMessage(), e);
            }
        }
    }

    @Override
    public boolean isSupportRevival() {
        return true;
    }

    @Override
    public MetaConfig getMetaConfig() {
        return metaConfig;
    }
    /**
     * 序列化会员信息到Cookie中
     *
     * @param cookieValue cookie中保存的值
     * @param expireDateTime cookie到期的日期
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieSymbol cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     * @param isHttps work on HTTPS/true,false work on http
     */
    protected void serializeCookie(
            String cookieValue,
            LocalDateTime expireDateTime,
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieSymbol,
            String cookiePath,
            String cookieDomain,
            boolean isHttps) {
        try {
            CookieUtils.serializeCookie(cookieValue, expireDateTime, request, response, cookieSymbol, cookiePath, cookieDomain, isHttps);
        } catch (IllegalStateException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[OM][CS]write cookie fail, reason: " + e.getMessage(), e);
            }
        }
    }

    /**
     * 清空Cookie中的会员信息
     *
     * @param request Http请求对象
     * @param response Http响应对象
     * @param cookieSymbol cookie的Key
     * @param cookiePath cookie的路径
     * @param cookieDomain cookie的域名
     */
    protected void expireCookie(
            HttpServletRequest request,
            HttpServletResponse response,
            String cookieSymbol,
            String cookiePath,
            String cookieDomain) {
        CookieUtils.expireCookie(request, response, cookieSymbol, cookiePath, cookieDomain);
    }
    /**
     *
     * @param stub 长度12位
     * @param ipAddr ipv4
     * @return
     */
    private Optional<String> hash(String stub, String ipAddr){ //redis key的长度
        if(!Commons.isNotBlank(stub) || !Commons.isNotBlank(ipAddr)){
            return Optional.empty();
        }
        return Optional.of(stub);
    }

    private String rndStub(){
        return java.util.UUID.randomUUID().toString().substring(24);
    }
}