package com.easyagents.agent.runtime.knowledge.citation;

import com.easyagents.agent.runtime.message.AgentKnowledgeReference;
import org.junit.Assert;
import org.junit.Test;

import java.util.List;

/**
 * 测试启发式知识库引用匹配器。
 */
public class HeuristicKnowledgeCitationMatcherTest {

    private final HeuristicKnowledgeCitationMatcher matcher = new HeuristicKnowledgeCitationMatcher();

    /**
     * 当答案与候选片段存在明确文本证据时，应返回对应引用。
     */
    @Test
    public void shouldMatchReferenceByAnswerEvidence() {
        AgentKnowledgeReference unrelated = reference("doc-1", "周期结算金额等于订单金额减去佣金金额。");
        AgentKnowledgeReference cited = reference("faq-1",
                "问题：暑假安排是什么？答案：2026 年暑假安排为 7 月 1 日到 8 月 15 日。");

        List<AgentKnowledgeReference> references = matcher.match(
                "根据查询到的信息，2026 年暑假安排为：7 月 1 日至 8 月 15 日。",
                List.of(unrelated, cited));

        Assert.assertEquals(1, references.size());
        Assert.assertEquals("faq-1", references.get(0).getDocumentId());
    }

    /**
     * 即使只有一个候选，如果答案缺少文本支撑，也不应强行返回引用。
     */
    @Test
    public void shouldNotGuessOnlyCandidateWithoutEvidence() {
        AgentKnowledgeReference candidate = reference("doc-1", "唯一命中片段");

        List<AgentKnowledgeReference> references = matcher.match("final answer", List.of(candidate));

        Assert.assertTrue(references.isEmpty());
    }

    /**
     * 多候选无明确文本支撑时，应返回空引用列表。
     */
    @Test
    public void shouldNotGuessMultipleCandidatesWithoutEvidence() {
        AgentKnowledgeReference first = reference("doc-1", "周期结算金额等于订单金额减去佣金金额。");
        AgentKnowledgeReference second = reference("doc-2", "权限配置需要绑定角色与数据范围。");

        List<AgentKnowledgeReference> references = matcher.match("final answer", List.of(first, second));

        Assert.assertTrue(references.isEmpty());
    }

    /**
     * 日期数字相同但语义内容不相关时，不应仅凭数字覆盖返回引用。
     */
    @Test
    public void shouldNotMatchByDateNumbersOnly() {
        AgentKnowledgeReference unrelated = reference("doc-1",
                "内部文件 胜意科技AI Infra解决方案 技术文件 武汉科技大学计算机科学与技术学院 "
                        + "2026年1月 目录 1 总体目标 4 2 总体方案 5 3 详细方案 6 3.2 流程与编排 7 "
                        + "3.3 知识库管理 8 3.4 智能文档解析 9");

        List<AgentKnowledgeReference> references = matcher.match(
                "根据系统中的信息，2026 年暑假的安排是：2026 年 7 月 1 日到 8 月 15 日。",
                List.of(unrelated));

        Assert.assertTrue(references.isEmpty());
    }

    /**
     * 长篇多主题汇总回答应按独立条目匹配引用，避免整篇答案稀释局部证据。
     */
    @Test
    public void shouldMatchReferencesFromLongMultiTopicAnswer() {
        AgentKnowledgeReference checkIn = reference("faq-check-in",
                "问题：最早入住酒店时间说明 答案：最早入住时间为入住日当天下午14:00，提前到店按房间状况安排。");
        AgentKnowledgeReference luggage = reference("faq-luggage",
                "问题：酒店寄存行李服务说明 答案：离店客人通常可免费寄存2天，第3天起收费。");
        AgentKnowledgeReference unrelated = reference("faq-unrelated",
                "问题：会员卡如何补办 答案：请携带本人证件到指定服务网点申请补办。");

        String answer = "根据知识库整理，主要主题如下：\n"
                + "一、预订与支付：官方渠道包括APP、微信小程序和客服电话。\n"
                + "二、入住与退房：最早入住时间为下午14:00，提前到店按房态安排。\n"
                + "三、酒店服务：离店后行李通常可免费寄存2天，第3天起收费。\n"
                + "四、会员商城：可使用彩虹如愿豆兑换商品。";

        List<AgentKnowledgeReference> references = matcher.match(
                answer, List.of(unrelated, checkIn, luggage));

        Assert.assertEquals(2, references.size());
        Assert.assertTrue(references.stream().anyMatch(reference -> "faq-check-in".equals(reference.getDocumentId())));
        Assert.assertTrue(references.stream().anyMatch(reference -> "faq-luggage".equals(reference.getDocumentId())));
    }

    /**
     * 空答案或空候选不应返回引用。
     */
    @Test
    public void shouldReturnEmptyWhenAnswerOrCandidatesEmpty() {
        AgentKnowledgeReference candidate = reference("doc-1", "问题：暑假安排是什么？答案：7月1日到8月15日。");

        Assert.assertTrue(matcher.match("", List.of(candidate)).isEmpty());
        Assert.assertTrue(matcher.match("暑假安排是什么？", List.of()).isEmpty());
    }

    private AgentKnowledgeReference reference(String documentId, String chunkContent) {
        AgentKnowledgeReference reference = new AgentKnowledgeReference();
        reference.setKnowledgeId("kb-1");
        reference.setKnowledgeName("知识库");
        reference.setDocumentId(documentId);
        reference.setDocumentName(documentId + ".md");
        reference.setChunkId(documentId + "-chunk");
        reference.setChunkContent(chunkContent);
        return reference;
    }
}
