package agent

import (
	"strings"
	"testing"
)

// TestFallbackSummary 验证 LLM 压缩失败时的兜底摘要：
// 纯文本、保留最近消息要点、无孤立 tool 消息。
func TestFallbackSummary(t *testing.T) {
	history := []Message{
		{Role: "user", Content: stringPointer("第一轮任务：分析日志")},
		{Role: "assistant", Content: stringPointer("已确认方案，开始执行")},
		{Role: "assistant", ToolCalls: []ToolCall{{ID: "c1", Type: "function", Function: ToolFunctionCall{Name: "run_bash", Arguments: `{"command":"ls"}`}}}},
		{Role: "tool", ToolCallID: "c1", Content: stringPointer("file.txt")},
		{Role: "user", Content: stringPointer("继续，报告结果")},
	}
	summary := fallbackSummary(history)
	if summary == "" {
		t.Fatal("fallbackSummary 不应返回空")
	}
	if strings.Contains(summary, "tool_calls") || strings.Contains(summary, "\"id\"") {
		t.Fatalf("fallbackSummary 必须为纯文本，不得包含结构化 tool_calls: %s", summary)
	}
	if !strings.Contains(summary, "用户") || !strings.Contains(summary, "助手") || !strings.Contains(summary, "工具结果") {
		t.Fatalf("fallbackSummary 应包含用户/助手/工具结果要点: %s", summary)
	}
	if !strings.Contains(summary, "调用了 run_bash") {
		t.Fatalf("fallbackSummary 应记录工具调用名: %s", summary)
	}
}

// TestCompactionInputBudget 验证单次压缩输入预算随窗口放大而放大，
// 且始终留出输出与安全余量。
func TestCompactionInputBudget(t *testing.T) {
	a := &Agent{cfg: Config{MaxContextTokens: 96000, CompactionTokens: 4096, StreamOutputTokens: 4096}}
	budget := a.compactionInputBudget()
	if budget <= 0 || budget >= 96000 {
		t.Fatalf("预算应介于 (0, 96000)，got %d", budget)
	}
	// 窗口放大后预算也应放大
	a2 := &Agent{cfg: Config{MaxContextTokens: 48000, CompactionTokens: 4096, StreamOutputTokens: 4096}}
	if a2.compactionInputBudget() >= budget {
		t.Fatalf("窗口更大的预算应更大，got %d vs %d", a2.compactionInputBudget(), budget)
	}
	// 极端小窗口时兜底下限 4096
	a3 := &Agent{cfg: Config{MaxContextTokens: 2000, CompactionTokens: 512, StreamOutputTokens: 512}}
	if got := a3.compactionInputBudget(); got < 4096 {
		t.Fatalf("小窗口预算应有下限，got %d", got)
	}
}

// TestEstimateMessageTokens 验证单条消息估算包含 content、tool_calls 与开销。
func TestEstimateMessageTokens(t *testing.T) {
	m := Message{
		Role:    "assistant",
		Content: stringPointer("你好"),
		ToolCalls: []ToolCall{{
			ID: "c1", Type: "function",
			Function: ToolFunctionCall{Name: "read_file", Arguments: `{"path":"a.txt"}`},
		}},
	}
	n := estimateMessageTokens(m)
	if n <= 0 {
		t.Fatalf("估算应大于 0，got %d", n)
	}
	plain := estimateMessageTokens(Message{Role: "user", Content: stringPointer("你好")})
	if n <= plain {
		t.Fatalf("含 tool_calls 的消息估算应大于纯文本，got %d vs %d", n, plain)
	}
}
