package tech.easyflow.ai.service.discovery;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.net.httpserver.HttpServer;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.ai.entity.ModelProvider;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.GZIPOutputStream;

/**
 * 远端模型目录 URL 合并与网络目标限制测试。
 */
public class RemoteModelHttpClientTest {

    /**
     * 验证 Endpoint 已含版本路径时不会重复拼接。
     */
    @Test
    public void shouldJoinEndpointAndPathWithoutDuplicatingPrefix() {
        ModelProvider provider = provider("self-hosted", "http://127.0.0.1:8000/v1");

        URI uri = new RemoteModelHttpClient(new ObjectMapper()).buildUri(
                provider, "/v1/models", Map.of("type", "text"));

        Assert.assertEquals("http://127.0.0.1:8000/v1/models?type=text", uri.toString());
    }

    /**
     * 验证云服务商类型不能访问本机地址。
     */
    @Test(expected = BusinessException.class)
    public void shouldRejectPrivateTargetForCloudProvider() {
        ModelProvider provider = provider("openai", "http://127.0.0.1:8000");

        new RemoteModelHttpClient(new ObjectMapper()).buildUri(provider, "/v1/models", Map.of());
    }

    /**
     * 验证自部署类型也不能访问链路本地元数据地址。
     */
    @Test(expected = BusinessException.class)
    public void shouldRejectMetadataTargetForSelfHostedProvider() {
        ModelProvider provider = provider("self-hosted", "http://169.254.169.254");

        new RemoteModelHttpClient(new ObjectMapper()).buildUri(provider, "/v1/models", Map.of());
    }

    /**
     * 验证客户端会请求并正确解压 gzip 模型目录响应。
     *
     * @throws Exception 本地测试服务启动或请求失败时抛出
     */
    @Test
    public void shouldRequestAndDecodeGzipResponse() throws Exception {
        AtomicReference<String> acceptEncoding = new AtomicReference<>();
        HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
        server.createContext("/v1/models", exchange -> {
            acceptEncoding.set(exchange.getRequestHeaders().getFirst("Accept-Encoding"));
            byte[] payload = gzip("{\"data\":[{\"id\":\"test-model\"}]}");
            exchange.getResponseHeaders().add("Content-Type", "application/json");
            exchange.getResponseHeaders().add("Content-Encoding", "gzip");
            exchange.sendResponseHeaders(200, payload.length);
            try (var responseBody = exchange.getResponseBody()) {
                responseBody.write(payload);
            }
        });
        server.start();

        try {
            ModelProvider provider = provider("self-hosted",
                    "http://127.0.0.1:" + server.getAddress().getPort());

            var result = new RemoteModelHttpClient(new ObjectMapper())
                    .getJson(provider, "/v1/models", Map.of());

            Assert.assertEquals("test-model", result.path("data").path(0).path("id").asText());
            Assert.assertEquals("gzip", acceptEncoding.get());
        } finally {
            server.stop(0);
        }
    }

    /**
     * 压缩测试响应内容。
     *
     * @param content 原始响应内容
     * @return gzip 压缩后的字节
     * @throws IOException 压缩失败时抛出
     */
    private byte[] gzip(String content) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        try (GZIPOutputStream gzip = new GZIPOutputStream(output)) {
            gzip.write(content.getBytes(StandardCharsets.UTF_8));
        }
        return output.toByteArray();
    }

    /**
     * 创建测试服务商。
     *
     * @param providerType 服务商类型
     * @param endpoint API 地址
     * @return 测试服务商
     */
    private ModelProvider provider(String providerType, String endpoint) {
        ModelProvider provider = new ModelProvider();
        provider.setProviderType(providerType);
        provider.setEndpoint(endpoint);
        return provider;
    }
}
