package com.lantone.common.service.impl;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.lantone.common.dto.JwtStore;
import com.lantone.common.exception.Asserts;
import com.lantone.common.service.SysTokenService;
import com.lantone.common.util.DateUtil;
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.List;
import java.util.Map;

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

    @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 = decodedJWT(token.getRefreshToken());
        Map<String, Claim> claims = jwt.getClaims();
        String userId = claims.get("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;
    }
    private DecodedJWT decodedJWT(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt;
        } catch (JWTDecodeException var2) {
            var2.printStackTrace();
            Asserts.fail("token解析失败！");
        }
        return null;
    }

    /**
     * 验证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;
        }
        DecodedJWT jwt = decodedJWT(token);
        Map<String, Claim> claims = jwt.getClaims();
        Claim claim = (Claim)claims.get("id");
        String userId = claim.asInt().toString();
        //从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;
    }

    /**
     * 批量删除用户token
     *
     * @param userIds 用户列表
     * @return 删除是否成功
     */
    @Override
    public Boolean deleteBatchToken(List<Long> userIds) {
        Long l = (Long) redisForToken.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.openPipeline();
                Long cnt = 0L;
                for (Long userId : userIds) {
                    byte[] redis_key = getUserTokenKey(userId.toString());
                    connection.del(redis_key);
                    cnt++;
                }
                connection.closePipeline();
                return cnt;
            }
        });
        return l > 0;
    }

    /**
     * 获取用户jwt
     *
     * @param userId 用户ID
     * @return jwt信息
     */
    @Override
    public JwtStore getToken(String userId) {
        JwtStore tokenStore = null;
        //从redis中取出
        final byte[] redis_key = getUserTokenKey(userId);
        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);
            }
        });
        return tokenStore;
    }

    @Override
    public String getUserIDByToken(String token) {
        DecodedJWT jwt = decodedJWT(token);
        Map<String, Claim> claims = jwt.getClaims();
        return claims.get("id").asInt().toString();
    }
}
