package com.easyagents.flow.core.node;

import com.easyagents.flow.core.chain.Chain;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;
import java.util.Map;

/**
 * {@link HttpNode} 性能保护与重试安全回归测试。
 */
public class HttpNodePerformanceSafetyTest {

    /**
     * 验证非幂等请求不会执行节点内部隐式自动重试。
     */
    @Test
    public void shouldNotAutomaticallyRetryNonIdempotentMethod() {
        FailingHttpNode node = new FailingHttpNode();
        node.setMethod("POST");

        try {
            node.execute(null);
            Assert.fail("request should fail");
        } catch (RuntimeException expected) {
            Assert.assertEquals(1, node.attempts);
        }
    }

    /**
     * 验证仅读取类 HTTP 方法允许节点内部自动重试。
     */
    @Test
    public void shouldOnlyRetrySafeReadMethods() {
        FailingHttpNode node = new FailingHttpNode();

        Assert.assertTrue(node.allowsAutomaticRetry("GET"));
        Assert.assertTrue(node.allowsAutomaticRetry("HEAD"));
        Assert.assertTrue(node.allowsAutomaticRetry("OPTIONS"));
        Assert.assertFalse(node.allowsAutomaticRetry("POST"));
        Assert.assertFalse(node.allowsAutomaticRetry("PUT"));
        Assert.assertFalse(node.allowsAutomaticRetry("PATCH"));
        Assert.assertFalse(node.allowsAutomaticRetry("DELETE"));
    }

    /**
     * 验证响应头未声明长度时仍按实际读取量拒绝超限文本。
     *
     * @throws Exception 响应读取失败时抛出
     */
    @Test
    public void shouldEnforceActualTextResponseSize() throws Exception {
        FailingHttpNode node = new FailingHttpNode();
        ResponseBody body = new ResponseBody() {
            @Override
            public MediaType contentType() {
                return MediaType.parse("text/plain; charset=utf-8");
            }

            @Override
            public long contentLength() {
                return -1L;
            }

            @Override
            public BufferedSource source() {
                return new Buffer().writeUtf8("12345");
            }
        };

        try {
            node.read(body, 4L);
            Assert.fail("actual response bytes should be limited");
        } catch (IOException exception) {
            Assert.assertTrue(exception.getMessage().contains("4 bytes"));
        }
    }

    /**
     * 始终返回 I/O 异常的测试节点。
     */
    private static final class FailingHttpNode extends HttpNode {

        private int attempts;

        /**
         * 模拟单次 HTTP 调用失败。
         *
         * @param chain 工作流链
         * @return 不会正常返回
         * @throws IOException 固定抛出模拟异常
         */
        @Override
        public Map<String, Object> doExecute(Chain chain) throws IOException {
            attempts++;
            throw new IOException("simulated failure");
        }

        /**
         * 暴露自动重试判断供测试使用。
         *
         * @param method HTTP 方法
         * @return 是否允许自动重试
         */
        private boolean allowsAutomaticRetry(String method) {
            return supportsAutomaticRetry(method);
        }

        /**
         * 暴露文本响应读取供测试使用。
         *
         * @param body 响应体
         * @param maxBytes 最大允许字节数
         * @return 响应文本
         * @throws IOException 读取失败或超限时抛出
         */
        private String read(ResponseBody body, long maxBytes) throws IOException {
            return readTextBody(body, maxBytes);
        }
    }
}
