package com.wisedu.coeus.core.security;

import com.wisedu.coeus.core.ConstantStatic;
import com.wisedu.coeus.core.bo.LoginUser;
import com.wisedu.coeus.core.cache.CacheControl;
import com.wisedu.coeus.core.exception.AuthException;
import com.wisedu.coeus.debug.Logger;
import com.wisedu.coeus.debug.LoggerFactory;
import com.wisedu.coeus.helper.ApplicationHelper;
import com.wisedu.coeus.util.CookieUtils;
import com.wisedu.coeus.util.Strings;
import com.wisedu.coeus.util.UrlUtils;
import com.wisedu.coeus.web.AppConfig;
import com.wisedu.coeus.web.LocaleResolver;
import com.wisedu.coeus.web.LoginContext;
import com.wisedu.mooc.app.home.service.HomeService;
import com.wisedu.mooc.app.user.bo.UserInfo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 授权注解拦截器
 */
public class AuthInterceptor extends HandlerInterceptorAdapter {
    private static Logger LOGGER = LoggerFactory.getLogger(AuthInterceptor.class);


    @Autowired
    private HomeService homeService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (Strings.isEmpty(CookieUtils.getCookie(request, "moocvk"))) {
            CookieUtils.setCookie(response, "moocvk", UUID.randomUUID().toString().toLowerCase().replace("-", ""), 1576800000);
        }
        String url = request.getRequestURI();
        if (Strings.isNotEmpty(request.getQueryString())) {
            url = url.concat("?").concat(request.getQueryString());
        }
        url = url.substring(ApplicationHelper.getContextPath().length());
        request.setAttribute("RequestUrl", url);

        // 装载会话中的登录上下文
        LoginContext.setLoginUser(null);
        LoginContext.setRequest(request);
        LoginContext.setResponse(response);

        LoginUser loginUser = LoginContext.load();

        Locale locale = LocaleResolver.getLocale(request);
        loginUser.setUserLocale(locale);

        UserInfo userInfo = null;
        if (loginUser.getUserInfo() != null) {
            userInfo = (UserInfo) loginUser.getUserInfo();
        } else {
            if (!loginUser.getGuest()) {
                userInfo = loginUser.getUserInfo();
            }
        }

        // 自动获取权限
        LoginCheck loginCheck = null;
        HandlerMethod methodHandler = null;
        boolean allowAnonymous = false;
        if (handler instanceof HandlerMethod) {
            methodHandler = (HandlerMethod) handler;
            loginCheck = methodHandler.getMethodAnnotation(LoginCheck.class);
            if (loginCheck == null) {
                Object bean = methodHandler.getBean();
                loginCheck = bean.getClass().getAnnotation(LoginCheck.class);
            }
            if (loginCheck != null && loginCheck.value().length() >= 1 && loginCheck.value().equalsIgnoreCase("false")) {
                allowAnonymous = true;
            }
        }

        request.setAttribute("lUser", loginUser);
        request.setAttribute("uInfo", userInfo);
        if (methodHandler == null) {
            return true;
        }

        if (doCache(request, response, methodHandler)) {
            response.setStatus(304);
            clearLoginContext();
            return true;
        }

        if (allowAnonymous) {
            // 不要求登录验证
            return true;
        }

        if (loginUser.getGuest()) {
            // 需要验证，而当前是匿名访问，抛拒绝访问
            clearLoginContext();
            throw new AuthException(AuthException.ACCESS_DENIED, "Access Denied");
        }

        AuthCheck authCheck = methodHandler.getMethodAnnotation(AuthCheck.class);
        if (authCheck == null || (authCheck != null && authCheck.roles().length == 0 && authCheck.keys().length == 0 && authCheck.identities().length == 0)) {
            return true;
        }
        if (loginUser == null) {
            clearLoginContext();
            // 需要授权验证，但未登录
            throw new AuthException(AuthException.ACCESS_DENIED, "Access Denied");
        }
        boolean authPass = false;
        // 先判断身份是否通过
        if (authCheck != null && authCheck.identities().length > 0) {
            for (String identityKey : authCheck.identities()) {
                if (AuthHelper.hasAuthority(loginUser, identityKey, "identity")) {
                    authPass = true;
                    break;
                }
            }
        }

        // 判断角色是否通过
        if (!authPass && authCheck != null && authCheck.roles().length > 0) {
            for (String roleKey : authCheck.roles()) {
                if (AuthHelper.hasAuthority(loginUser, roleKey, authCheck.limitSite().equalsIgnoreCase("true") ? "role" : "role*")) {
                    authPass = true;
                    break;
                }
            }
        }

        // 在判断角色是否通过
        if (!authPass && authCheck != null && authCheck.keys().length > 0) {
            for (String funKey : authCheck.keys()) {
                if (AuthHelper.hasAuthority(loginUser, funKey, authCheck.limitSite().equalsIgnoreCase("true") ? "function" : "function*")) {
                    authPass = true;
                    break;
                }
            }
        }

        if (false == authPass) {
            // 验证未通过
            clearLoginContext();
            throw new AuthException(AuthException.ACCESS_DENIED, "Access Denied");
        }

        return true;
    }

    private boolean doCache(HttpServletRequest request, HttpServletResponse response, HandlerMethod methodHandler) throws ParseException {
        // 緩存
        CacheControl cacheControl = methodHandler.getMethodAnnotation(CacheControl.class);
        if (cacheControl != null && cacheControl.method().length >= 1) {
            boolean cache = false;
            int i;
            for (i = 0; i < cacheControl.method().length; i++) {
                if (cacheControl.method()[i].equalsIgnoreCase(request.getMethod())) {
                    cache = true;
                    break;
                }
            }
            if (cache) {
                String expireVal = Strings.trimToEmpty(cacheControl.expire());
                long expires = 1800;
                int expireValLength = expireVal.length();
                if (expireValLength > 0) {
                    String unitVal = expireVal.substring(expireVal.length() - 1);
                    expireVal = expireVal.substring(0, expireVal.length() - 1);
                    long e = Long.parseLong(expireVal);

                    if (unitVal.equalsIgnoreCase("m")) {
                        expires = e * 60;
                    } else if (unitVal.equalsIgnoreCase("h")) {
                        expires = e * 3600;
                    } else if (unitVal.equalsIgnoreCase("d")) {
                        expires = e * 86400;
                    } else {
                        expires = e;
                    }
                }

                String eTag;
                long lastModified = 0;
                Date dateLastMod;

                // GMT格式日期
                DateFormat df = new SimpleDateFormat("EEE,dd MMM yyyy hh:mm:ss z", new DateFormatSymbols(Locale.US));
                df.setTimeZone(TimeZone.getTimeZone("GMT"));

                Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
                dateLastMod = calendar.getTime();
                lastModified = dateLastMod.getTime();

                String headerIfModSince = request.getHeader("If-Modified-Since");
                String headIfNoneMatch = request.getHeader("If-None-Match");
                long reqMatch = 0;

                if (headerIfModSince != null) {
                    Date reqSince = df.parse(headerIfModSince);
                    reqMatch = df.parse(headerIfModSince).getTime();
                }

                if (headIfNoneMatch != null) {
                    reqMatch = Long.parseLong(headIfNoneMatch);
                }

                // 未达到超时时间
                if (lastModified - reqMatch <= expires * 1000) {
                    return true;
                }

                // 进行缓存的处理
                eTag = lastModified + "";

                response.setHeader("Last-Modified", df.format(dateLastMod));
                response.setHeader("ETag", eTag);

                response.setHeader("Cache-Control", "public");  //   HTTP/1.1
                response.setHeader("Pragma", "Pragma");         //   HTTP/1.0
                response.addHeader("Cache-Control", "max-age=" + expires);
            }
        }
        return false;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
        clearLoginContext();
    }

    /**
     * 清除登录上下文
     */
    private void clearLoginContext() {
        LoginContext.saveSession();
        LoginContext.clear();
    }
}

