/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.core.secure.utils;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.concurrent.TimeUnit;

import org.springblade.core.secure.properties.JwtProperties;
import org.springframework.data.redis.core.RedisTemplate;

import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

/**
 * Jwt工具类
 *
 * @author Chill
 */
public class JwtUtil {

	/**
	 * token基础配置
	 */
	public static String BEARER = "bearer";
	public static Integer AUTH_LENGTH = 7;

	/**
	 * token保存至redis的key
	 */
	private static final String REFRESH_TOKEN_CACHE = "blade:refreshToken";
	private static final String TOKEN_CACHE = "blade:token";
	private static final String TOKEN_KEY = "token:state:";

	/**
	 * jwt配置
	 */
	private static JwtProperties jwtProperties;

	/**
	 * redis工具
	 */
	private static RedisTemplate<String, Object> redisTemplate;

	public static JwtProperties getJwtProperties() {
		return jwtProperties;
	}

	public static void setJwtProperties(JwtProperties properties) {
		if (JwtUtil.jwtProperties == null) {
			JwtUtil.jwtProperties = properties;
		}
	}

	public static RedisTemplate<String, Object> getRedisTemplate() {
		return redisTemplate;
	}

	public static void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
		if (JwtUtil.redisTemplate == null) {
			JwtUtil.redisTemplate = redisTemplate;
		}
	}

	/**
	 * 签名加密
	 */
	public static String getBase64Security() {
		return Base64.getEncoder().encodeToString(getJwtProperties().getSignKey().getBytes(StandardCharsets.UTF_8));
	}

	/**
	 * 获取请求传递的token串
	 *
	 * @param auth token
	 * @return String
	 */
	public static String getToken(String auth) {
		if ((auth != null) && (auth.length() > AUTH_LENGTH)) {
			String headStr = auth.substring(0, 6).toLowerCase();
			if (headStr.compareTo(BEARER) == 0) {
				auth = auth.substring(7);
			}
			return auth;
		}
		return null;
	}

	/**
	 * 解析jsonWebToken
	 *
	 * @param jsonWebToken token串
	 * @return Claims
	 */
	public static Claims parseJWT(String jsonWebToken) {
		try {
			return Jwts.parserBuilder()
				.setSigningKey(Base64.getDecoder().decode(getBase64Security())).build()
				.parseClaimsJws(jsonWebToken).getBody();
		} catch (Exception ex) {
			return null;
		}
	}

	/**
	 * 获取保存在redis的accessToken
	 *
	 * @param tenantId    租户id
	 * @param userId      用户id
	 * @param accessToken accessToken
	 * @return accessToken
	 */
	public static String getAccessToken(String tenantId, String userId, String accessToken) {
		return String.valueOf(getRedisTemplate().opsForValue().get(getAccessTokenKey(tenantId, userId, accessToken)));
	}


	/**
	 * 添加accessToken至redis
	 *
	 * @param tenantId    租户id
	 * @param userId      用户id
	 * @param accessToken accessToken
	 * @param expire      过期时间
	 */
	public static void addAccessToken(String tenantId, String userId, String accessToken, int expire) {
		getRedisTemplate().delete(getAccessTokenKey(tenantId, userId, accessToken));
		getRedisTemplate().opsForValue().set(getAccessTokenKey(tenantId, userId, accessToken), accessToken, expire, TimeUnit.SECONDS);
	}

	/**
	 * 删除保存在redis的accessToken
	 *
	 * @param tenantId 租户id
	 * @param userId   用户id
	 */
	public static void removeAccessToken(String tenantId, String userId) {
		removeAccessToken(tenantId, userId, null);
	}

	/**
	 * 删除保存在redis的accessToken
	 *
	 * @param tenantId    租户id
	 * @param userId      用户id
	 * @param accessToken accessToken
	 */
	public static void removeAccessToken(String tenantId, String userId, String accessToken) {
		getRedisTemplate().delete(getAccessTokenKey(tenantId, userId, accessToken));
	}

	/**
	 * 获取accessToken索引
	 *
	 * @param tenantId    租户id
	 * @param userId      用户id
	 * @param accessToken accessToken
	 * @return token索引
	 */
	public static String getAccessTokenKey(String tenantId, String userId, String accessToken) {
		String key = tenantId.concat(":").concat(TOKEN_CACHE).concat("::").concat(TOKEN_KEY);
		if (getJwtProperties().getSingle() || StrUtil.isEmpty(accessToken)) {
			return key.concat(userId);
		} else {
			return key.concat(accessToken);
		}
	}

	/**
	 * 获取保存在redis的refreshToken
	 *
	 * @param tenantId     租户id
	 * @param userId       用户id
	 * @param refreshToken refreshToken
	 * @return accessToken
	 */
	public static String getRefreshToken(String tenantId, String userId, String refreshToken) {
		return String.valueOf(getRedisTemplate().opsForValue().get(getRefreshTokenKey(tenantId, userId)));
	}

	/**
	 * 添加refreshToken至redis
	 *
	 * @param tenantId     租户id
	 * @param userId       用户id
	 * @param refreshToken refreshToken
	 * @param expire       过期时间
	 */
	public static void addRefreshToken(String tenantId, String userId, String refreshToken, int expire) {
		getRedisTemplate().delete(getRefreshTokenKey(tenantId, userId));
		getRedisTemplate().opsForValue().set(getRefreshTokenKey(tenantId, userId), refreshToken, expire, TimeUnit.SECONDS);
	}

	/**
	 * 删除保存在refreshToken的token
	 *
	 * @param tenantId 租户id
	 * @param userId   用户id
	 */
	public static void removeRefreshToken(String tenantId, String userId) {
		getRedisTemplate().delete(getRefreshTokenKey(tenantId, userId));
	}

	/**
	 * 获取refreshToken索引
	 *
	 * @param tenantId 租户id
	 * @param userId   用户id
	 * @return token索引
	 */
	public static String getRefreshTokenKey(String tenantId, String userId) {
		return tenantId.concat(":").concat(REFRESH_TOKEN_CACHE).concat("::").concat(TOKEN_KEY).concat(userId);
	}

}
