package cn.nianxx.thhotel.integrations.ai.superagent.control;

import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentTaskResultErrorResponse;
import cn.nianxx.thhotel.integrations.ai.superagent.service.impl.SuperAgentTaskResultProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * SuperAgent 任务结果请求体大小过滤器。进入 Controller 前拦截明显超限请求，并对未知长度请求做读取限流。
 */
@Component
public class SuperAgentTaskResultBodySizeFilter extends OncePerRequestFilter {

    private static final String REQUEST_PATH = "/api/integrations/superagent/task-results";

    private final SuperAgentTaskResultProperties properties;
    private final ObjectMapper objectMapper;

    /**
     * 注入 SuperAgent 请求大小配置和 JSON 序列化器，用于输出统一安全错误响应。
     */
    public SuperAgentTaskResultBodySizeFilter(
            SuperAgentTaskResultProperties properties,
            ObjectMapper objectMapper) {
        this.properties = properties;
        this.objectMapper = objectMapper;
    }

    /**
     * 对 SuperAgent 入站接口执行大小预检查和流式限流，超限时不再进入业务处理。
     */
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        long maxBodyBytes = properties.getMaxBodyBytes();
        long contentLength = request.getContentLengthLong();
        if (matchesTaskResultEndpoint(request) && maxBodyBytes >= 0 && contentLength > maxBodyBytes) {
            writePayloadTooLarge(response);
            return;
        }
        if (matchesTaskResultEndpoint(request) && maxBodyBytes >= 0) {
            try {
                filterChain.doFilter(new LimitedBodyRequest(request, maxBodyBytes), response);
            } catch (RequestBodyTooLargeException exception) {
                if (!response.isCommitted()) {
                    writePayloadTooLarge(response);
                }
            }
            return;
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 判断当前请求是否为 SuperAgent 任务结果入站接口。
     */
    private boolean matchesTaskResultEndpoint(HttpServletRequest request) {
        return "POST".equalsIgnoreCase(request.getMethod()) && REQUEST_PATH.equals(request.getServletPath());
    }

    /**
     * 写出统一 413 错误，避免响应中包含原始请求体或 Secret。
     */
    private void writePayloadTooLarge(HttpServletResponse response) throws IOException {
        response.setStatus(HttpStatus.PAYLOAD_TOO_LARGE.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        objectMapper.writeValue(response.getWriter(), new SuperAgentTaskResultErrorResponse(
                null,
                "REQUEST_BODY_TOO_LARGE",
                "请求体超过允许大小。",
                List.of()));
    }

    /**
     * 限流请求包装器。用于 Content-Length 不可靠或缺失时，在读取请求体过程中强制限制字节数。
     */
    private static final class LimitedBodyRequest extends HttpServletRequestWrapper {

        private final long maxBodyBytes;

        /**
         * 包装原始请求，并记录最大允许读取字节数。
         */
        private LimitedBodyRequest(HttpServletRequest request, long maxBodyBytes) {
            super(request);
            this.maxBodyBytes = maxBodyBytes;
        }

        /**
         * 返回带字节计数的输入流。
         */
        @Override
        public ServletInputStream getInputStream() throws IOException {
            return new LimitedServletInputStream(super.getInputStream(), maxBodyBytes);
        }

        /**
         * 返回带字节计数的字符读取器，保持请求原始字符集。
         */
        @Override
        public BufferedReader getReader() throws IOException {
            String encoding = getCharacterEncoding();
            Charset charset = encoding == null ? StandardCharsets.UTF_8 : Charset.forName(encoding);
            return new BufferedReader(new InputStreamReader(getInputStream(), charset));
        }
    }

    /**
     * 带读取上限的 ServletInputStream。超过限制立即抛出受控 IO 异常。
     */
    private static final class LimitedServletInputStream extends ServletInputStream {

        private final ServletInputStream delegate;
        private final long maxBodyBytes;
        private long bytesRead;

        /**
         * 注入原始输入流和最大允许读取字节数。
         */
        private LimitedServletInputStream(ServletInputStream delegate, long maxBodyBytes) {
            this.delegate = delegate;
            this.maxBodyBytes = maxBodyBytes;
        }

        /**
         * 逐字节读取并累计大小，超过限制则中断。
         */
        @Override
        public int read() throws IOException {
            int value = delegate.read();
            if (value != -1) {
                countBytes(1);
            }
            return value;
        }

        /**
         * 批量读取并累计实际读取大小，超过限制则中断。
         */
        @Override
        public int read(byte[] buffer, int offset, int length) throws IOException {
            int count = delegate.read(buffer, offset, length);
            if (count > 0) {
                countBytes(count);
            }
            return count;
        }

        /**
         * 委托容器判断流是否结束。
         */
        @Override
        public boolean isFinished() {
            return delegate.isFinished();
        }

        /**
         * 委托容器判断流是否可读。
         */
        @Override
        public boolean isReady() {
            return delegate.isReady();
        }

        /**
         * 委托异步读取监听器。
         */
        @Override
        public void setReadListener(ReadListener readListener) {
            delegate.setReadListener(readListener);
        }

        /**
         * 累计已读字节数，超过上限时抛出专用异常。
         */
        private void countBytes(int count) throws IOException {
            bytesRead += count;
            if (bytesRead > maxBodyBytes) {
                throw new RequestBodyTooLargeException();
            }
        }
    }

    /**
     * 请求体读取超限异常。只在过滤器内部转换为 413 响应。
     */
    private static final class RequestBodyTooLargeException extends IOException {
    }
}
