package cn.com.capcloud.core.system.security;

import cn.com.capcloud.api.action.service.ApiService;
import cn.com.capcloud.ccui.core.common.json.JackJSON;
import cn.com.capcloud.ccui.core.common.json.JackJSONObject;
import cn.com.capcloud.core.system.exception.CustomException;
import cn.com.capcloud.core.util.RequestBodyUtils;
import cn.com.capcloud.core.system.role.RoleService;
import cn.com.capcloud.core.system.user.User;
import cn.com.capcloud.core.system.user.UserService;
import org.apache.http.Consts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;

@Component
public class JwtTokenFilter extends OncePerRequestFilter {

  private JwtTokenProvider jwtTokenProvider;

  public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
    this.jwtTokenProvider = jwtTokenProvider;
  }
  @Value("${server.servlet.context-path}")
  private  String contextPath;
  @Resource
  private AntPathMatcher antPathMatcher;
  @Resource
  private RoleService roleService;
  @Resource
  private UserService userService;
  @Resource
  private JdbcTemplate jdbcTemplate;
  @Resource
  private ApiService apiService;
  private static JwtTokenFilter jwt;
  @PostConstruct
  public void init(){
    jwt=this;
    jwt.contextPath=contextPath;
    jwt.antPathMatcher=this.antPathMatcher;
    jwt.roleService=this.roleService;
    jwt.apiService =this.apiService;
    jwt.jdbcTemplate =this.jdbcTemplate;
    jwt.userService =this.userService;
  }

  @Override
  protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
    String token = jwtTokenProvider.resolveToken(httpServletRequest);
    Authentication auth;
    String requestUrl;
    try {
      Boolean isLogin=true;
      Boolean isWebSocket=false;
      requestUrl = httpServletRequest.getRequestURI();
      // 判断访问的URL是否是NoLogin就能访问的URL
      // 如：登录，获取签名和验证码这些资源是不需要登录的
      List<Map<String,Object>> listNoSessionURL=jwt.roleService.getNoLoginURL();
      for (int i = 0; i < listNoSessionURL.size(); i++) {
        if (jwt.antPathMatcher.match(jwt.contextPath+listNoSessionURL.get(i).get("url_path"), requestUrl)) {
          requestUrl=requestUrl.replace(jwt.contextPath,"");
          isLogin=false;
        }
      }
      if(isLogin){//如果不是 no session url 则判断是不是WebSocketURL
        if(jwt.antPathMatcher.match(jwt.contextPath+"/ws/websocket/**",requestUrl)){
          token=httpServletRequest.getParameter("token");
          isWebSocket=true;
        }
      }
      if (isLogin) {
        if(token==null||!jwtTokenProvider.validateToken(token)){
          throw new CustomException("4010",HttpStatus.UNAUTHORIZED);//未登录不允许访问资源。注：后面的参数：UNAUTHORIZED用不到。
        }
        auth = jwtTokenProvider.getAuthentication(token,requestUrl);
        SecurityContextHolder.getContext().setAuthentication(auth);
        if(isWebSocket){//websocket URL
          Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
          UserInfo userInfo = (UserInfo) principal;
          String urlUserId=requestUrl.replace(jwt.contextPath+"/ws/websocket/","");
          if(!(String.valueOf(userInfo.getUserId()).equals(urlUserId))){
            throw new CustomException("4010",HttpStatus.UNAUTHORIZED);//已登录，但token的信息和URL的userId不一致
          }
        }else{
          // start 对按钮权限校验  后续有时间封装一下
          String contentType = httpServletRequest.getContentType();
          //如果contentType是空或者contentType是多媒体的上传类型则忽略，不进行包装，直接return
          if (contentType == null) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
          }else if(httpServletRequest.getContentType().startsWith("multipart/")){
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
          }else if (!(httpServletRequest instanceof MyServletRequestWrapper)) {
            httpServletRequest = new MyServletRequestWrapper(httpServletRequest);
          }
          String body;
          try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(httpServletRequest.getInputStream()));
            body = RequestBodyUtils.read(reader);
            JackJSONObject params= null;
            try {
              params = JackJSONObject.fromObject(body);
            } catch (Exception e) {
              params=new JackJSONObject();
            }
            String buttonUuid=String.valueOf(params.get("buttonUuid"));
            if(!buttonUuid.equals("null")&&buttonUuid.length()>0){
              String queryButtonSql="SELECT * FROM tbl_data_button WHERE STATUS=0 and button_status=0 and uuid='"+buttonUuid+"' order by button_sort asc";
              Map<String, Object> buttonMap=jwt.jdbcTemplate.queryForMap(queryButtonSql);
              String buttonType=String.valueOf(buttonMap.get("button_type"));
              String roleStr=String.valueOf(buttonMap.get("button_role"));
              String rowButtonShowFunc=String.valueOf(buttonMap.get("row_button_show_func_java"));
              boolean isButton = false;
              User user = jwt.userService.getUser(httpServletRequest);
              if(!roleStr.equals("null")&&roleStr.length()>0){
                String[] roleArr=roleStr.split(",");
                for(int i=0;i<roleArr.length;i++) {
                  if(!roleArr.equals("")&&jwt.userService.isExistsRole(roleArr[i], user.getId())) {
                    isButton=true;
                  }
                }
                if(!isButton){
                  throw new CustomException("4032",HttpStatus.UNAUTHORIZED);//无按钮权限  后续单独分出一个编码
                }
              }
              if(!rowButtonShowFunc.equals("null")&&rowButtonShowFunc.length()>0){
                if(buttonType.equals("rowButton")){//行按钮可能需要根据旧行数据判断触发，单独处理
                  ScriptEngineManager manager = new ScriptEngineManager();
                  ScriptEngine engine = manager.getEngineByName("javascript");
                  try{
                    String buttonContentStr=String.valueOf(params.get("buttonContent"));
                    JackJSONObject buttonContentJson=JackJSONObject.fromObject(buttonContentStr);
                    buttonContentJson.put("userId",user.getId());
                    rowButtonShowFunc=rowButtonShowFunc.replaceAll("&nbsp;"," ");
                    rowButtonShowFunc=rowButtonShowFunc.replaceAll("&quot;","'");//todo 这个转换写一个通用方法
                    rowButtonShowFunc="function add(infoMap){" + rowButtonShowFunc + "}";
                    engine.eval(rowButtonShowFunc);
                    if (engine instanceof Invocable) {
                      Invocable in = (Invocable) engine;
                      isButton=Boolean.valueOf(String.valueOf(in.invokeFunction("add",buttonContentJson)));
                      if(!isButton){
                        throw new CustomException("4032",HttpStatus.UNAUTHORIZED);//无按钮权限  后续单独分出一个编码
                      }
                    }
                  }catch(Exception e){
                  }
                }
              }
            }
          } catch (IOException e) {
          }
          // end 对按钮权限校验
        }
      }//else JWT令牌已过期或无效
    } catch (CustomException ex) {
      String status=ex.getMessage();
      httpServletResponse.setStatus(200);
      Map<String, Object> retMap=jwt.apiService.getReturnDataByCode(String.valueOf(status));
      httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);// 返回类型,json格式
      httpServletResponse.setCharacterEncoding(Consts.UTF_8.toString());// 返回值编码 ，utf-8,不设置可能出现乱码
      httpServletResponse.getWriter().write(JackJSON.toJSONString(retMap));
      return;
    }
    try {
      filterChain.doFilter(httpServletRequest, httpServletResponse);
    } catch (CustomException ex) {
      httpServletResponse.sendError(ex.getHttpStatus().value(), ex.getMessage());
      return;
    }
  }

}
