package security;

import com.google.common.base.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Created by nolan on 11/11/2016.
 * description:
 */
@Service
public class RedisTokenManager extends AbstractTokenManager {
    private final String GLOBAL_TOKEN = "GLOBAL_TOKEN";

    /**
     * Redis中Key的前缀
     */
    private static final String REDIS_KEY_PREFIX = "MANAGER_AUTHORIZATION_KEY_";

    /**
     * Redis中Token的前缀
     */
    private static final String REDIS_TOKEN_PREFIX = "MANAGER_AUTHORIZATION_TOKEN_";

    /**
     * Redis中账户的前缀
     */
    private static final String REDIS_ACCOUNT_PREFIX = "MANAGER_AUTHORIZATION_ACCOUNT_";

    @Autowired
    private RedisTemplate<String, String> redisTemplate;


    /**
     * 踢出此账号下的所有登陆
     */
    @Override
    public void delMultiRelationshipByKey(String key) {
        if(Strings.isNullOrEmpty(key))
            return;

        //获取所有登陆TOKEN
        Set<String> tokenSet = this.smembers(formatAccountKey(key));

        if(!CollectionUtils.isEmpty(tokenSet)){
            //删除这些TOKEN
            delete(tokenSet);
            //删除对应关系
            delete(formatAccountKey(key));
        }
    }

    @Override
    protected void delSingleRelationshipByKey(String key) {
        String token = getToken(key);
        if (token != null) {
            delete(formatKey(key), formatToken(token));
        }
    }

    /**
     * 根据token删掉关系
     * @param token
     */
    @Override
    public void delRelationshipByToken(String token) {

        final String formatToken = formatToken(token);

        if (singleTokenWithUser) {

            String key = getKeyFromToken(token);
            delete(formatKey(key), formatToken);

        } else {

            //删除对应关系中的token
            srem(formatAccountKey(get(formatToken)),formatToken);
            //删除token
            delete(formatToken);
        }
    }

    /**
     * 创建单点登陆的登陆关系
     * @param key
     * @param token
     */
    @Override
    protected void createSingleRelationship(String key, String token) {
        String oldToken = get(formatKey(key));
        if (oldToken != null) {
            delete(formatToken(oldToken));
        }
        set(formatToken(token), key, tokenExpireSeconds);
        set(formatKey(key), token, tokenExpireSeconds);
    }

    /**
     * 创建多点登陆的登陆关系
     * @param key
     * @param token
     */
    @Override
    protected void createMultipleRelationship(String key, String token) {
        set(formatToken(token), key, tokenExpireSeconds);
        //添加到账户KEY对应的SET中
        sadd(formatAccountKey(key), formatToken(token), tokenExpireSeconds);
    }

    @Override
    protected String getKeyByToken(String token) {
        return get(formatToken(token));
    }

    @Override
    protected String getTokenByKey(String key) {
        return get(formatKey(key));
    }

    /**
     * 操作后刷新登陆关系时长
     * @param key
     * @param token
     */
    @Override
    protected void flushExpireAfterOperation(String key, String token) {
        if (singleTokenWithUser) {
            expire(formatKey(key), tokenExpireSeconds);
        }
        expire(formatToken(token), tokenExpireSeconds);
        //刷新对应关系时长
        expire(formatAccountKey(get(formatToken(token))), tokenExpireSeconds);
    }

    private String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    private String set(String key, String value, int expireSeconds) {
        redisTemplate.opsForValue().set(key, value);
        redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS);
        return value;
    }

    /**
     * 添加value 对于的KEY set
     */
    private Long sadd(String key, String value, int expireSeconds){

        Long num = redisTemplate.opsForSet().add(key, value);

        if(0 != num){
            redisTemplate.expire(key, expireSeconds, TimeUnit.SECONDS);
        }

        return num;
    }

    /**
     * 删除set中的某个
     */
    private Long srem(String key, String value){
        return redisTemplate.opsForSet().remove(key,value);
    }

    /**
     * 获取set中所有元素
     */
    private Set<String> smembers(String key){
        return redisTemplate.opsForSet().members(key);
    }

    private void expire(String key, int seconds) {
        redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
    }

    private void delete(String... keys) {
        redisTemplate.delete(Arrays.asList(keys));
    }

    private void delete(Collection<String> collection){
        redisTemplate.delete(collection);
    }

    private String getToken(String key) {
        return get(formatKey(key));
    }

    private String formatKey(String key) {
        return REDIS_KEY_PREFIX.concat(key);
    }

    private String formatToken(String token) {
        return REDIS_TOKEN_PREFIX.concat(token);
    }

    private String formatAccountKey(String key){
        return REDIS_ACCOUNT_PREFIX.concat(key);
    }
}