package com.games.gateway.client.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.games.gateway.client.SsoTouchSessionRpcClient;
import com.games.gateway.config.GatewayBizConfig;
import com.games.gateway.domain.Session;
import com.games.gateway.enums.BizResultCode;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class SsoTouchSessionRpcClientImpl implements SsoTouchSessionRpcClient, InitializingBean {

    /**
     * URL地址
     */
    private static final String URI_TOUCH_SESSION = "/api/auth/touch-session";

    /**
     * 本地缓存
     */
    private LoadingCache<String, Session> localSessionCache;

    /**
     * 过期时间
     */
    private long localSessionAliveMills = 1 * 1000;
    /**
     * 压缩session. 不保存tk信息. 这样可以减少cache的大小
     */
    private boolean isSessionCompacted = true;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private GatewayBizConfig globalConfig;

    /**
     * 正常返回则表示该session处于活跃状态；抛出异常表示该会话已过期。
     * <p/>
     * LoadingCache.get，如果缓存中不存在，则调用load直接访问远程服务，并放入本地缓存。
     * 如果 load 抛出异常，get 会直接抛出 load 的异常
     * <p>
     *
     * @param token 用户会话TOKEN
     */
    public Session touchSession(final String token) {
        BizResultCode.NO_LOGIN.check(Strings.isNullOrEmpty(token));

        Session session = localSessionCache.get(token);
        BizResultCode.NO_LOGIN.check(session == null);

        if (session != null) {
            session.setToken(token);
        }
        return session;
    }


    @Override
    public void afterPropertiesSet() {
        localSessionCache = Caffeine.newBuilder()
                .expireAfterWrite(localSessionAliveMills, TimeUnit.MILLISECONDS)
                .initialCapacity(10_000)
                .maximumSize(1_000_000)
                .recordStats()
                .build(key -> {
                    Map<String, Object> param = new HashMap<>(2);
                    param.put("token", key);
                    // 从SSO拿到session对象并保存到本地缓存，如果缓存无效或过期，则抛出异常
                    String response = restTemplate.postForObject(globalConfig.getBearHost() + URI_TOUCH_SESSION,
                            param, String.class);
                    JSONObject jsonObject = JSON.parseObject(response);
                    String sessionJson = jsonObject.getString("content");
                    Session session = null;
                    if (sessionJson != null) {
                        session = JSON.parseObject(sessionJson, Session.class);
                        if (isSessionCompacted) {
                            session.setToken(null);
                        }
                    }
                    return session;
                });
    }
}
