package tech.easyflow.ai.service.discovery;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import tech.easyflow.ai.entity.ModelProvider;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Map;

/**
 * 三类远端模型目录适配器测试。
 */
public class RemoteModelProviderAdapterTest {

    /** JSON 解析器。 */
    private ObjectMapper objectMapper;
    /** 受控 HTTP 客户端替身。 */
    private StubRemoteModelHttpClient httpClient;
    /** 服务商配置。 */
    private ModelProvider provider;

    /**
     * 初始化测试夹具。
     */
    @Before
    public void setUp() {
        objectMapper = new ObjectMapper();
        httpClient = new StubRemoteModelHttpClient(objectMapper);
        provider = new ModelProvider();
        provider.setChatPath("/v1/chat/completions");
    }

    /**
     * 验证 OpenAI-compatible 路径推导和 data.id 解析。
     *
     * @throws Exception JSON 夹具解析失败时抛出
     */
    @Test
    public void shouldParseOpenAiCompatibleModels() throws Exception {
        provider.setProviderType("openai");
        httpClient.addResponse("{\"data\":[{\"id\":\"gpt-5\"}]}");

        List<String> result = new OpenAiCompatibleRemoteModelAdapter()
                .fetchModelIds(provider, httpClient);

        Assert.assertEquals(List.of("gpt-5"), result);
        Assert.assertEquals("/v1/models", httpClient.lastPath);
        Assert.assertEquals(Map.of(), httpClient.lastQuery);
    }

    /**
     * 验证 Ollama 优先读取 name 并兼容 model 字段。
     *
     * @throws Exception JSON 夹具解析失败时抛出
     */
    @Test
    public void shouldParseOllamaModels() throws Exception {
        httpClient.addResponse(
                "{\"models\":[{\"name\":\"qwen3:8b\"},{\"model\":\"bge-m3:latest\"}]}");

        List<String> result = new OllamaRemoteModelAdapter().fetchModelIds(provider, httpClient);

        Assert.assertEquals(List.of("qwen3:8b", "bge-m3:latest"), result);
    }

    /**
     * 验证阿里百炼模型名称和分页字段解析。
     *
     * @throws Exception JSON 夹具解析失败时抛出
     */
    @Test
    public void shouldParseAliyunModels() throws Exception {
        httpClient.addResponse(
                "{\"request_id\":\"request-1\",\"output\":{"
                        + "\"page_no\":1,\"page_size\":100,\"total\":1,"
                        + "\"models\":[{\"model_name\":\"qwen-plus\"}]}}");

        List<String> result = new AliyunRemoteModelAdapter().fetchModelIds(provider, httpClient);

        Assert.assertEquals(List.of("qwen-plus"), result);
        Assert.assertEquals("/api/v1/deployments/models", httpClient.lastPath);
        Assert.assertEquals(Map.of(
                "model_source", "base",
                "page_no", "1",
                "page_size", "100",
                "version", "v1.0"), httpClient.lastQuery);
    }

    /**
     * 验证阿里百炼历史根级响应仍可解析。
     *
     * @throws Exception JSON 夹具解析失败时抛出
     */
    @Test
    public void shouldKeepAliyunLegacyResponseCompatibility() throws Exception {
        httpClient.addResponse(
                "{\"models\":[{\"model_name\":\"qwen-turbo\"}],\"total_count\":1}");

        List<String> result = new AliyunRemoteModelAdapter().fetchModelIds(provider, httpClient);

        Assert.assertEquals(List.of("qwen-turbo"), result);
    }

    /**
     * 验证百炼媒体生成和内部算法模型不会被误标为对话模型。
     *
     * @throws Exception JSON 夹具解析失败时抛出
     */
    @Test
    public void shouldExcludeUnsupportedAliyunModels() throws Exception {
        httpClient.addResponse(
                "{\"output\":{\"page_no\":1,\"page_size\":100,\"total\":7,"
                        + "\"models\":["
                        + "{\"model_name\":\"animate-anyone\"},"
                        + "{\"model_name\":\"animate-anyone-detect\"},"
                        + "{\"model_name\":\"emo\"},"
                        + "{\"model_name\":\"emo-detect\"},"
                        + "{\"model_name\":\"mock-algo-v1\"},"
                        + "{\"model_name\":\"wanx-v1-0521\"},"
                        + "{\"model_name\":\"qwen-plus\"}]}}"
        );

        List<String> result = new AliyunRemoteModelAdapter().fetchModelIds(provider, httpClient);

        Assert.assertEquals(List.of("qwen-plus"), result);
    }

    /**
     * 以队列响应替代真实网络请求的轻量测试客户端。
     */
    private static final class StubRemoteModelHttpClient extends RemoteModelHttpClient {

        /** JSON 解析器。 */
        private final ObjectMapper objectMapper;
        /** 待返回响应队列。 */
        private final Deque<String> responses = new ArrayDeque<>();
        /** 最近请求路径。 */
        private String lastPath;
        /** 最近查询参数。 */
        private Map<String, String> lastQuery;

        /**
         * 创建测试客户端。
         *
         * @param objectMapper JSON 解析器
         */
        private StubRemoteModelHttpClient(ObjectMapper objectMapper) {
            super(objectMapper);
            this.objectMapper = objectMapper;
        }

        /**
         * 添加下一次请求返回的 JSON。
         *
         * @param response JSON 文本
         */
        private void addResponse(String response) {
            responses.addLast(response);
        }

        /**
         * 返回预置 JSON 并记录请求参数。
         *
         * @param provider 服务商配置
         * @param requestPath 请求路径
         * @param queryParameters 查询参数
         * @return 预置 JSON 根节点
         */
        @Override
        public com.fasterxml.jackson.databind.JsonNode getJson(
                ModelProvider provider,
                String requestPath,
                Map<String, String> queryParameters) {
            lastPath = requestPath;
            lastQuery = Map.copyOf(queryParameters);
            try {
                return objectMapper.readTree(responses.removeFirst());
            } catch (Exception exception) {
                throw new AssertionError("测试 JSON 解析失败", exception);
            }
        }
    }
}
