package com.gzzm.lobster.context;

import com.gzzm.lobster.common.MemoryCategory;
import com.gzzm.lobster.memory.PersonalMemory;

import java.util.List;

/**
 * MemoryIndexPolicy —— 记忆索引注入策略 / Memory index injection policy.
 *
 * <p>参考 Claude Code 的 MEMORY.md：把全量记忆"索引行"作为稳定前缀常驻 system，
 * 每条只输出 {@code name + description + category + memoryId}；正文由 LLM 按需用
 * {@code memory_read} 工具拉取。不依赖本轮用户输入——彻底消除 memory 段的抖动，
 * 利于 prompt-cache 命中。
 *
 * <p>Inspired by Claude Code's MEMORY.md: inject a compact, stable index section
 * listing every memory's hook, and let the LLM pull full bodies on demand via
 * {@code memory_read}. This removes any dependency on the current user input and
 * keeps the prefix stable for prompt-cache.
 */
public final class MemoryIndexPolicy {

    /** 单行上限；超出按字符截断并附 …。 */
    private static final int MAX_LINE_CHARS = 150;
    /** 整段软上限，避免全量索引撑爆 system。 */
    private static final int MAX_SECTION_CHARS = 12_000;

    private final int maxEntries;

    public MemoryIndexPolicy(int maxEntries) {
        this.maxEntries = maxEntries <= 0 ? 120 : maxEntries;
    }

    /**
     * 渲染索引段。无条目时返回空串——调用方自行决定是否注入。
     */
    public String buildSection(List<PersonalMemory> entries) {
        if (entries == null || entries.isEmpty()) return "";

        StringBuilder sb = new StringBuilder();
        sb.append("## 个人记忆索引\n");
        sb.append("以下为当前用户的全部长期记忆「钩子」。判断某条与当前任务相关时，调用 ")
          .append("`memory_read` 工具并传入 memoryId 取完整正文。写入新记忆请用 `memory_write`，")
          .append("且在调用前确认 name/description 足以让未来对话判断相关性。\n\n");

        int count = 0;
        for (PersonalMemory m : entries) {
            if (count >= maxEntries) break;
            String line = renderLine(m);
            if (line == null) continue;
            if (sb.length() + line.length() + 1 > MAX_SECTION_CHARS) break;
            sb.append(line).append('\n');
            count++;
        }
        if (count == 0) return "";
        return sb.toString();
    }

    private String renderLine(PersonalMemory m) {
        if (m == null) return null;
        String name = safe(m.getName());
        String desc = safe(m.getDescription());
        if (name.isEmpty() && desc.isEmpty()) return null;
        MemoryCategory cat = m.getCategory();
        String catStr = cat == null ? "user" : cat.name();
        String id = m.getMemoryId() == null ? "" : m.getMemoryId();

        // 格式：- [category] name — description (memoryId)
        StringBuilder line = new StringBuilder();
        line.append("- [").append(catStr).append("] ")
            .append(name)
            .append(" — ").append(desc)
            .append(" (").append(id).append(')');
        if (line.length() > MAX_LINE_CHARS) {
            return line.substring(0, MAX_LINE_CHARS - 1) + "…";
        }
        return line.toString();
    }

    private String safe(String s) { return s == null ? "" : s; }

    public int getMaxEntries() { return maxEntries; }
}
