package promptcompiler

import (
	"fmt"
	"os"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

func TestRuntimeToolPrefixUsesKimiMCPNaming(t *testing.T) {
	t.Parallel()
	require.Equal(t, "mcp__agentteam__message_send", toolNamePrefix("kimi", "agentteam")("message_send"))
}

func TestResolveMemoryPrompt(t *testing.T) {
	t.Parallel()

	t.Run("prefers remote memory index when present", func(t *testing.T) {
		t.Parallel()
		workDir := t.TempDir()
		require.NoError(t, os.MkdirAll(filepath.Join(workDir, "memory"), 0o755))
		require.NoError(t, os.WriteFile(filepath.Join(workDir, remoteMemoryIndexPath), []byte("# remote\n"), 0o644))
		require.NoError(t, os.WriteFile(filepath.Join(workDir, localMemoryIndexPath), []byte("# local\n"), 0o644))

		prompt := resolveMemoryPrompt(workDir)
		require.Equal(t, "remote", prompt.Kind)
		require.Equal(t, remoteMemoryIndexPath, prompt.IndexPath)
		require.Equal(t, templatePlatformRemoteMemory, prompt.TemplatePath)
		require.Contains(t, prompt.Text, "memory/MEMORY.md")
		require.Contains(t, prompt.Text, "Index:")
		require.Contains(t, prompt.Text, filepath.Join(workDir, "memory"))
		require.Contains(t, prompt.Text, "Memory root:")
		require.Contains(t, prompt.Text, "Never write Memory to root `MEMORY.md`")
		require.Contains(t, prompt.Text, "`profiles/user.md`")
		require.Contains(t, prompt.Text, "Local Knowledge Index")
		require.Contains(t, prompt.Text, "verify the file before confirming")
		require.Contains(t, prompt.Text, "Explicit requests: edit here")
		require.Contains(t, prompt.Text, "Local recorded is not cloud-synced")
		require.Contains(t, prompt.Text, "claim cloud sync only when confirmed")
		require.Contains(t, prompt.Text, "Consolidate")
		require.NotContains(t, prompt.Text, "kb/index.md")
		require.LessOrEqual(t, EstimateTokens(prompt.Text), 200)
	})

	t.Run("falls back to local memory when remote index is missing", func(t *testing.T) {
		t.Parallel()
		workDir := t.TempDir()
		require.NoError(t, os.WriteFile(filepath.Join(workDir, localMemoryIndexPath), []byte("# local\n"), 0o644))

		prompt := resolveMemoryPrompt(workDir)
		require.Equal(t, "local", prompt.Kind)
		require.Equal(t, localMemoryIndexPath, prompt.IndexPath)
		require.Equal(t, templatePlatformWorkspaceMemory, prompt.TemplatePath)
		require.Contains(t, prompt.Text, "**`MEMORY.md`**")
		require.Contains(t, prompt.Text, "kb/")
		require.Contains(t, prompt.Text, "consolidate the touched scope")
		require.Contains(t, prompt.Text, "Insights/Reflections")
		require.Contains(t, prompt.Text, "require a successful local Memory edit before confirmation")
		require.Contains(t, prompt.Text, "otherwise say not recorded")
		require.LessOrEqual(t, EstimateTokens(prompt.Text), 180)
	})

	t.Run("empty workdir falls back to local memory", func(t *testing.T) {
		t.Parallel()
		prompt := resolveMemoryPrompt("")
		require.Equal(t, "local", prompt.Kind)
		require.Equal(t, localMemoryIndexPath, prompt.IndexPath)
	})
}

func TestBuildWorkspaceMemoryTextSubstitutesPath(t *testing.T) {
	t.Parallel()

	t.Run("remote prompt names the supplied path", func(t *testing.T) {
		t.Parallel()
		workDir := t.TempDir()
		text := buildRemoteWorkspaceMemoryText(workDir, remoteMemoryIndexPath)
		require.Contains(t, text, "memory/MEMORY.md")
		require.Contains(t, text, filepath.Join(workDir, "memory"))
		require.Contains(t, text, "(`memory/`)")
		require.Contains(t, text, "Never write Memory to root `MEMORY.md`")
		require.NotContains(t, text, "{{.")
	})

	t.Run("local prompt names the supplied path", func(t *testing.T) {
		t.Parallel()
		text := buildLocalWorkspaceMemoryText(localMemoryIndexPath)
		require.Contains(t, text, "**`MEMORY.md`**")
		require.Contains(t, text, "consolidate the touched scope")
		require.NotContains(t, text, "{{.")
	})
}

func TestBuildDeferredContinuationPrompt_IncludesAgentHeaderMarker(t *testing.T) {
	prompt := buildDeferredContinuationPrompt(types.InboxMessage{
		MessageID:     "msg-2468",
		Target:        "#ops:deadbeef",
		AuthorProfile: types.AuthorProfile{ActorType: "agent", DisplayName: "reviewer"},
		Content:       "please address the comments",
		Timestamp:     "",
		ChannelType:   "thread",
		ChannelName:   "thread-deadbeef",
	}, 2, "message_send", "message_send", "")

	require.Contains(t, prompt, "Deferred follow-up after a busy turn:")
	require.Contains(t, prompt, "[target=#ops:deadbeef msg=msg-2468 time=- type=agent] @reviewer: please address the comments")
	require.Contains(t, prompt, "There are 2 more deferred message(s) queued behind it")
	require.Contains(t, prompt, "Agent-authored wake guidance:")
	require.Contains(t, prompt, "This agent-authored mention is a real wake")
	require.Contains(t, prompt, "Do not send a reply whose only purpose is acknowledging receipt")
	require.Contains(t, prompt, "Reply using message_send with the exact target from the message header, or take action as needed.")
}

func TestBuildDeferredContinuationPrompt_ContextRecoveryUsesExactTarget(t *testing.T) {
	prompt := buildDeferredContinuationPrompt(types.InboxMessage{
		MessageID:     "ctx-recovery-1",
		Target:        "#ops:deadbeef",
		AuthorProfile: types.AuthorProfile{ActorType: "system", DisplayName: "daemon"},
		Content:       "Recover local context only.",
		MessageType:   "context_recovery",
	}, 0, "mcp__agentteam__message_send", "mcp__agentteam__message_send", "")

	require.Contains(t, prompt, "Recover local context only")
	require.Contains(t, prompt, "Reply using mcp__agentteam__message_send with the exact target from the message header")
}

func TestPromptCompilerUsesAgentHandleForPromptAuthors(t *testing.T) {
	message := types.InboxMessage{
		MessageID: "msg-2468",
		Target:    "#ops:deadbeef",
		AuthorProfile: types.AuthorProfile{
			ActorType:   "agent",
			PrincipalID: "agt_worker",
			MemberID:    "mem_worker",
			TargetID:    "hh-worker1",
			DisplayName: "研发",
		},
		Content:     "please continue",
		Timestamp:   "",
		ChannelType: "thread",
		ChannelName: "thread-deadbeef",
	}

	prompt := buildDeferredContinuationPrompt(message, 0, "message_send", "message_send", "")
	require.Contains(t, prompt, "[target=#ops:deadbeef msg=msg-2468 time=- type=agent] @hh-worker1: please continue")
	require.Contains(t, prompt, "fromDisplayName: 研发")
	require.NotContains(t, prompt, "@研发:")

	deliveryText := renderDeliveryText(&DeliveryEnvelope{
		Binding:           ConversationBindingRef{Target: "#ops:deadbeef"},
		Correlation:       DeliveryCorrelation{MessageID: "msg-2468"},
		AuthorMentionName: "hh-worker1",
		AuthorDisplayName: "研发",
		PayloadText:       "please continue",
	})
	require.Contains(t, deliveryText, "[target=#ops:deadbeef msg=msg-2468] @hh-worker1: please continue")
	require.Contains(t, deliveryText, "fromDisplayName: 研发")
	require.NotContains(t, deliveryText, "@研发:")
}

func TestFormatPromptMessageUsesWorkflowPresentationAuthor(t *testing.T) {
	message := types.InboxMessage{
		MessageID: "msg-workflow-callback",
		Target:    "#agentteam",
		AuthorProfile: types.AuthorProfile{
			ActorType:   "human",
			PrincipalID: "usr_owner",
			MemberID:    "mem_owner",
			DisplayName: "Arithmetic E2E Owner",
		},
		Source: types.ContributionSource{
			Kind:    "workflow_run",
			RefType: "workflow_run",
			RefID:   "wfr_calculator",
			RunID:   "wfr_calculator",
			EventID: "wrf_calculator_effect",
			Label:   "Parallel calculator",
		},
		Content: "Compute the next work item.",
	}

	prompt := FormatPromptMessage("#agentteam", message, "message_send")
	require.Contains(t, prompt, "@Parallel calculator: Compute the next work item.")
	require.NotContains(t, prompt, "Arithmetic E2E Owner")
}

func TestBuildWakeOrResumeText_FormatsTeamContexts(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam",
		},
		nil,
		types.InboxMessage{
			MessageID:     "msg-team-context",
			Target:        "#agentteam",
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "alice"},
			Content:       "@core-team please prepare a complete responsibility proposal for this Team and submit it for approval",
			TeamContexts: []types.TeamContext{{
				TeamID:            "team_core",
				TeamHandle:        "core-team",
				TeamDisplayName:   "Core Team",
				TeamDescription:   "Act as core.\nKeep responses concise.",
				TeamMemberID:      "wtm_agent",
				Role:              "leader",
				RoleLabel:         "Coordinator",
				RoleOverlayPrompt: "Build it.",
				MemberRoster: []types.TeamRosterMember{{
					TeamMemberID:      "wtm_agent",
					ActorKind:         "agent",
					ActorID:           "mem_agent",
					DisplayName:       "AgentTeam Agent",
					Handle:            "agentteam-agent",
					Role:              "leader",
					RoleLabel:         "Coordinator",
					RoleOverlayPrompt: "Build it.",
					WakeOnTeamMention: true,
					DeliveryKind:      "wake",
					Self:              true,
				}, {
					TeamMemberID:      "wtm_reviewer",
					ActorKind:         "agent",
					ActorID:           "mem_reviewer",
					DisplayName:       "Review Agent",
					Handle:            "review-agent",
					Role:              "member",
					RoleLabel:         "Reviewer",
					RoleOverlayPrompt: "",
					WakeOnTeamMention: false,
					DeliveryKind:      "agent",
				}},
			}},
		},
		"",
	)

	require.Contains(t, prompt, "team contexts:")
	require.Contains(t, prompt, "- team: @core-team")
	require.Contains(t, prompt, "team id: team_core")
	require.Contains(t, prompt, "team invocation: you were awakened as this Team member. Other Team members may not have been awakened automatically.")
	require.NotContains(t, prompt, "collaboration rules:")
	require.NotContains(t, prompt, "team_invoke_team_id")
	require.NotContains(t, prompt, "workflow-plan-author Skill")
	require.Contains(t, prompt, "Team responsibility updates:")
	require.Contains(t, prompt, "with `agentteam exec team update`; it will request Human approval")
	require.Contains(t, prompt, "When replacing a rejected proposal, use a new requestId.")
	require.Contains(t, prompt, "team duty:\n    Act as core.\n    Keep responses concise.")
	require.NotContains(t, prompt, "description:")
	require.Contains(t, prompt, "your Team role: leader")
	require.Contains(t, prompt, "your role label: Coordinator")
	require.Contains(t, prompt, "your duty:\n    Build it.")
	require.Contains(t, prompt, "team member roster:\n    - @agentteam-agent (AgentTeam Agent) (agent, Coordinator, active participant, self)")
	require.Contains(t, prompt, "      teamMemberId: wtm_agent")
	require.Contains(t, prompt, "      Team role: leader")
	require.Contains(t, prompt, "      duty:\n        Build it.")
	require.Contains(t, prompt, "    - @review-agent (Review Agent) (agent, Reviewer, as-needed participant)")
	require.Contains(t, prompt, "      teamMemberId: wtm_reviewer")
	require.Contains(t, prompt, "      Team role: member")
	require.Contains(t, prompt, "      duty: not configured")
}

func TestFormatTeamContextPromptDetailsKeepsCompleteProposalIDsBeyondRosterLimit(t *testing.T) {
	roster := make([]types.TeamRosterMember, promptTeamRosterMaxItems+1)
	for index := range roster {
		roster[index] = types.TeamRosterMember{
			TeamMemberID:      fmt.Sprintf("wtm_%02d", index),
			ActorKind:         "agent",
			ActorID:           fmt.Sprintf("mem_%02d", index),
			DisplayName:       fmt.Sprintf("Agent %02d", index),
			Handle:            fmt.Sprintf("agent-%02d", index),
			Role:              "member",
			RoleLabel:         fmt.Sprintf("Role %02d", index),
			RoleOverlayPrompt: "Configured duty.",
		}
	}
	roster[0].Role = "leader"
	roster[1].RoleOverlayPrompt = ""
	roster[len(roster)-1].RoleOverlayPrompt = "Overflow duty must stay compact."

	format := func(role string) string {
		return formatTeamContextPromptDetails([]types.TeamContext{{
			TeamID: "team_large", Role: role, MemberRoster: roster,
		}})
	}

	leaderPrompt := format("leader")
	require.Contains(t, leaderPrompt, "    - ... (1 more)")
	require.NotContains(t, leaderPrompt, "Overflow duty must stay compact.")
	require.Contains(t, leaderPrompt, "complete responsibility proposal member index (use every teamMemberId exactly once):")
	require.Contains(t, leaderPrompt, "- wtm_00: @agent-00 (Agent 00); Team role: leader; role label: Role 00")
	require.Contains(t, leaderPrompt, "- wtm_20: @agent-20 (Agent 20); Team role: member; role label: Role 20")

	require.NotContains(t, format("member"), "complete responsibility proposal member index")
	roster[1].RoleOverlayPrompt = "Configured duty."
	require.NotContains(t, format("leader"), "complete responsibility proposal member index")
}

func TestBuildWakeOrResumeText_FormatsTaskDiscussionWakeDetails(t *testing.T) {
	prompt := buildWakeOrResumeText(

		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam",
		},
		nil,
		types.InboxMessage{
			MessageID:     "tsk-1",
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "alice"},
			Content:       "Task discussion update for #t9",
			MessageType:   "task_dispatch",
			Metadata: map[string]any{
				"task": map[string]any{
					"taskNumber":         9,
					"title":              "Fix reply routing",
					"status":             "in_progress",
					"channelRef":         "#agentteam",
					"priority":           "high",
					"description":        "Keep all routine updates on the scope-native task discussion.",
					"acceptanceCriteria": []any{"Use scope-native replies for task progress", "Do not lose the exact channel target"},
					"reviewChecklist":    []any{"Prompt exposes task-specific requirements"},
					"relevantURLs":       []any{"https://example.com/spec"},
				},
				"taskDiscussion": map[string]any{
					"body": "Please keep the reply in the task discussion.",
					"createdBy": map[string]any{
						"name": "alice",
					},
				},
			},
		},
		"",
	)

	require.Contains(t, prompt, "[target=#agentteam msg=tsk-1 time=-] @alice: Task discussion update for #t9")
	require.Contains(t, prompt, `task discussion: #t9 "Fix reply routing"`)
	require.Contains(t, prompt, "channel: #agentteam")
	require.Contains(t, prompt, "task attributes:\nstatus: in_progress\npriority: high\ndescription:\nKeep all routine updates on the scope-native task discussion.")
	require.Contains(t, prompt, "acceptanceCriteria:\n- Use scope-native replies for task progress\n- Do not lose the exact channel target")
	require.Contains(t, prompt, "reviewChecklist:\n- Prompt exposes task-specific requirements")
	require.Contains(t, prompt, "relevantURLs:\n- https://example.com/spec")
	require.Contains(t, prompt, "instruction: Treat these structured task attributes as requirements.")
	require.Contains(t, prompt, "comment by @alice:\nPlease keep the reply in the task discussion.")
	require.Contains(t, prompt, `reply: use mcp__agentteam__message_send with the exact task scope target from the current message header`)
	require.Contains(t, prompt, "Active task target:")
	require.Contains(t, prompt, "- task: #t9 in #agentteam")
	require.Contains(t, prompt, "- source: task_dispatch")
	require.NotContains(t, prompt, `target="#tsk_..."`)
}

func TestBuildWakeOrResumeText_FormatsTaskDispatchAttributesWithoutDiscussion(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam",
		},
		nil,
		types.InboxMessage{
			MessageID:     "tsk-2",
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "alice"},
			Content:       "Assigned #t10 Fill task SPEC",
			MessageType:   "task_dispatch",
			Metadata: map[string]any{
				"task": map[string]any{
					"taskScopeId": "tsk_scope_10",
					"taskNumber":  10,
					"title":       "Fill task SPEC",
					"status":      "todo",
					"channelRef":  "#agentteam",
					"kind":        "feature",
					"priority":    "high",
					"assigneeProfile": map[string]any{
						"actorType":   "agent",
						"principalId": "agent-1",
						"memberId":    "member-1",
						"targetId":    "implementer",
						"displayName": "Implementer",
					},
					// Old rolling payloads may still carry these keys, but the
					// canonical runtime prompt must not expose them.
					"suggestedToYou":     true,
					"reviewer":           map[string]any{"name": "legacy-reviewer"},
					"description":        "Update task structured attributes before implementation.",
					"acceptanceCriteria": []any{"Task attributes are visible on start/assign wakes"},
					"reviewChecklist":    []any{"No full task discussion fetch is required"},
					"workspaceRefs":      []any{"specs/task-attributes.md"},
				},
			},
		},
		"",
	)

	require.Contains(t, prompt, "[target=#agentteam msg=tsk-2 time=-] @alice: Assigned #t10 Fill task SPEC")
	require.Contains(t, prompt, `task dispatch: #t10 "Fill task SPEC"`)
	require.Contains(t, prompt, "channel: #agentteam")
	require.Contains(t, prompt, "task attributes:\nstatus: todo\nkind: feature\npriority: high\nassignee: @implementer\ndescription:\nUpdate task structured attributes before implementation.")
	require.NotContains(t, prompt, "suggestedToYou")
	require.NotContains(t, prompt, "legacy-reviewer")
	require.NotContains(t, prompt, "reviewer:")
	require.Contains(t, prompt, "acceptanceCriteria:\n- Task attributes are visible on start/assign wakes")
	require.Contains(t, prompt, "reviewChecklist:\n- No full task discussion fetch is required")
	require.Contains(t, prompt, "workspaceRefs:\n- specs/task-attributes.md")
	require.Contains(t, prompt, "instruction: Treat these structured task attributes as requirements.")
	require.NotContains(t, prompt, "comment by @")
	require.NotContains(t, prompt, "reply: use mcp_agentteam_add_discussion_entry")
	require.Contains(t, prompt, "Active task target:")
	require.Contains(t, prompt, "- task: #t10 in #agentteam")
	require.Contains(t, prompt, "- scope: #tsk_scope_10")
	require.Contains(t, prompt, "- source: task_dispatch")
}

func TestBuildWakeOrResumeText_FormatsTaskScopeDiscussionMessage(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID:   "tsk_tk8msx2pmcjpw9ujlqn0",
			ChannelType: "task",
			TargetID:    "#tsk_tk8msx2pmcjpw9ujlqn0",
		},
		nil,
		types.InboxMessage{
			MessageID:     "msg-task-scope",
			Target:        "#tsk_tk8msx2pmcjpw9ujlqn0",
			ChannelType:   "task",
			ChannelName:   "tsk_tk8msx2pmcjpw9ujlqn0",
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "alice"},
			Content:       "test3",
			MessageType:   "task_discussion",
		},
		"",
	)

	require.Contains(t, prompt, "[target=#tsk_tk8msx2pmcjpw9ujlqn0 msg=msg-task-scope time=-] @alice: test3")
	require.Contains(t, prompt, "task discussion: scope-native message")
	require.Contains(t, prompt, "scope: #tsk_tk8msx2pmcjpw9ujlqn0")
	require.Contains(t, prompt, `reply: use mcp__agentteam__message_send(target="#tsk_tk8msx2pmcjpw9ujlqn0", content="...") to continue the task discussion`)
	require.NotContains(t, prompt, "mcp_agentteam_add_discussion_entry")
}

func TestBuildWakeOrResumeText_AgentReactionDeliveryOmitsAgentAuthoredWakeGuidance(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam",
		},
		nil,
		types.InboxMessage{
			MessageID:     "rxdlv-1",
			AuthorProfile: types.AuthorProfile{ActorType: "agent", DisplayName: "reviewer"},
			Content:       "reacted +1 to your message",
			MessageType:   "reaction",
			Metadata: map[string]any{
				"reaction": map[string]any{
					"emoji":                "+1",
					"targetMessageId":      "msg-123",
					"targetMessagePreview": "Please review the API shape",
				},
			},
		},
		"",
	)

	require.Contains(t, prompt, "[target=#agentteam msg=rxdlv-1 time=- type=agent] @reviewer: reacted +1 to your message")
	require.Contains(t, prompt, "reaction: +1")
	require.NotContains(t, prompt, "Agent-authored wake guidance:")
	require.NotContains(t, prompt, "This agent-authored mention is a real wake")
	require.NotContains(t, prompt, "message_send(target=")
}

func TestBuildWakeOrResumeText_DirectThreadWakeWithActiveTaskTargetKeepsThreadContext(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam:00000034",
			ActiveTaskTarget: types.ActiveTaskTarget{
				Channel:      "#agentteam",
				TaskNumber:   13,
				Source:       "thread_focus",
				ThreadTarget: "#agentteam:00000034",
			},
		},
		nil,
		types.InboxMessage{
			MessageID:     "msg-1",
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
			Content:       "@codex1 提一个 PR",
		},
		"",
	)

	require.Contains(t, prompt, "This wake is a normal channel/DM/thread message, not a task scope message.")
	require.Contains(t, prompt, `mcp__agentteam__message_history_read(target="#agentteam:00000034", limit=20)`)
	require.Contains(t, prompt, `mcp__agentteam__message_send(target="#agentteam:00000034", content="...")`)
	require.Contains(t, prompt, `Proactive task discussion updates use mcp__agentteam__message_send with the exact task scope target`)
}

func TestBuildWakeOrResumeText_ThreadWakeGuidesHistoryWhenMessageIsUnclear(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam:00000034",
		},
		nil,
		types.InboxMessage{
			MessageID:     "msg-1",
			TargetSeq:     5,
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
			Content:       "@codex1",
		},
		"",
	)

	require.Contains(t, prompt, "Thread context guidance:")
	require.Contains(t, prompt, "If the message is ambiguous")
	require.Contains(t, prompt, `mcp__agentteam__message_history_read(target="#agentteam:00000034", limit=20)`)
	require.Contains(t, prompt, "If the message is already clear and actionable")
	require.NotContains(t, prompt, "thread history:")
}

func TestBuildWakeOrResumeText_ChannelWakeDoesNotRenderThreadContextGuidance(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam",
		},
		nil,
		types.InboxMessage{
			MessageID:     "msg-1",
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
			Content:       "@codex1 提一个 PR",
		},
		"",
	)

	require.NotContains(t, prompt, "Thread context guidance:")
}

func TestBuildWakeOrResumeText_DirectThreadWakeDoesNotRenderStickyClaimedTaskTarget(t *testing.T) {
	prompt := buildWakeOrResumeText(
		"codex",
		ConversationBinding{
			ChannelID: "#agentteam",
			TargetID:  "#agentteam:00000034",
			ActiveTaskTarget: types.ActiveTaskTarget{
				Channel:      "#agentteam",
				TaskNumber:   13,
				Source:       "claimed_task",
				ThreadTarget: "#agentteam:00000034",
			},
		},
		nil,
		types.InboxMessage{
			MessageID:     "msg-1",
			TargetSeq:     1,
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
			Content:       "@codex1 这个普通问题不需要写 task discussion",
			Metadata: map[string]any{
				"replyThreadRoot": map[string]any{
					"messageId":  "49ac6c63bc45e2bf8e1e08868239e247",
					"authorName": "andc5vq",
					"authorProfile": map[string]any{
						"actorType":   "human",
						"displayName": "andc5vq",
					},
					"messageType": "text",
					"content":     "@codex1 请基于 ~/agentteam 最新的 origin/main，定位 agent 看不到 thread 里根消息的问题",
					"timestamp":   "2026-05-17T15:09:05+08:00",
				},
			},
		},
		"",
	)

	require.Contains(t, prompt, "[target=#agentteam:00000034 msg=msg-1 time=-] @wink: @codex1 这个普通问题不需要写 task discussion")
	require.Contains(t, prompt, "thread root:")
	require.Contains(t, prompt, "@andc5vq: @codex1 请基于 ~/agentteam 最新的 origin/main，定位 agent 看不到 thread 里根消息的问题")
	require.NotContains(t, prompt, "thread history:")
	require.NotContains(t, prompt, "thread context:")
	require.NotContains(t, prompt, "Active task target:")
	require.NotContains(t, prompt, "add_discussion_entry(channel=")
}

func TestFormatPromptMessage_ReplyThreadRootReportsOmittedHistory(t *testing.T) {
	for _, test := range []struct {
		targetSeq int64
		want      string
	}{
		{targetSeq: 2, want: "thread history: 1 earlier reply omitted"},
		{targetSeq: 5, want: "thread history: 4 earlier replies omitted"},
	} {
		t.Run(test.want, func(t *testing.T) {
			message := types.InboxMessage{
				MessageID:     "msg-history",
				TargetSeq:     test.targetSeq,
				AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "wink"},
				Content:       "@codex1 please continue",
				Metadata: map[string]any{
					"replyThreadRoot": map[string]any{
						"messageId":  "root-1",
						"authorName": "alice",
						"content":    "Please investigate the issue.",
					},
				},
			}

			prompt := FormatPromptMessage("#agentteam:00000034", message, "message_send")

			require.Contains(t, prompt, "thread root:")
			require.Contains(t, prompt, test.want)
		})
	}
}

func TestBuildDeferredContinuationPrompt_FormatsTaskDiscussionDetails(t *testing.T) {
	prompt := buildDeferredContinuationPrompt(
		types.InboxMessage{
			MessageID:     "tsk-1",
			Target:        "#agentteam",
			AuthorProfile: types.AuthorProfile{ActorType: "human", DisplayName: "alice"},
			Content:       "Task discussion update for #t9",
			MessageType:   "task_dispatch",
			Metadata: map[string]any{
				"task": map[string]any{
					"taskNumber": 9,
					"title":      "Fix reply routing",
					"channelRef": "#agentteam",
				},
				"taskDiscussion": map[string]any{
					"body": "Please keep the reply in the task discussion.",
					"createdBy": map[string]any{
						"name": "alice",
					},
				},
			},
		},
		2,
		"mcp_agentteam_message_send",
		"mcp_agentteam_message_send",
		"",
	)

	require.Contains(t, prompt, "[target=#agentteam msg=tsk-1 time=-] @alice: Task discussion update for #t9")
	require.Contains(t, prompt, "comment by @alice:\nPlease keep the reply in the task discussion.")
	require.Contains(t, prompt, `reply: use mcp_agentteam_message_send with the exact task scope target from the current message header`)
	require.Contains(t, prompt, "use mcp_agentteam_message_send with the task scope target for task discussion replies, or for milestone/channel-thread updates")
}
