package openjoe.smart.sso.server.manager;

import openjoe.smart.sso.base.constant.BaseConstant;
import openjoe.smart.sso.base.entity.LifecycleManager;
import openjoe.smart.sso.base.entity.TokenUser;
import openjoe.smart.sso.base.util.HttpUtils;
import openjoe.smart.sso.server.entity.CodeContent;
import openjoe.smart.sso.server.entity.TokenContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.*;

/**
 * 调用凭证AccessToken管理抽象
 *
 * @author Joe
 */
public abstract class AbstractTokenManager implements LifecycleManager<TokenContent> {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * accessToken超时时效
     */
    private int accessTokenTimeout;

    /**
     * refreshToken时效和登录超时时效保持一致
     */
    private int refreshTokenTimeout;

    protected final ExecutorService executorService;

    public AbstractTokenManager(int accessTokenTimeout, int refreshTokenTimeout, int threadPoolSize) {
        this.accessTokenTimeout = accessTokenTimeout;
        this.refreshTokenTimeout = refreshTokenTimeout;
        this.executorService = new ThreadPoolExecutor(
                threadPoolSize,
                threadPoolSize,
                3L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1000)
        );
    }

    /**
     * 通过AccessToken获取
     *
     * @param accessToken
     * @return
     */
    public abstract TokenContent getByAccessToken(String accessToken);

    /**
     * 通过TGT移除
     *
     * @param tgt
     */
    public abstract void removeByTgt(String tgt);

    /**
     * 创建AccessToken
     *
     * @param tc
     * @return
     */
    public TokenContent create(TokenContent tc) {
        return create(tc.getUserId(), tc.getLogoutUri(), tc);
    }

    /**
     * 创建AccessToken
     *
     * @param userId
     * @param codeContent
     * @return
     */
    public TokenContent create(Long userId, String logoutUri, CodeContent codeContent) {
        String accessToken = "AT-" + UUID.randomUUID().toString().replaceAll("-", "");
        String refreshToken = "RT-" + UUID.randomUUID().toString().replaceAll("-", "");
        TokenContent tc = new TokenContent(accessToken, refreshToken, userId, logoutUri, codeContent.getTgt(), codeContent.getClientId());
        create(refreshToken, tc);
        return tc;
    }

    /**
     * 提交删除Token任务
     *
     * @param refreshTokenSet
     */
    protected void submitRemoveToken(Set<String> refreshTokenSet) {
        // 用于存储所有的Future对象，以便后续等待所有任务完成
        List<Future<?>> futures = new ArrayList<>();

        refreshTokenSet.forEach(refreshToken -> {
            // 发起客户端退出请求，提交任务到线程池并获取Future对象
            Future<?> future = executorService.submit(() -> {
                try {
                    processRemoveToken(refreshToken);
                } catch (Exception e) {
                    logger.error("执行删除Token操作出现异常", e);
                }
            });
            futures.add(future);
        });

        // 等待所有的请求任务都完成
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (Exception e) {
                logger.error("执行删除Token任务出现异常", e);
            }
        }
    }

    /**
     * 真正执行删除Token
     *
     * @param refreshToken
     */
    public abstract void processRemoveToken(String refreshToken);

    public abstract Map<String, Set<String>> getClientIdMapByTgt(Set<String> tgtSet);

    /**
     * 发起客户端退出请求
     *
     * @param redirectUri
     * @param accessToken
     */
    protected void sendLogoutRequest(String redirectUri, String accessToken) {
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put(BaseConstant.LOGOUT_PARAMETER_NAME, accessToken);
        HttpUtils.postHeader(redirectUri, headerMap);
    }

    public int getAccessTokenTimeout() {
        return accessTokenTimeout;
    }

    public void setAccessTokenTimeout(int accessTokenTimeout) {
        this.accessTokenTimeout = accessTokenTimeout;
    }

    public int getRefreshTokenTimeout() {
        return refreshTokenTimeout;
    }

    public void setRefreshTokenTimeout(int refreshTokenTimeout) {
        this.refreshTokenTimeout = refreshTokenTimeout;
    }
}
