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

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 com.apobates.forum.utils.DateTimeUtils;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;

/**
 * 使用Redis存储会员在线信息,Cookie中存储的是票根
 * 支持redis 3.0.5
 * 
 * @author xiaofanku
 * @since 20200908
 */
public class OnlineMemberRedisStorage implements OnlineMemberStorage{
    @Autowired
    private CookieMetaConfig metaConfig;
    @Autowired
    private RedisTemplate<String, Object> template;
    private final static Logger logger = LoggerFactory.getLogger(OnlineMemberRedisStorage.class);

    @Override
    public void store(MemberSessionBean memberSessionBean, HttpServletRequest request, HttpServletResponse response) {
        if(!isContinue()){
            return;
        }
        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 redisKey = metaConfig.getName() + ":" + afterPart.get();
            template.opsForHash().putAll(redisKey, val);
            if (template.expireAt(redisKey, DateTimeUtils.toDate(expireDate))) {
                serializeCookie(passStub, expireDate, request, response, metaConfig.getName(), metaConfig.getPath(), metaConfig.getDomain(), metaConfig.isHttps());
            }
        }
    }

    @Override
    public void refresh(HttpServletRequest request, HttpServletResponse response, MemberStatusEnum status, MemberGroupEnum group, MemberRoleEnum role){
        Optional<String> afterPart = getRedisKey(request); //前缀:hashValue
        if (afterPart.isPresent()) {
            Map<String, String> data = new HashMap<>();
            data.put("status", status.getSymbol()+"");
            data.put("group", group.getSymbol()+"");
            data.put("role", role.getSymbol()+"");
            
            String redisKey = metaConfig.getName() + ":" + afterPart.get();
            //还有用原来的key吗
            template.opsForHash().putAll(redisKey, data);
        }
    }

    @Override
    public void delete(HttpServletRequest request, HttpServletResponse response) {
        Optional<String> afterPart = getRedisKey(request); //前缀:hashValue
        if (afterPart.isPresent()) {
            if (template.delete(metaConfig.getName() + ":" + afterPart.get())) {
                expireCookie(request, response, metaConfig.getName(), metaConfig.getPath(), metaConfig.getDomain());
            }
        }
    }

    @Override
    public Optional<MemberSessionBean> getInstance(HttpServletRequest request, String sentinel) {
        Map<String, String> data = getStoreStruct(request);
        if(data.isEmpty()){
            return Optional.empty();
        }
        return MemberSessionBeanConverter.toInstance(data, Commons.getRequestIp(request), sentinel);
    }
    
    @Override
    public boolean isSupportRevival() {
        return true;
    }
    
    @Override
    public MetaConfig getMetaConfig() {
        return metaConfig;
    }
    
    private boolean isContinue(){
        return null != template;
    }

    private Optional<String> getRedisKey(HttpServletRequest request){
        if(!isContinue()){
            return Optional.empty();
        }
        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
    }

    private Map<String, String> getStoreStruct(HttpServletRequest request){
        Optional<String> afterPart = getRedisKey(request); //前缀:hashValue
        if (afterPart.isPresent()) {
            String redisKey = metaConfig.getName() + ":" + afterPart.get();
            HashOperations<String, String, String> operation = template.opsForHash();
            return operation.entries(redisKey);
        }
        return Collections.emptyMap();
    }
    /**
     * 序列化会员信息到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 UUID.randomUUID().toString().substring(24);
    }
}