package cn.superdesk.uniorder.common.security;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.superdesk.uniorder.common.enums.login.SysLoginTypeEnum;
import cn.superdesk.uniorder.common.exception.UnifiedOrderRuntimeException;
import cn.superdesk.uniorder.common.exception.constants.ExceptionMsg;
import cn.superdesk.uniorder.common.json.AnJsonUtils;
import cn.superdesk.uniorder.common.numerical.AnDateHelper;
import cn.superdesk.uniorder.common.numerical.AnIntegerUtils;
import cn.superdesk.uniorder.common.numerical.AnLongUtils;
import cn.superdesk.uniorder.common.numerical.AnStringUtils;
import cn.superdesk.uniorder.common.security.dto.LoginSysUser;
import cn.superdesk.uniorder.common.security.dto.TokenVo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * @Description: JwtUtil
 * @Author: linjinyu 补充 修改
 * @CreateDate: 4/29/20 5:51 PM
 * @UpdateUser: linjinyu
 * @UpdateDate: 4/29/20 5:51 PM
 * @UpdateRemark: 修改内容
 * @Version: 1.0
 */
@Slf4j
public class JwtUtil {

	/**
	 * 私钥
	 */
	private static final String base64EncodedSecretKey = "cY-!I8aQY0(;Pz/@Slove#3";

	/**
	 * 后端 root
	 */
	public static final String FORGED_BACKEND_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJzYWxlaWQiOm51bGwsInZpcnRJZHMiOm51bGwsIm91";
	public static final String FORGED_MALL_TOKEN    = "eyJhbGciOiJIUzI1NiJ9.eyJzYWxlSWQiOm51bGwsImZ1bGxuYW1lIjoi5p6X6L-b6ZuoIiwidmlydElkcyI6bnVsbCwib3V0TG9naW4iOiIwIiwidXNlck5hbWUiOiJsaW5qeSIsInJvbGVUeXBlIjoiMSIsIm9jSWQiOm51bGwsImV4cCI6MTYzNTk5MjU5OSwidXNlcklkIjoiNjQiLCJpYXQiOjE2MzU5MDYxOTl9.dsbpbx4X_O3haEuq3gqWNjveIjTKkAzuwKkBcj4EqHo";

	@Getter
	private static LoginSysUser loginSysUserAnonymous;
	@Getter
	private static LoginSysUser loginSysUserBackendRoot;
	@Getter
	private static LoginSysUser loginSysUserForgedMall;

	static {

		// outLogin:-1-匿名;0-统一订单;1-服务之窗;2-福利商城_侨城汇;@link SysLoginTypeEnum
		loginSysUserAnonymous = LoginSysUser.builder()
				.sysUserId(-1L)
				.sysUserName("anonymous")
				.fullName(SysLoginTypeEnum.匿名.getDesc())
				.outLogin(SysLoginTypeEnum.匿名.getKey())
				.build();
		loginSysUserBackendRoot = LoginSysUser.builder()
				.sysUserId(1L)
				.sysUserName("root")
				.fullName(SysLoginTypeEnum.统一订单.getDesc())
				.outLogin(SysLoginTypeEnum.统一订单.getKey())
				.build();
		loginSysUserForgedMall = LoginSysUser.builder()
				.sysUserId(2L)
				.sysUserName("superdeskmall")
				.fullName(SysLoginTypeEnum.福利商城_侨城汇.getDesc())
				.outLogin(SysLoginTypeEnum.福利商城_侨城汇.getKey())
				.build();
	}

	/**
	 * TODO 东西是在create 的时候就丢进去了
	 *
	 * @param userId
	 * @param expirateTime
	 * @param userName
	 * @param roleType
	 * @param ocId
	 * @param virtIds
	 * @param saleId
	 * @param outLogin
	 * @return
	 */
	public static TokenVo createToken(
			String userId,
			String userName,
			String realName,
			String roleType,
			String ocId,
			String virtIds,
			String saleId,
			int expirateTime,
			Integer outLogin) {

		TokenVo result = new TokenVo();

		result.setSubject(userId);
		result.setUsername(userName);

		result.setRoletype(roleType);

		result.setCreateTime(DateUtil.date());
		result.setExpireTime(getDeltaTime(result.getCreateTime(), expirateTime));

		Map<String, Object> claims = new HashMap<>();

		claims.put("userId", userId);
		claims.put("userName", result.getUsername());
		claims.put("fullname", realName);


		claims.put("roleType", result.getRoletype());

		claims.put("ocId", ocId);

		claims.put("virtIds", virtIds);

		claims.put("saleId", saleId);
		claims.put("outLogin", outLogin);

		String token = Jwts.builder()
				//.setSubject(userId)
				.setClaims(claims)
				.setIssuedAt(result.getCreateTime())
				.setExpiration(result.getExpireTime())
				.signWith(SignatureAlgorithm.HS256, base64EncodedSecretKey)
				.compact();

		result.setToken(token);

		return result;
	}

	/**
	 * 检查token,只要不正确就会抛出异常 TODO 将要弃用
	 *
	 * @param token
	 * @return
	 * @throws UnifiedOrderRuntimeException
	 */
	@Deprecated
	public static String checkTokenAndGetUserId(String token) throws UnifiedOrderRuntimeException {

//		log.info("checkTokenAndGetUserId : token : {}", token);

		if (FORGED_BACKEND_TOKEN.equals(token)) {
			return loginSysUserBackendRoot.getSysUserId().toString();
		} else if (FORGED_MALL_TOKEN.equals(token)) {
			return loginSysUserForgedMall.getSysUserId().toString();
		}

		String userId = null;

		try {
			final Claims claims = Jwts.parser().setSigningKey(base64EncodedSecretKey).parseClaimsJws(token).getBody();

			log.info(AnJsonUtils.toJson(claims));

			userId = claims.get("userId").toString();

			log.info("response : userId : {}", userId);

		} catch (ExpiredJwtException e1) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_EXPIRED.getCode(), ExceptionMsg.TOKEN_EXPIRED.getMsg());
		} catch (Exception e) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_VERIFY_ERROR.getCode(), ExceptionMsg.TOKEN_VERIFY_ERROR.getMsg());
		}

		log.info("##################### JwtUtil checkTokenAndGetUserId  end  #####################");

		return userId;
	}


	/**
	 * 弃用 mark by linjinyu at 20210312
	 *
	 * @param token
	 * @return
	 * @throws UnifiedOrderRuntimeException
	 */
	@Deprecated
	public static String checkTokenAndGetUserName(String token) throws UnifiedOrderRuntimeException {

//		log.info("checkTokenAndGetUserName : token : {}", token);

		if (FORGED_BACKEND_TOKEN.equals(token)) {
			return loginSysUserBackendRoot.getSysUserName();
		} else if (FORGED_MALL_TOKEN.equals(token)) {
			return loginSysUserForgedMall.getSysUserName();
		}

		String userName = null;

		try {
			final Claims claims = Jwts.parser().setSigningKey(base64EncodedSecretKey).parseClaimsJws(token).getBody();

			log.info(AnJsonUtils.toJson(claims));

			userName = claims.get("userName").toString();

			log.info("response : userName : {}", userName);


		} catch (ExpiredJwtException e1) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_EXPIRED.getCode(), ExceptionMsg.TOKEN_EXPIRED.getMsg());
		} catch (Exception e) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_VERIFY_ERROR.getCode(), ExceptionMsg.TOKEN_VERIFY_ERROR.getMsg());
		}

		return userName;
	}

	public static LoginSysUser getLoginSysUser(/* request.getHeader("token") */String token) {

//		log.info("getLoginSysUser : token : {}", token);

		if (FORGED_BACKEND_TOKEN.equals(token)) {
			return loginSysUserBackendRoot;
		} else if (FORGED_MALL_TOKEN.equals(token)) {
			return loginSysUserForgedMall;
		}

		LoginSysUser loginSysUser = null;

		try {

			final Claims claims = Jwts.parser().setSigningKey(base64EncodedSecretKey).parseClaimsJws(token).getBody();

			loginSysUser = LoginSysUser.builder()
					.sysUserId(AnLongUtils.getLong(claims.get("userId")))
					.sysUserName(AnStringUtils.getString(claims.get("userName")))
					.fullName(AnStringUtils.getString(claims.get("fullname")))
					.roleType(AnLongUtils.getLong(claims.get("roleType")))
					// 0-统一订单;1-服务之窗;2-商城;
					.outLogin(AnIntegerUtils.getInteger(claims.get("outLogin")))
					.ocId(AnLongUtils.getLong(claims.get("outLogin")))

					.virtIds(AnStringUtils.getString(claims.get("virtIds")))
					.saleId(AnLongUtils.getLong(claims.get("saleId")))

					.build();

		} catch (ExpiredJwtException e1) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_EXPIRED.getCode(), ExceptionMsg.TOKEN_EXPIRED.getMsg());
		} catch (Exception e) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_VERIFY_ERROR.getCode(), ExceptionMsg.TOKEN_VERIFY_ERROR.getMsg());
		}

		return loginSysUser;

	}

	public static LoginSysUser getLoginSysUser(HttpServletRequest request) {

		String token = request.getHeader("token");

//		log.info("getLoginSysUser : token : {}", token);

		if (StrUtil.isBlank(token)) {
			return loginSysUserAnonymous;
		} else if (FORGED_BACKEND_TOKEN.equals(token)) {
			return loginSysUserBackendRoot;
		} else if (FORGED_MALL_TOKEN.equals(token)) {
			return loginSysUserForgedMall;
		}

		LoginSysUser loginSysUser = null;

		try {

			final Claims claims = Jwts.parser().setSigningKey(base64EncodedSecretKey).parseClaimsJws(token).getBody();

			loginSysUser = LoginSysUser.builder()
					.sysUserId(AnLongUtils.getLong(claims.get("userId")))
					.sysUserName(AnStringUtils.getString(claims.get("userName")))
					.fullName(AnStringUtils.getString(claims.get("fullname")))
					.roleType(AnLongUtils.getLong(claims.get("roleType")))
					// 0-统一订单;1-服务之窗;2-商城;
					.outLogin(AnIntegerUtils.getInteger(claims.get("outLogin")))
					.ocId(AnLongUtils.getLong(claims.get("outLogin")))

					.virtIds(AnStringUtils.getString(claims.get("virtIds")))
					.saleId(AnLongUtils.getLong(claims.get("saleId")))

					.build();


		} catch (ExpiredJwtException e1) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_EXPIRED.getCode(), ExceptionMsg.TOKEN_EXPIRED.getMsg());
		} catch (Exception e) {
//			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_VERIFY_ERROR.getCode(), ExceptionMsg.TOKEN_VERIFY_ERROR.getMsg());
		}

		return loginSysUser;

	}

	/**
	 * @Date:17-12-12 下午6:21
	 * @Author:root
	 * @Desc:检查token,只要不正确就会抛出异常
	 **/
	@Deprecated
	public static Map<String, Object> getTokenVo(String token) throws UnifiedOrderRuntimeException {

		Map<String, Object> map = new HashMap<>();

		try {
			final Claims claims = Jwts.parser().setSigningKey(base64EncodedSecretKey).parseClaimsJws(token).getBody();

			map.put("userId", claims.get("userId"));
			map.put("userName", claims.get("userName"));

			map.put("roleType", claims.get("roleType"));

			// 0-统一订单;1-服务之窗;2-商城;
			map.put("outLogin", claims.get("outLogin"));

			map.put("ocId", claims.get("ocId"));
			map.put("virtIds", claims.get("virtIds"));
			map.put("saleId", claims.get("saleId"));

		} catch (ExpiredJwtException e1) {
			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_EXPIRED.getCode(), ExceptionMsg.TOKEN_EXPIRED.getMsg());
		} catch (Exception e) {
			throw new UnifiedOrderRuntimeException(ExceptionMsg.TOKEN_VERIFY_ERROR.getCode(), ExceptionMsg.TOKEN_VERIFY_ERROR.getMsg());
		}

		return map;
	}


	private static Date getDeltaTime(Date now, int timeGap) {
		return AnDateHelper.getDateByOffsetSeconds(now, timeGap);
	}
}
