package com.jiayue.ssi.filter;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.jiayue.ssi.constant.CacheConstants;
import com.jiayue.ssi.constant.Constants;
import com.jiayue.ssi.constant.SecretKeyConstants;
import com.jiayue.ssi.dto.ActiveUserDto;
import com.jiayue.ssi.entity.SysUser;
import com.jiayue.ssi.factory.LoginFactory;
import com.jiayue.ssi.service.impl.UserServiceImpl;
import com.jiayue.ssi.servlet.ParameterRequestWrapper;
import com.jiayue.ssi.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 验证sm过滤器
 *
 * @author xsl
 * @since 2023/02/27
 */
@RequiredArgsConstructor
@Order(3)
@Slf4j
public class VerifySmFilter extends OncePerRequestFilter {
    private String defaultFilterProcessUrl = "/user/login";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
        try {
            if (request.getRequestURI().equals(defaultFilterProcessUrl)){
                String currentIp = IPUtils.getIpAddr();
                String ip = CacheConstants.IP_USER_MAP.get(currentIp);
                if (ip!=null && !"".equals(ip)){
                    // 判断当前访问的ip是否已经存在，如果存在则不让任何用户访问
                    ResponseInfo.doResponse(response, "您的客户端用户正在使用，请等待一小会登录系统！", 405);
                    return;
                }
            }

            Map<String, Object> stringToMap = new HashMap(16);
            ParameterRequestWrapper initWrapper = new ParameterRequestWrapper(request);
            // 不是登录操作
            if (!("POST".equalsIgnoreCase(request.getMethod())
                    && defaultFilterProcessUrl.equals(request.getServletPath())) && !("/getVerifyCode".equals(request.getServletPath()))) {
                String tokenStr = request.getHeader("Authorization");
                String tokenSign = request.getHeader("JySign");

                if (StringUtils.isNotEmpty(tokenStr)) {
                    // 解密token
                    String decryptTokenStr = SM2CryptUtils.decrypt(tokenStr, SecretKeyConstants.SERVER_PRIVATE_KEY);

                    // 验证签名
                    boolean verifySign =
                            SM2CryptUtils.verifySign(SecretKeyConstants.CLIENT_PUBLIC_KEY, decryptTokenStr, tokenSign);
                    if (!verifySign) {
                        // 验签失败
                        ResponseInfo.doResponse(response, "token验签失败,不能访问系统！", 401);
                        return;
                    }
//                    JwtTokenUtil jwtTokenUtil = new JwtTokenUtil();
//                    if (jwtTokenUtil.isTokenExpired(decryptTokenStr)) {
//                        ResponseInfo.doResponse(response, "token令牌无效,请重新登录！", 403);
//                        return;
//                    }

                    initWrapper.addHeader("Authorization", decryptTokenStr);
                } else {
                    ResponseInfo.doResponse(response, "没有令牌权限,不能访问系统！", 401);
                    return;
                }
            }
            // 解密后的参数字符串
            String decryptStr = "";
            if ("POST".equalsIgnoreCase(request.getMethod()) || "PUT".equalsIgnoreCase(request.getMethod()) || "DELETE".equalsIgnoreCase(request.getMethod())) {
                byte[] bytes = null;
                try {
                    bytes = initWrapper.getBodyContent(request).getBytes(StandardCharsets.UTF_8);
                } catch (Exception e) {
                    throw e;
//                    e.printStackTrace();
                }
                String json = new String(bytes);
                JSONObject jsonObject;
                try {
                    jsonObject = JSONUtil.parseObj(json);
                } catch (Exception e) {
//                    System.out.println("出错字符=====>"+json);
                    throw e;
                }
                // 验证加密的参数文本
                String data_sm2 = jsonObject.getStr("secretData");
                if (StringUtils.isNotEmpty(data_sm2)) {
                    try {
                        decryptStr = SM2CryptUtils.decrypt(data_sm2, SecretKeyConstants.SERVER_PRIVATE_KEY);
                    } catch (Exception e) {
                        // 参数验签失败
                        if (defaultFilterProcessUrl.equals(request.getServletPath())) {
                            // 记录用户退出日志
                            LoginFactory.recordLogininfor("未知", Constants.LOGIN_FAIL, "参数解密失败");
                        }
                        ResponseInfo.doResponse(response, "参数解密失败,不能访问系统！", 401);
                        return;
                    }
                    // 验签前端参数
                    String paramSign = jsonObject.getStr("paramSign");
                    try {
                        // 验证签名
                        boolean verifySign =
                                SM2CryptUtils.verifySign(SecretKeyConstants.CLIENT_PUBLIC_KEY, decryptStr, paramSign);
                        if (!verifySign) {
                            if (defaultFilterProcessUrl.equals(request.getServletPath())) {
                                // 记录用户退出日志
                                LoginFactory.recordLogininfor("未知", Constants.LOGIN_FAIL, "参数验签失败");
                            }
                            // 验签失败
                            ResponseInfo.doResponse(response, "参数验签失败,不能访问系统！", 401);
                            return;
                        }
                    } catch (Exception e) {
                        // 验签失败
                        if (defaultFilterProcessUrl.equals(request.getServletPath())) {
                            // 记录用户退出日志
                            LoginFactory.recordLogininfor("未知", Constants.LOGIN_FAIL, "参数验签失败");
                        }
                        ResponseInfo.doResponse(response, "参数验签失败,不能访问系统！", 401);
                        return;
                    }
                    if (!"".equals(decryptStr)) {
                        try {
                            stringToMap = JSONUtil.parseObj(decryptStr);
                        } catch (Exception e) {
                            if (defaultFilterProcessUrl.equals(request.getServletPath())) {
                                // 记录用户退出日志
                                LoginFactory.recordLogininfor("未知", Constants.LOGIN_FAIL, "参数转换json失败");
                            }
                            ResponseInfo.doResponse(response, "参数不能转换json格式,不能访问系统！", 401);
                            return;
                        }
                    }
                }
            } else {
                // get请求无参数，取出是null
                String get_sm2Str = request.getParameter("0");
                if (StringUtils.isNotEmpty(get_sm2Str)) {
                    Map<String, String> tempMap = new HashMap(16);
                    // 对加密串解密验签
                    try {
                        String[] tempStr = get_sm2Str.replaceAll("＆", "&").split("&");
                        for (int i = 0; i < tempStr.length; i++) {
                            String[] fieldStr = tempStr[i].split("=");
                            tempMap.put(fieldStr[0], fieldStr[1]);
                        }
                        decryptStr =
                                SM2CryptUtils.decrypt(tempMap.get("secretData"), SecretKeyConstants.SERVER_PRIVATE_KEY);
                    } catch (Exception e) {
                        // 参数验签失败
                        ResponseInfo.doResponse(response, "参数解密失败,不能访问系统！", 401);
                        return;
                    }
                    // 验签前端参数
                    String paramSign = tempMap.get("paramSign");
                    // 验证签名
                    try {
                        boolean verifySign =
                                SM2CryptUtils.verifySign(SecretKeyConstants.CLIENT_PUBLIC_KEY, decryptStr, paramSign);
                        if (!verifySign) {
                            // 验签失败
                            ResponseInfo.doResponse(response, "参数验签失败,不能访问系统！", 401);
                            return;
                        }
                    } catch (Exception e) {
                        // 验签失败
                        ResponseInfo.doResponse(response, "参数验签失败,不能访问系统！", 401);
                        return;
                    }
                    if (!"".equals(decryptStr)) {
                        stringToMap = JSONUtil.parseObj(decryptStr).getRaw();
                    }
                }
            }
            //对参数里的字符进行合理化校验
            String regex = "^[\\u4e00-\\u9fa5a-zA-Z0-9!@#$%^&*()_+=|<>,.?/:;'\"\\[\\]{}：；，。！¥（）“”‘’、《》【】？— \\-\\s]*$";
            for (String key : stringToMap.keySet()) {
                if (!stringToMap.get(key).toString().matches(regex)) {
                    ResponseInfo.doResponse(response, "参数验证失败,包含特殊字符 " + stringToMap.get(key), 405);
                    return;
                }
            }
            ParameterRequestWrapper pr = new ParameterRequestWrapper(initWrapper, stringToMap, decryptStr);
            filterChain.doFilter(pr, response);
        } catch (Exception e) {
//            e.printStackTrace();
//            log.error(IPUtils.getIpAddr(request) + "访问系统失败", e);
            ResponseInfo.doResponse(response, "不能访问系统,联系管理员！", 401);
            return;
        }
    }
}
