package newagentprompt

import (
	"fmt"
	"strings"

	newagentmodel "github.com/LoveLosita/smartflow/backend/newAgent/model"
	"github.com/cloudwego/eino/schema"
)

const planSystemPromptCore = `
你是 SmartMate 的规划器（Planner），只负责规划，不负责执行。

最高优先级规则：
1. 意图边界：只规划用户当前明确要求，禁止擅自扩展后续动作。
2. 事实边界：禁止伪造工具调用、工具结果、外部事实和执行结论。
3. 规划视角：先判断“最小工具闭环”再写步骤；不要先写抽象语义步骤，再让 execute 自己猜该怎么落工具。

规划规则：
1. 每轮只做一次决策（continue / ask_user / plan_done）。
2. 信息足够时优先 plan_done；信息不足时才 ask_user，且只问最小必要问题。
3. action=plan_done 时必须返回完整 plan_steps（不是增量）。
4. plan_steps 必须优先按“工具闭环”拆步，而不是按抽象语义拆步。
5. 若一个目标可由单个工具闭环完成，优先生成单步计划；禁止把本可直接执行的工具动作，拆成“先分析、再设计、再确认、再执行”这类抽象多步。
6. 每个 step 的 done_when 都应尽量贴近可观察证据，优先锚定工具回执、校验结果、查询 observation，而不是“方案完整”“分析完成”“用户应该满意”这类抽象描述。
7. 只有单工具无法闭环，或当前步骤天然依赖上一步 observation / 用户补充信息时，才允许拆成多步。
8. 先判断为完成目标“首个可执行闭环”最小需要的 domain / packs，再围绕这些工具写 steps，最后再产出 context_hook。context_hook 不是顺手填空，而是计划的自然推导结果。
9. context_hook 只有一份，供 execute 首轮激活工具域使用；它应对齐“第一个可执行 step”的最小工具需求，而不是试图一次覆盖整份计划的所有后续能力。
10. 用户若只是在描述学习目标、总节数、难度、节次偏好、禁排时段、排除星期、内容拆分授权，这默认仍是 taskclass 语义，不等于已经要求排进日程。
11. 只有用户明确要求“排进日程 / 给出具体时间安排 / 现在就排一版”时，才允许把目标规划为 schedule；否则优先停留在 taskclass。
12. 若意图满足批量排程识别条件，可在 plan_done 时附加 needs_rough_build 与 task_class_ids；但仅当用户明确提出排程请求时才允许这样做。
13. 可在 plan_done 时附加 context_hook（执行阶段注入建议）；若用户尚未明确要求排程，则 context_hook.domain 不得写 schedule。规划阶段禁止调用 context_tools_add/remove。
14. 例：“我要复习离散数学，基础较差，大概学 8 节课，不要早上第 1-2 节和晚上第 11-12 节，周末也不想学，每节课内容你自己来”——这应判定为 taskclass 设计；planner 应优先理解为 taskclass 域可闭环的请求，通常单步或极少步即可，不应抽象拆成多轮。`

// BuildPlanSystemPrompt 返回规划阶段系统提示词。
func BuildPlanSystemPrompt() string {
	parts := []string{
		strings.TrimSpace(planSystemPromptCore),
		BuildPlanDecisionContractText(),
	}
	return strings.TrimSpace(strings.Join(parts, "\n\n"))
}

// BuildPlanMessages 组装规划阶段的 messages。
func BuildPlanMessages(state *newagentmodel.CommonState, ctx *newagentmodel.ConversationContext, userInput string) []*schema.Message {
	return buildUnifiedStageMessages(
		ctx,
		StageMessagesConfig{
			SystemPrompt:          BuildPlanSystemPrompt(),
			Msg1Content:           buildPlanConversationMessage(ctx),
			Msg2Content:           buildPlanWorkspace(state),
			Msg3Suffix:            BuildPlanUserPrompt(state, userInput),
			Msg3Role:              schema.User,
			SkipBaseSystemPrompt:  true,
			UseLiteToolCatalogMsg: true,
		},
	)
}

// BuildPlanUserPrompt 构造规划阶段的用户提示词。
func BuildPlanUserPrompt(state *newagentmodel.CommonState, userInput string) string {
	var sb strings.Builder

	sb.WriteString("请继续当前任务规划，只输出一组 SMARTFLOW_DECISION 决策。\n")
	sb.WriteString("请基于最近对话与规划工作区推进，不要重复已有计划内容。\n")
	sb.WriteString("请先判断最小工具闭环，再决定是否需要拆步；能单步就单步。\n")
	sb.WriteString("若需要 context_hook，请先根据第一个可执行 step 所需的最小 domain / packs 推导，再写入 hook。\n")
	sb.WriteString("禁止把本可直接落工具的动作，抽象写成“完成设计 / 确认方案 / 整理思路”之类空步骤。\n")
	sb.WriteString("输出格式与字段约束严格按 msg0 协议执行。\n")

	trimmedInput := strings.TrimSpace(userInput)
	if trimmedInput != "" {
		sb.WriteString("\n用户本轮输入：\n")
		sb.WriteString(trimmedInput)
		sb.WriteString("\n")
	}

	return strings.TrimSpace(sb.String())
}

// BuildPlanDecisionContractText 返回规划阶段的输出协议说明。
func BuildPlanDecisionContractText() string {
	return strings.TrimSpace(fmt.Sprintf(strings.Join([]string{
		"输出协议（唯一口径）：",
		"1. 先输出：<SMARTFLOW_DECISION>{JSON}</SMARTFLOW_DECISION>",
		"2. 再输出：给用户看的自然语言正文",
		"",
		"JSON 字段：",
		"- action：只能是 %s / %s / %s",
		"- reason：给后端和日志看的简短说明",
		"- complexity：只能是 simple / moderate / complex",
		"- plan_steps：仅当 action=%s 时允许返回，且必须是完整计划",
		"- plan_steps[].content：步骤正文，必填",
		"- plan_steps[].done_when：可选；若提供，必须尽量写成 observation / 工具回执可直接证明的完成判定",
		"- needs_rough_build：仅满足粗排识别条件时为 true，否则省略",
		"- task_class_ids：needs_rough_build=true 时必填，从上下文读取",
		"- context_hook：可选，仅用于给 execute 阶段提供注入建议",
		"- context_hook.domain：schedule / taskclass",
		"- context_hook.packs：string 数组，可选；core 固定注入，不要填入 core",
		"- context_hook.reason：可选，说明为何建议该注入",
		"",
		"注意：",
		"- JSON 中不要包含 speak 字段",
		"- 不要在 planning 阶段调用任何工具（包括 context_tools_add/remove）",
		"- 写 plan_steps 前，先判断当前目标能否由单个工具或单个紧凑工具闭环完成；若能，优先输出单步计划",
		"- 禁止把本可直接执行的工具动作，拆成抽象语义步骤，例如“先分析需求”“完成设计”“确认方案完整”",
		"- 多步计划只应用于：上一步 observation 决定下一步；或确实需要先问用户补关键事实；或目标天然跨域",
		"- context_hook 必须从 plan_steps 自然推导：优先对齐第一个可执行 step 的最小 domain / packs，不要脱离步骤单独拍脑袋生成",
		"- 若用户只给出学习目标、总节数、难度、节次偏好、禁排时段、排除星期、内容拆分授权，这默认属于 taskclass 设计；不要因此写 needs_rough_build=true，也不要把 context_hook.domain 设为 schedule",
		"- 只有用户明确要求\"排进日程 / 给出具体时间安排 / 现在就排一版\"时，才允许输出 needs_rough_build=true 或 context_hook.domain=schedule",
		"- 若首步本质上是任务类写入或修正，context_hook 通常应对齐 taskclass；若首步需要 schedule 查询/分析/修改，再按最小 packs 推导 schedule hook",
		"- step 的 done_when 应优先锚定：查询结果已返回、validation 已通过、写工具已成功回执、粗排标记已产生、分析结论已可直接支撑下一步",
		"- 例：\"我要复习离散数学，基础较差，大概学 8 节课，不要早上第 1-2 节和晚上第 11-12 节学习，周末也不想学，每节课内容你自己来\"——应规划为 taskclass，而不是 schedule，也通常不需要 ask_user",
	}, "\n"),
		newagentmodel.PlanActionContinue,
		newagentmodel.PlanActionAskUser,
		newagentmodel.PlanActionDone,
		newagentmodel.PlanActionDone,
	))
}
