package org.springblade.common.auth.permission;

import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSON;
import com.wn.common.validator.Validator;
import org.springblade.common.auth.UserContext;
import org.springblade.common.auth.UserContextUtil;
import org.springblade.common.auth.support.AuthErrorConstants;
import org.springblade.common.auth.support.AuthUtil;
import com.wn.common.controller.BaseController;
import com.wn.common.datamodel.ResultDTO;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.modules.app.common.dto.UserDTO;
import org.springblade.modules.app.utils.ErrorMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;

@Component
@Slf4j
public class PermissionInterceptor implements HandlerInterceptor {

    @Value("${security.permission.check.flag:true}")
    @Setter
    //一般情况下不允许修改，只用于开发内部为调试方便而设置
    private boolean permissionCheckFlag = true;
    @Value("${spring.application.name}")
    private String applicationName;
    @Value("${cas.support.flag:false}")
    @Setter
    private boolean casSupportFlag;
    @Value("${spring.cas.server-name:idms-service}")
    @Setter
    private String serverName;

//    @Autowired
    private PermissionHandler permissionHandler = new PermissionHandler();

    private BladeRedis bladeRedis;

    public PermissionInterceptor(BladeRedis bladeRedis){
        this.bladeRedis = bladeRedis;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (!permissionCheckFlag) {
            return true;
        }
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        log.info("权限校验: 请求地址 = {}, 请求方法 = {}, 处理类 = {}, 处理方法 = {}", request.getRequestURI(), request.getMethod(),
                handlerMethod.getBean().getClass().getName(), handlerMethod.getMethod().getName());

        if (!(handlerMethod.getBean() instanceof BaseController)) {
            return true;
        }
        SessionIdContextUtil.clearCurrentSessionId();
        PermissionContextUtil.clearCurrentApplication();

        if (handlerMethod.hasMethodAnnotation(DisableLoginVerify.class)) {
            return true;
        }

        log.info("当前Session ID:{}, 缓存内容:{}", request.getSession().getId(), request.getSession().getAttributeNames());
        UserContext userContext = new UserContext();

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



        JWT jwt = JWTUtil.parseToken(token);
        Integer exp = (Integer) jwt.getPayload("exp");
        Long now = new Date().getTime()/1000;

		Validator.isTrue(exp > now, ErrorMsg.NEW(AuthErrorConstants.NOT_LOGIN.getCode(), AuthErrorConstants.NOT_LOGIN.getMessage()));

		if (exp < now) {
            response.setContentType(AuthUtil.CONTENT_TYPE_JSON);
            response.getWriter().write(createErrorResp(AuthErrorConstants.NOT_LOGIN, request.getServletPath()));
            response.getWriter().flush();
            return false;
        }

		Object object =jwt.getPayload("userDto");
		UserDTO userDTO = JSON.parseObject(object.toString(), UserDTO.class);

        if (userDTO == null) {
            log.error("请求地址:{}, 需要登录才能访问，但用户未登录，返回未登录错误...", request.getRequestURL());
            response.setContentType(AuthUtil.CONTENT_TYPE_JSON);
            response.getWriter().write(createErrorResp(AuthErrorConstants.NOT_LOGIN, request.getServletPath()));
            response.getWriter().flush();
            return false;
        }

        String url = "/" + applicationName + request.getServletPath();
        Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String handledUrl = AuthUtil.handleRequestUrl(url, pathVariables);
        log.info("处理前的URL是: {}, 处理后的URL是: {}", url, handledUrl);

        String code = permissionHandler.getPermissionCode(handledUrl, request.getMethod(),bladeRedis);
        if (code == null) {
            log.info("请求地址:{}, 系统没有访问权限控制, 返回成功！", url);
            return true;
        }

        if (userContext.isContainOperationCode(code, handledUrl)) {
            log.info("用户:{}, 请求地址: {}, 有访问权限, 返回成功！", userDTO.getUserName(), url);
            PermissionContextUtil.setCurrentApplication(userContext.getCurrentApplication(code, handledUrl));
            return true;
        } else {
            String requestInfo = getRequestInfo(handlerMethod);
            log.info("用户:{}, 请求地址:{}, 没有访问权限，返回无权限错误...请求详情:{}", userDTO.getUserName(), url, requestInfo);
            response.setContentType(AuthUtil.CONTENT_TYPE_JSON);
            response.getWriter().write(createErrorResp(AuthErrorConstants.NO_PERMISSION, request.getServletPath()));
            response.getWriter().flush();
            return false;
        }

    }

    public String createErrorResp(AuthErrorConstants error, String url) {
        ResultDTO<?> errorResult = new ResultDTO<>();
        errorResult.setCode(error.getCode());
        if (casSupportFlag && error.equals(AuthErrorConstants.NOT_LOGIN)) {
            errorResult.setMessage(serverName + url);
        } else {
            errorResult.setMessage(error.getMessage());
        }
        errorResult.setSuccess(false);
        String errorResp = JSON.toJSONString(errorResult);
        return errorResp;
    }

    private String getRequestInfo(HandlerMethod handlerMethod) {
        RequestMapping clazzMapping = handlerMethod.getBean().getClass().getAnnotation(RequestMapping.class);
        RequestMapping methodMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
        StringBuilder sb = new StringBuilder("url = ");
        if (clazzMapping != null && clazzMapping.value().length > 0) {
            sb.append(clazzMapping.value()[0]);
        }
        if (methodMapping != null && methodMapping.value().length > 0) {
            sb.append(methodMapping.value()[0]);
            String method = "";
            if (methodMapping.method() == null || methodMapping.method().length == 0) {
                method = "get";
            } else {
                method = methodMapping.method()[0].toString();
            }
            sb.append(" , method = " + method);
        }
        return sb.toString();
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        //logger.info("PermissionInterceptor.afterCompletion()...");
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mv)
            throws Exception {
        //logger.info("PermissionInterceptor.postHandle()...");
    }

}
