package cc.smtweb.framework.core.session;

import cc.smtweb.framework.core.cache.SessionCache;
import cc.smtweb.framework.core.cache.SessionCacheFactory;
import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.common.SwConsts;
import cc.smtweb.framework.core.exception.BizException;
import cc.smtweb.framework.core.mvc.controller.IEditor;
import cc.smtweb.framework.core.mvc.realm.exception.UnauthenticatedException;
import cc.smtweb.framework.core.util.NumberUtil;
import cc.smtweb.framework.core.util.SpringUtil;
import cc.smtweb.framework.core.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 〈session工具类〉
 *
 * @author kevin
 * @since 1.0.0
 */
public class SessionUtil {
    //登录令牌header名
    private final static String KEY_HEADER_SESSION = "Auth-Token";
    //url参数名
    private final static String KEY_PARAM_SESSION = "auth_token";

    //防止重复提交指纹-header名
    private final static String KEY_HEADER_FP_KEY = "Fpk_Token";
    private final static String KEY_HEADER_FP_VAL = "Fpv_Token";
    //url参数名
    private final static String KEY_PARAM_FP_KEY = "fpk";
    private final static String KEY_PARAM_FP_VAL = "fpv";
    //不需要校验登录的url
    public static List<String> notLoginUrl = new ArrayList<>();
    /*//不需要切换数据源的url，强制用主库
    public static List<String> notSetDbUrl = new ArrayList<>();*/
    //不需要验证合法性的url
    public static List<String> notCheckUrlValid = new ArrayList<>();
    //不算用户交互操作的url，用于管理session失效
    public static List<String> notInterActiveUrl = new ArrayList<>();
    static {
        notLoginUrl.add("/");
        notLoginUrl.add("/api/getVerifyCode");
        notLoginUrl.add("/api/bpm/auth/login");
        notLoginUrl.add("/api/bpm/auth/mappingLogin");
        notLoginUrl.add("/fs/static/*");
        notLoginUrl.add("/static/*");
        notLoginUrl.add("/custdb/*.do");
        notLoginUrl.add("/attach/upload.do");
        notLoginUrl.add("/attach/download.do");
        notLoginUrl.add("/attach/uploadBase64.do");

        notCheckUrlValid.add("/db/*.do");
        notCheckUrlValid.add("/custdb/*.do");
        notCheckUrlValid.add("/cust/common/httpService/service.do");
        notCheckUrlValid.add("/attach/upload.do");
        notCheckUrlValid.add("/attach/uploadBase64.do");

    }

    private static void addUrl(String url, List<String> list) {
        list.add(url);
    }

    //是否不需要url校验
    private static boolean isMatchUrl(String lasturi, List<String> list) {
        if (list.contains(lasturi)) return true;
        for (String s: list){
            if (StringUtil.match(s, lasturi)) return true;
        }
        return false;
    }

    //是否不需要登录验证
    public static boolean isNoLogin(String lasturi) {
        return isMatchUrl(lasturi, notLoginUrl);
    }

    //增加不需要登录的Uri
    public static void addNoLoginUri(String uri) {
        addUrl(uri, notLoginUrl);
    }

    /*//是否不需要切换库
    public static boolean isNoSetDb(String lasturi) {
        return isMatchUrl(lasturi, notSetDbUrl);
    }

    //增加不需要登录的Uri
    public static void addNoSetDbUrl(String uri) {
        addUrl(uri, notSetDbUrl);
    }*/

    //是否不需要url校验
    public static boolean isNoCheckValid(String lasturi) {
        return isMatchUrl(lasturi, notCheckUrlValid);
    }

    //增加不需要校验合法性的Uri
    public static void addNoCheckValidUri(String uri) {
        addUrl(uri, notCheckUrlValid);
    }

    //不算用户交互操作
    public static boolean isNoInterActive(String lasturi) {
        return isMatchUrl(lasturi, notInterActiveUrl);
    }

    //不算用户交互操作
    public static void addNoInterActiveUri(String uri) {
        addUrl(uri, notInterActiveUrl);
    }

    private SessionUtil() {
    }

    public static Object getSession() {
        return RequestContextHolder.currentRequestAttributes().getAttribute(IEditor.USER_SESSION, 0);
    }

    public static String readToken(HttpServletRequest request) {
        return readToken(request, KEY_HEADER_SESSION, KEY_PARAM_SESSION);
    }

    public static String readFingerKey(HttpServletRequest request) {
        return readToken(request, KEY_HEADER_FP_KEY, KEY_PARAM_FP_KEY);
    }

    public static int readFingerVal(HttpServletRequest request) {
        return NumberUtil.getIntIgnoreErr(readToken(request, KEY_HEADER_FP_VAL, KEY_PARAM_FP_VAL));
    }

    private static String readToken(HttpServletRequest request, String headerName, String paramName) {
        String token = request.getHeader(headerName);

        if (token == null) {
            token = request.getParameter(paramName);
        }
        return token;
    }

    //无效登录异常
    public static void throwNoLogin() {
        throw new BizException(SwConsts.ErrorCode.NO_LOGIN);
    }

    public static void checkSession(HttpServletRequest request) {
        //校验登录
        String lasturi = getUriLast(request.getContextPath(), request.getServletPath());
        String token = readToken(request);
        UserSession us = null;
        if (StringUtils.isNotBlank(token)) {
            us = RedisManager.getInstance().get(token, UserSession.class);
            // token延时
            SessionManager sessionManager = SpringUtil.getBean(SessionManager.class);
            sessionManager.flush(token);
        }
        //校验登录
        final boolean isNologin ="design".equals(token) || isNoLogin(lasturi);
        if (!isNologin) {
            if (us == null) {
                throwNoLogin();
            }

            request.setAttribute(IEditor.USER_TOKEN, token);
            request.setAttribute(IEditor.USER_SESSION, us);
        }

        String fpk = readFingerKey(request);
        if (!StringUtil.isEmpty(fpk)) {
            //校验指纹
            String fpKey = us != null ? String.valueOf(us.getUserId()): request.getSession(true).getId();
            int fpv = readFingerVal(request);
            if (fpv > 0) {
                SessionCache cache = SessionCacheFactory.getInstance().getCache(KEY_PARAM_FP_KEY, 1200L);
                AtomicInteger ai = cache.get(fpKey + "_" + fpk);
                int bv = ai != null ? ai.get(): 0;
                if (fpv != bv) {
                    throw new BizException(SwConsts.ErrorCode.TOKEN_INVALID, "指纹错误，请勿重复提交！");
                }
            }
        }
    }

    //设置指纹
    public static void setFingerValue(HttpServletRequest request, R r) {
        String fpk = readFingerKey(request);
        if (!StringUtil.isEmpty(fpk)) {
            UserSession us = (UserSession) request.getAttribute(IEditor.USER_SESSION);
            String fpKey = us != null ? String.valueOf(us.getUserId()): request.getSession(true).getId();
            SessionCache cache = SessionCacheFactory.getInstance().getCache(KEY_PARAM_FP_KEY, 1200L);

            AtomicInteger ai = cache.get(fpKey + "_" + fpk);
            if (ai == null) {
                ai = new AtomicInteger(0);
                cache.put(fpKey + "_" + fpk, ai);
            }
            r.put(KEY_PARAM_FP_KEY, fpk);
            r.put(KEY_PARAM_FP_VAL, ai.incrementAndGet());
        }
    }

    /**
     * 截取请求名称，如重 /${contextPath}/framework/loginpage.do中截出loginpage.do;
     *
     * @param contextPath /dfp
     * @param uri         /dfp/framework/loginpage.do
     */
    private static String getUriLast(String contextPath, String uri) {
        int index = uri.indexOf(contextPath+"/");
        if (index < 0) return uri;
        if (index > 1) return uri;
        return uri.substring(index + contextPath.length());
    }

//    private static Session getShiroSession() {
//        Session session = SecurityUtils.getSubject().getSession();
//        if (session == null) {
//            throw new SmartException(ExceptionMessage.NO_LOGIN_ERROR);
//        }
//        return session;
//    }

//    public static String getSid() {
//        return getShiroSession().getId().toString();
//    }

//    public static UserSession getSession() {
//        Object session = getShiroSession().getAttribute(USER_SESSION);
//        if (session == null) {
//            throw new SmartException(ExceptionMessage.NO_LOGIN_ERROR);
//        }
//        UserSession userSession = (UserSession) session;
//        if (userSession.isChange()) {
//            throw new SmartException(ExceptionMessage.USER_PERMISSION_CHANGE);
//        }
//        return userSession;
//    }
//
//    public static void updateSession(UserSession userSession) {
//        getShiroSession().setAttribute(USER_SESSION, userSession);
//    }

//    public static String login(String userName, UserSession userSession) {
//        // 通过 shiro 登录验证
//        Subject subject = SecurityUtils.getSubject();
//        try {
//            // 统一默认密码登录
//            subject.login(new UsernamePasswordToken(userName, CommonUtil.INIT_PASSWORD));
//        } catch (ShiroException ex) {
//            throw new SmartException(ExceptionMessage.USER_ERROR);
//        }
//
//        Session session = subject.getSession();
//        if (session == null) {
//            throw new SmartException(ExceptionMessage.NO_LOGIN_ERROR);
//        }
//
//        session.setAttribute(USER_SESSION, userSession);
//        return session.getId().toString();
//    }
}
