package com.ytpm.encrypt;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.ytpm.annotation.ApiEncrypt;
import com.ytpm.annotation.Internal;
import com.ytpm.constant.StrConstant;
import com.ytpm.general.Result;
import com.ytpm.util.EncryptUtils;
import org.springframework.http.HttpMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.charset.StandardCharsets;

/**
 * Crypto 过滤器
 *
 * @author hidewnd
 * @date 2026/1/13 10:37
 */
public class CryptoFilter implements Filter {

    private final ApiDecryptProperties properties;

    public CryptoFilter(ApiDecryptProperties properties) {
        this.properties = properties;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        // 获取加密注解
        ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest, ApiEncrypt.class);
        Internal internal = this.getApiEncryptAnnotation(servletRequest, Internal.class);
        boolean responseFlag = apiEncrypt != null && apiEncrypt.response();
        boolean isInternalReuqest = internal != null;
        ServletRequest requestWrapper = null;
        ServletResponse responseWrapper = null;
        EncryptResponseBodyWrapper responseBodyWrapper = null;
        // 内部请求，中断加解密处理
        String headerValue = servletRequest.getHeader(StrConstant.INTERNAL_HEADER);
        if (isInternalReuqest) {
            if (StrUtil.isBlank(headerValue) || !StrConstant.INTERNAL_HEADER_FLAG.equals(EncryptUtils.decryptByBase64(headerValue))) {
                Result<Void> result = Result.resultFail(403, "没有访问权限，请联系管理员");
                servletResponse.reset();
                servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
                servletResponse.getWriter().write(JSONUtil.toJsonStr(result));
            } else {
                chain.doFilter(request, response);
            }
            return;
        }
        // 是否为 put 或者 post 请求
        if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
            // 是否存在加密标头
            headerValue = servletRequest.getHeader(properties.getHeaderFlag());
            if (StrUtil.isNotBlank(headerValue)) {
                // 请求解密
                requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
            } else {
                // 是否有注解，有就报错，没有放行
                if (ObjectUtil.isNotNull(apiEncrypt)) {
                    Result<Void> result = Result.resultFail(403, "没有访问权限，请联系管理员");
                    servletResponse.reset();
                    servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
                    servletResponse.getWriter().write(JSONUtil.toJsonStr(result));
                    return;
                }
            }
        }
        // 判断是否响应加密
        if (responseFlag) {
            responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
            responseWrapper = responseBodyWrapper;
        }

        chain.doFilter(
                ObjectUtil.defaultIfNull(requestWrapper, request),
                ObjectUtil.defaultIfNull(responseWrapper, response));
        // 返回信息加密
        if (responseFlag) {
            servletResponse.reset();
            // 对原始内容加密
            String encryptContent = responseBodyWrapper.getEncryptContent(
                    servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
            // 对加密后的内容写出
            servletResponse.getWriter().write(encryptContent);
        }
    }

    /**
     * 获取 ApiEncrypt 注解
     */
    private <A extends Annotation> A getApiEncryptAnnotation(HttpServletRequest servletRequest, Class<A> annotationClass) {
        RequestMappingHandlerMapping handlerMapping = SpringUtil.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        // 获取注解
        try {
            HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
            if (ObjectUtil.isNotNull(mappingHandler)) {
                Object handler = mappingHandler.getHandler();
                if (ObjectUtil.isNotNull(handler)) {
                    // 从handler获取注解
                    if (handler instanceof HandlerMethod) {
                        return ((HandlerMethod) handler).getMethodAnnotation(annotationClass);
                    }
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

    @Override
    public void destroy() {
    }
}
