package tech.easyflow.publicapi.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.web.bind.annotation.RequestParam;
import tech.easyflow.ai.entity.DocumentChunk;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.publicapi.dto.PublicKnowledgeSearchResultItem;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.math.BigInteger;
import java.util.List;

/**
 * {@link PublicKnowledgeShareController} 公开接口参数契约测试。
 */
public class PublicKnowledgeShareControllerContractTest {

    /**
     * 验证文档删除接口的 knowledgeId 和 id 均从 JSON 请求体读取。
     *
     * @throws Exception 反射失败
     */
    @Test
    public void documentRemoveShouldReadIdentifiersFromJsonBody()
        throws Exception {
        Method method = PublicKnowledgeShareController.class.getDeclaredMethod(
            "removeDocument",
            String.class,
            BigInteger.class,
            String.class,
            HttpServletRequest.class
        );
        Parameter knowledgeId = method.getParameters()[1];
        Parameter documentId = method.getParameters()[2];

        assertRequiredJsonField(knowledgeId, "knowledgeId");
        assertRequiredJsonField(documentId, "id");
        Assert.assertNull(knowledgeId.getAnnotation(RequestParam.class));
    }

    /**
     * 验证文档分块更新接口从 JSON 请求体读取 knowledgeId。
     *
     * @throws Exception 反射失败
     */
    @Test
    public void documentChunkUpdateShouldReadKnowledgeIdFromJsonBody()
        throws Exception {
        Method method = PublicKnowledgeShareController.class.getDeclaredMethod(
            "updateDocumentChunk",
            String.class,
            BigInteger.class,
            DocumentChunk.class,
            HttpServletRequest.class
        );
        Parameter knowledgeId = method.getParameters()[1];
        Parameter chunk = method.getParameters()[2];

        assertRequiredJsonField(knowledgeId, "knowledgeId");
        Assert.assertNull(knowledgeId.getAnnotation(RequestParam.class));
        JsonBody chunkBody = chunk.getAnnotation(JsonBody.class);
        Assert.assertNotNull(chunkBody);
        Assert.assertEquals("", chunkBody.value());
    }

    /**
     * 验证文档分块删除接口从 JSON 请求体读取 knowledgeId 和 id。
     *
     * @throws Exception 反射失败
     */
    @Test
    public void documentChunkRemoveShouldReadIdentifiersFromJsonBody()
        throws Exception {
        Method method = PublicKnowledgeShareController.class.getDeclaredMethod(
            "removeDocumentChunk",
            String.class,
            BigInteger.class,
            BigInteger.class,
            HttpServletRequest.class
        );
        Parameter knowledgeId = method.getParameters()[1];
        Parameter chunkId = method.getParameters()[2];

        assertRequiredJsonField(knowledgeId, "knowledgeId");
        assertRequiredJsonField(chunkId, "id");
        Assert.assertNull(knowledgeId.getAnnotation(RequestParam.class));
    }

    /**
     * 验证文档检索结果包含来源文档信息且不混入 FAQ 字段。
     *
     * @throws Exception 测试依赖注入失败
     */
    @Test
    public void searchShouldExposeDocumentIdentity() throws Exception {
        BigInteger documentId = BigInteger.valueOf(2002);
        com.easyagents.core.document.Document hit =
            com.easyagents.core.document.Document.of("命中文本");
        hit.addMetadata("resultType", DocumentCollection.TYPE_DOCUMENT);
        hit.addMetadata("documentId", documentId);
        hit.addMetadata("sourceFileName", "manual.pdf");
        List<PublicKnowledgeSearchResultItem> results =
            mapSearchResult(hit);

        Assert.assertEquals(1, results.size());
        PublicKnowledgeSearchResultItem item = results.get(0);
        Assert.assertEquals(DocumentCollection.TYPE_DOCUMENT, item.getResultType());
        Assert.assertEquals(documentId, item.getDocumentId());
        Assert.assertEquals("manual.pdf", item.getDocumentName());
        Assert.assertEquals("manual.pdf", item.getSourceFileName());
        Assert.assertNull(item.getFaqId());
        Assert.assertNull(item.getQuestion());
        String json = new ObjectMapper().writeValueAsString(item);
        Assert.assertFalse(json.contains("\"faqId\""));
        Assert.assertFalse(json.contains("\"question\""));
    }

    /**
     * 验证 FAQ 检索结果包含 FAQ 信息且不混入文档字段。
     *
     * @throws Exception 测试依赖注入失败
     */
    @Test
    public void searchShouldExposeFaqIdentity() throws Exception {
        BigInteger faqId = BigInteger.valueOf(3003);
        BigInteger categoryId = BigInteger.valueOf(4004);
        com.easyagents.core.document.Document hit =
            com.easyagents.core.document.Document.of("FAQ 命中文本");
        hit.addMetadata("resultType", DocumentCollection.TYPE_FAQ);
        hit.addMetadata("faqId", faqId);
        hit.addMetadata("question", "如何申请账号？");
        hit.addMetadata("answerText", "请联系管理员。");
        hit.addMetadata("categoryId", categoryId);
        List<PublicKnowledgeSearchResultItem> results =
            mapSearchResult(hit);

        Assert.assertEquals(1, results.size());
        PublicKnowledgeSearchResultItem item = results.get(0);
        Assert.assertEquals(DocumentCollection.TYPE_FAQ, item.getResultType());
        Assert.assertEquals(faqId, item.getFaqId());
        Assert.assertEquals("如何申请账号？", item.getQuestion());
        Assert.assertEquals("请联系管理员。", item.getAnswerText());
        Assert.assertEquals(categoryId, item.getCategoryId());
        Assert.assertNull(item.getDocumentId());
        Assert.assertNull(item.getDocumentName());
        String json = new ObjectMapper().writeValueAsString(item);
        Assert.assertFalse(json.contains("\"documentId\""));
        Assert.assertFalse(json.contains("\"documentName\""));
        Assert.assertFalse(json.contains("\"sourceFileName\""));
    }

    /**
     * 调用公开检索结果映射。
     *
     * @param hit 检索命中
     * @return 公开检索结果
     * @throws Exception 反射调用失败
     */
    @SuppressWarnings("unchecked")
    private List<PublicKnowledgeSearchResultItem> mapSearchResult(
        com.easyagents.core.document.Document hit
    ) throws Exception {
        Method method = PublicKnowledgeShareController.class
            .getDeclaredMethod(
                "toKnowledgeSearchResult",
                List.class
            );
        method.setAccessible(true);
        return (List<PublicKnowledgeSearchResultItem>) method.invoke(
            new PublicKnowledgeShareController(),
            List.of(hit)
        );
    }

    /**
     * 断言参数绑定到指定的必填 JSON 字段。
     *
     * @param parameter 方法参数
     * @param field JSON 字段名
     */
    private void assertRequiredJsonField(Parameter parameter, String field) {
        JsonBody jsonBody = parameter.getAnnotation(JsonBody.class);
        Assert.assertNotNull(jsonBody);
        Assert.assertEquals(field, jsonBody.value());
        Assert.assertTrue(jsonBody.required());
    }
}
