package com.gitee.sop.admin.common.context;

import com.auth0.jwt.interfaces.Claim;
import com.gitee.sop.admin.common.config.Configs;
import com.gitee.sop.admin.common.enums.ConfigKeyEnum;
import com.gitee.sop.admin.common.exception.ErrorTokenException;
import com.gitee.sop.admin.common.exception.JwtErrorException;
import com.gitee.sop.admin.common.exception.JwtExpiredException;
import com.gitee.sop.admin.common.exception.LoginFailureException;
import com.gitee.sop.admin.common.manager.UserCacheManager;
import com.gitee.sop.admin.common.user.User;
import com.gitee.sop.admin.common.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;

/**
 * @author tanghc
 */
@Slf4j
public class UserContext {

    public static final String HEADER_AUTHORIZATION = "Authorization";
    public static final String JWT_PREFIX = "Bearer ";


    private static Supplier<String> tokenGetter = () -> {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) (RequestContextHolder.getRequestAttributes());
        if (requestAttributes == null) {
            return null;
        }
        HttpServletRequest request = requestAttributes.getRequest();
        return getToken(request);
    };

    public static void setTokenGetter(Supplier<String> tokenGetter) {
        UserContext.tokenGetter = tokenGetter;
    }

    /**
     * 获取当前登录用户id
     *
     * @return 返回id, 没有返回null
     */
    public static Long getUserId() {
        return Optional.ofNullable(getUser()).map(User::getUserId).orElse(null);
    }

    /**
     * 获取当前登录用户
     *
     * @return 返回当前登录用户，没有返回null
     */
    public static User getUser() {
        String token = tokenGetter.get();
        try {
            return getUser(token);
        } catch (ErrorTokenException e) {
            throw new LoginFailureException();
        }
    }

    /**
     * 获取当前登录用户
     *
     * @return 返回当前登录用户，没有返回null
     */
    public static User getUser(HttpServletRequest request) {
        String token = getToken(request);
        try {
            return getUser(token);
        } catch (ErrorTokenException e) {
            throw new LoginFailureException();
        }
    }

    public static String getToken(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        String token = request.getHeader(HEADER_AUTHORIZATION);
        if (StringUtils.hasText(token) && token.startsWith(JWT_PREFIX)) {
            return token.substring(JWT_PREFIX.length());
        }
        return token;
    }


    /**
     * 获取登录用户
     *
     * @param token 格式：<userId>:<jwt>
     * @return 返回token对应的用户，没有返回null
     */
    private static User getUser(String token) throws ErrorTokenException {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        String secret = Configs.getValue(ConfigKeyEnum.JWT_SECRET);
        Map<String, Claim> data;
        // verify jwt
        try {
            data = JwtUtil.verifyJwt(token, secret);
        } catch (JwtExpiredException | JwtErrorException e) {
            log.error("jwt verify failed, token:{}, message:{}", token, e.getMessage(), e);
            throw new ErrorTokenException();
        }
        Claim id = data.get("id");
        long userId = NumberUtils.toLong(id.asString(), 0);
        if (userId == 0) {
            return null;
        }
        return SpringContext.getBean(UserCacheManager.class).getUser(userId);
    }


    @Deprecated
    public static Locale getLocale() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            return request.getLocale();
        } catch (Exception e) {
            return Locale.SIMPLIFIED_CHINESE;
        }
    }
}
