package com.chinaweal.aiccs.config;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;

@Slf4j
public class ImsRequestFilter implements Filter {

    // 匹配不可见字符（控制字符0-31和127，以及零宽字符）
    private static final Pattern INVISIBLE_CHAR_PATTERN = Pattern.compile(
        "[\\x00-\\x1f\\x7f\\u200b-\\u200f\\u2028\\u2029\\ufeff]"
    );

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        // 只处理 JSON 请求
        String contentType = request.getContentType();
        if (contentType != null && contentType.contains("application/json")) {
            CachedBodyHttpServletRequest cachedRequest =
                new CachedBodyHttpServletRequest(request);

            String body = cachedRequest.getBody();
            // 移除不可见字符
            String cleanedBody = INVISIBLE_CHAR_PATTERN.matcher(body).replaceAll("");

            if (!cleanedBody.equals(body)) {
                log.warn("ImsRequestFilter removed invisible characters from request body");
            }

            cachedRequest.updateBody(cleanedBody);
            chain.doFilter(cachedRequest, resp);
        } else {
            chain.doFilter(req, resp);
        }
    }

    private static class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
        private byte[] cachedBody;

        public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
            super(request);
            InputStream inputStream = request.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            inputStream.close();
            this.cachedBody = baos.toByteArray();
        }

        public String getBody() {
            return new String(cachedBody, StandardCharsets.UTF_8);
        }

        public void updateBody(String body) {
            this.cachedBody = body.getBytes(StandardCharsets.UTF_8);
        }

        @Override
        public ServletInputStream getInputStream() {
            return new CachedBodyServletInputStream(cachedBody);
        }

        @Override
        public BufferedReader getReader() {
            return new BufferedReader(
                new InputStreamReader(getInputStream(), StandardCharsets.UTF_8)
            );
        }
    }

    private static class CachedBodyServletInputStream extends ServletInputStream {
        private final ByteArrayInputStream delegate;

        public CachedBodyServletInputStream(byte[] body) {
            this.delegate = new ByteArrayInputStream(body);
        }

        @Override
        public boolean isFinished() {
            return delegate.available() == 0;
        }

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setReadListener(ReadListener listener) {
            // non-blocking read not supported for cached body
        }

        @Override
        public int read() {
            return delegate.read();
        }
    }
}