package com.huimv.video.dhicc.test.schedule;

import com.alibaba.fastjson.JSON;
import com.dahuatech.icc.oauth.model.v202010.OauthRefreshTokenResponse;
import com.dahuatech.icc.oauth.profile.GrantType;
import com.huimv.video.dhicc.test.config.*;
import com.huimv.video.dhicc.test.request.OauthRefreshTokenRequest;
import com.huimv.video.dhicc.test.token.LocalTokenStore;
import com.huimv.video.dhicc.test.token.RedisTokenStore;
import com.huimv.video.dhicc.test.token.Token;
import com.huimv.video.dhicc.test.token.TokenStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
 * 定时任务类：更新token
 * TODO: 当前版本暂未支持自动刷新token
 */
public class UpdateTokenSchedule {
    public static final Logger logger = LoggerFactory.getLogger(UpdateTokenSchedule.class);

    //token存储方式：这里采用本地缓存，如需修改，可替换为Redis或MySQL存储
    public static final TokenStore tokenStore = new LocalTokenStore();
    private static final Object lock = new Object();
    private static UpdateTokenSchedule instance;
    public static final Map<String, OauthConfigBaseInfo> httpConfigMap = new ConcurrentHashMap<String, OauthConfigBaseInfo>();
    public static final Map<String, OauthConfigBaseInfo02> httpConfigMap2 = new ConcurrentHashMap<String, OauthConfigBaseInfo02>();
    public static final Map<String, OauthConfigBaseInfo03> httpConfigMap3 = new ConcurrentHashMap<String, OauthConfigBaseInfo03>();
    //2分钟刷新一次过期token
    private static final Long expireTime = 120 * 1000L;
    //线程刷新token和保活
    private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
    //初始延迟
    private static final Long INITIAL_DELAY = 0L;
    //执行任务延迟
    private static final Long FRESH_TOKEN_INTERVAL = 2L;

    private UpdateTokenSchedule() {
        //定时刷新token
//        executorService.scheduleWithFixedDelay(new Thread()() -> {
//            logger.info("UpdateTokenSchedule,UpdateTokenSchedule,Start refreshing token...");
//            updateToken();
//        }, INITIAL_DELAY, FRESH_TOKEN_INTERVAL, TimeUnit.SECONDS);
    }
//
//    public static UpdateTokenSchedule getInstance(){
//        if(instance == null){
//            synchronized (lock){
//                if(instance == null){
//                    instance = new UpdateTokenSchedule();
//                }
//            }
//        }
//        return instance;
//    }

    public void updateToken() {
        Object o = tokenStore.getToken();
        if (o != null) {
            dealToken(o);
        } else {
            logger.info("Token story is empty, update token task return.");
        }
    }

    public void dealToken(Object o) {
      /*  if (tokenStore instanceof MysqlTokenStore) {
            logger.info("Update token from mysql.");
            MysqlTokenStore mysqlTokenStore = (MysqlTokenStore) tokenStore;
            Map<String, Token> tokens = (Map<String, Token>) o;
            if (CollectionUtils.isEmpty(tokens)) {
                logger.info("Mysql token story is empty, update token task return.");
                return;
            }
            dealExpiredToken(mysqlTokenStore, tokens);
        }*/

        if (tokenStore instanceof RedisTokenStore) {
            logger.info("Update token from redis.");
            RedisTokenStore redisTokenStore = (RedisTokenStore) tokenStore;
            Map<String, String> tokens = (Map<String, String>) o;
            if (CollectionUtils.isEmpty(tokens)) {
                logger.info("Redis token story is empty, update token task return.");
                return;
            }
            dealExpiredToken(redisTokenStore, tokens);
        }

        if (tokenStore instanceof LocalTokenStore) {
            logger.info("Update token from local story.");
            LocalTokenStore localTokenStore = (LocalTokenStore) tokenStore;
            Map<String, Token> tokenMap = (Map<String, Token>) o;
            if (CollectionUtils.isEmpty(tokenMap)) {
                logger.info("Local map token story is empty, update token task return.");
                return;
            }
            dealExpiredToken(localTokenStore, tokenMap);
        }
    }

  /*  public void dealExpiredToken(MysqlTokenStore mysqlTokenStore, Map<String, Token> tokenMap) {
        for (Map.Entry entry : tokenMap.entrySet()) {
            String key = (String) entry.getKey();
            Token token = (Token) entry.getValue();
            Long ttl = token.getTtl();

            //token剩余过期时间小于等于2分钟时，更新token
            if (ttl - System.currentTimeMillis() <= expireTime) {
                logger.info("UpdateTokenSchedule,dealExpiredToken, key:{}", key);
                token = refreshToken(key, token.getRefresh_token());
                if (token == null) {
                    return;
                }
                //生成用于缓存的key
                OauthConfigBaseInfo oauthConfigBaseInfo = httpConfigMap.get(key);
                String tokenKey = getKey(oauthConfigBaseInfo);
                if (StringUtils.isEmpty(tokenKey)) {
                    return;
                }
                //将token进行缓存
                mysqlTokenStore.updateToken(tokenKey, token);
            }
        }

    }*/

    public void dealExpiredToken(LocalTokenStore localTokenStore, Map<String, Token> tokenMap) {
        for (Map.Entry entry : tokenMap.entrySet()) {
            String key = (String) entry.getKey();
            Token token = (Token) entry.getValue();
            Long ttl = token.getTtl();

            //token剩余过期时间小于等于2分钟时，更新token
            if (ttl - System.currentTimeMillis() <= expireTime) {
                logger.info("UpdateTokenSchedule,dealExpiredToken, key:{}", key);
                token = refreshToken(key, token.getRefresh_token());
                if (token == null) {
                    return;
                }
                //生成用于缓存的key
                OauthConfigBaseInfo oauthConfigBaseInfo = httpConfigMap.get(key);
                String tokenKey = getKey(oauthConfigBaseInfo);
                if (StringUtils.isEmpty(tokenKey)) {
                    return;
                }
                //将token进行缓存
                localTokenStore.tokenMap.put(tokenKey, token);
            }
        }
    }

    public void dealExpiredToken(RedisTokenStore redisTokenStore, Map<String, String> tokens) {
        for (Map.Entry entry : tokens.entrySet()) {
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            Token token = JSON.parseObject(value, Token.class);
            Long ttl = token.getTtl();

            //token剩余过期时间小于等于2分钟时，更新token
            if (ttl - System.currentTimeMillis() <= expireTime) {
                logger.info("UpdateTokenSchedule,dealExpiredToken, key:{}", key);
                token = refreshToken(key, token.getRefresh_token());
                if (token == null) return;
                //生成用于缓存的key
                OauthConfigBaseInfo oauthConfigBaseInfo = httpConfigMap.get(key);
                String tokenKey = getKey(oauthConfigBaseInfo);
                if (StringUtils.isEmpty(tokenKey)) return;
                //将token进行缓存
                redisTokenStore.updateToken(tokenKey, token);
            }
        }
    }

    public Token refreshToken(String key, String refreshToken) {
        Token newToken = null;
        OauthConfigBaseInfo oauthConfigBaseInfo = httpConfigMap.get(key);

        //目前只支持密码模式
        if (oauthConfigBaseInfo.getGrantType() == GrantType.password) {
            OauthRefreshTokenRequest oauthRefreshTokenRequest = new OauthRefreshTokenRequest(oauthConfigBaseInfo, refreshToken);
            OauthRefreshTokenResponse oauthRefreshTokenResponse = oauthRefreshTokenRequest.doAction();
            if (oauthRefreshTokenResponse.isSuccess()) {
                OauthRefreshTokenResponse.IccReFreshToken iccReFreshToken = oauthRefreshTokenResponse.getData();
                newToken = new Token();
                BeanUtils.copyProperties(iccReFreshToken, newToken);
                //设置过期时间
                newToken.setTtl(System.currentTimeMillis() + iccReFreshToken.getExpires_in() * 1000);
                return newToken;
            } else {
                logger.error("UpdateTokenSchedule,refreshToken, error:{}", key);
                return null;
            }
        }
        //客户端模式，暂不作处理
        else if (oauthConfigBaseInfo.getGrantType() == GrantType.client_credentials) {

        }
        return newToken;
    }

    public static String getKey(OauthConfigBaseInfo oauthConfigBaseInfo) {
        if (oauthConfigBaseInfo.getGrantType() == GrantType.password) {
            String host = oauthConfigBaseInfo.getHttpConfigInfo().getHost();
            String grantType = oauthConfigBaseInfo.getGrantType().name();
            String userName = null;
            OauthConfigUserPwdInfoTest01 oauthConfigUserPwdInfo = (OauthConfigUserPwdInfoTest01) oauthConfigBaseInfo;
            userName = oauthConfigUserPwdInfo.getUsername();
            String key = host + "_" + grantType + "_" + userName;
            logger.info("UpdateTokenSchedule,getKey, key:{}", key);
            return key;
        }
        return null;
    }

    public static String getKey2(OauthConfigBaseInfo02 oauthConfigBaseInfo) {
        if (oauthConfigBaseInfo.getGrantType() == GrantType.password) {
            String host = oauthConfigBaseInfo.getHttpConfigInfo().getHost();
            String grantType = oauthConfigBaseInfo.getGrantType().name();
            String userName = null;
            OauthConfigUserPwdInfoTest02 oauthConfigUserPwdInfo = (OauthConfigUserPwdInfoTest02) oauthConfigBaseInfo;
            userName = oauthConfigUserPwdInfo.getUsername();
            String key = host + "_" + grantType + "_" + userName;
            logger.info("UpdateTokenSchedule,getKey, key:{}", key);
            return key;
        }
        return null;
    }

    public static String getKey3(OauthConfigBaseInfo03 oauthConfigBaseInfo) {
        if (oauthConfigBaseInfo.getGrantType() == GrantType.password) {
            String host = oauthConfigBaseInfo.getHttpConfigInfo().getHost();
            String grantType = oauthConfigBaseInfo.getGrantType().name();
            String userName = null;
            OauthConfigUserPwdInfoTest03 oauthConfigUserPwdInfo = (OauthConfigUserPwdInfoTest03) oauthConfigBaseInfo;
            userName = oauthConfigUserPwdInfo.getUsername();
            String key = host + "_" + grantType + "_" + userName;
            logger.info("UpdateTokenSchedule,getKey, key:{}", key);
            return key;
        }
        return null;
    }
}
