package com.diagbot.service.impl;

import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.diagbot.entity.JwtStore;
import com.diagbot.service.TokenService;
import com.diagbot.util.DateUtil;
import com.diagbot.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.Map;

/**
 * @Description: Token验证类 实现
 * @author: gaodm
 * @time: 2018/10/29 13:34
 */
@Slf4j
@Service
public class TokenServiceImpl implements TokenService {

    @Autowired
    @Qualifier("redisTemplateForToken")
    RedisTemplate redisForToken;

    private byte[] serializeKey(Object o) {
        return redisForToken.getKeySerializer().serialize(o);
    }

    private byte[] serializeValue(Object o) {
        return redisForToken.getValueSerializer().serialize(o);
    }

    private Object deserializeValue(byte[] b) {
        return redisForToken.getValueSerializer().deserialize(b);
    }

    private byte[] getUserTokenKey(String userId) {
        String userTokensFormat = "user_tokens_%s";
        return serializeKey(String.format(userTokensFormat, userId));
    }

    /**
     * 创建token
     *
     * @param token 用户token
     * @return
     */
    @Override
    public Boolean createToken(JwtStore token) {
        DecodedJWT jwt = JwtUtil.decodedJWT(token.getRefreshToken());
        Map<String, Claim> claims = jwt.getClaims();
        String userId = claims.get("user_id").asInt().toString();
        Date expDate = claims.get("exp").asDate();
        final byte[] redis_key = getUserTokenKey(userId);
        redisForToken.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                //获取旧的
                byte[] bytes = connection.get(redis_key);
                //删除旧的
                if (bytes != null) {
                    connection.del(bytes);
                }
                //设置新的
                connection.setEx(
                        redis_key,
                        (expDate.getTime() - DateUtil.now().getTime()) / 1000,
                        serializeValue(token)
                );
                return true;
            }
        });
        return true;
    }

    /**
     * 验证token是否有效
     *
     * @param token 待验证的token
     * @param type  1:accessToken,2:refreshToken
     * @return
     */
    @Override
    public Boolean verifyToken(String token, Integer type) {
        Boolean res = false;
        if (null == token) {
            return false;
        }
        String userId = JwtUtil.getUserId(token);
        //从redis中取出
        final byte[] redis_key = getUserTokenKey(userId);
        JwtStore tokenStore = (JwtStore) redisForToken.execute(new RedisCallback<JwtStore>() {
            @Override
            public JwtStore doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] bytes = connection.get(redis_key);
                if (bytes == null) {
                    return null;
                }
                return (JwtStore) deserializeValue(bytes);
            }
        });

        if (null != tokenStore){
            if (type == 1){
                if (null != tokenStore.getAccessToken() && tokenStore.getAccessToken().equals(token)) {
                    res = true;
                }
            }

            if (type == 2){
                if (null != tokenStore.getRefreshToken() && tokenStore.getRefreshToken().equals(token)) {
                    res = true;
                }
            }
        }

        return res;
    }

    /**
     *  删除用户token
     * @param userId 用户ID
     * @return 删除是否成功
     */
    @Override
    public Boolean deleteToken(String userId){
        final byte[] redis_key = getUserTokenKey(userId);
        Long l = (Long) redisForToken.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(redis_key);
            }
        });
        return l > 0;
    }
}
