package quicknote

import "time"

const (
	// QuickNoteDatetimeMinuteLayout 是“随口记”链路内部统一的分钟级时间格式。
	// 说明：
	// 1) 用于把“当前时间基准”传给模型，避免模型在相对时间推断时出现秒级抖动。
	// 2) 用于日志和调试，读起来比 RFC3339 更直观。
	QuickNoteDatetimeMinuteLayout = "2006-01-02 15:04"

	// quickNoteTimezoneName 是随口记链路默认业务时区。
	// 这里固定为东八区，避免容器运行在 UTC 时把“明天/今晚”解释偏移到错误日期。
	quickNoteTimezoneName = "Asia/Shanghai"

	// QuickNotePriorityImportantUrgent 对应四象限里的“重要且紧急”。
	// 在当前 tasks 表中映射为 priority=1（数值越小优先级越高）。
	QuickNotePriorityImportantUrgent = 1
	// QuickNotePriorityImportantNotUrgent 对应“重要不紧急”。
	QuickNotePriorityImportantNotUrgent = 2
	// QuickNotePrioritySimpleNotImportant 对应“简单不重要”。
	QuickNotePrioritySimpleNotImportant = 3
	// QuickNotePriorityComplexNotImportant 对应“不简单不重要”。
	QuickNotePriorityComplexNotImportant = 4
)

// IsValidTaskPriority 判断优先级是否合法。
// 目前后端任务模型限定为 1~4。
func IsValidTaskPriority(priority int) bool {
	return priority >= QuickNotePriorityImportantUrgent && priority <= QuickNotePriorityComplexNotImportant
}

// PriorityLabelCN 把优先级数值转换为中文标签，便于拼接给用户的自然语言回复。
func PriorityLabelCN(priority int) string {
	switch priority {
	case QuickNotePriorityImportantUrgent:
		return "重要且紧急"
	case QuickNotePriorityImportantNotUrgent:
		return "重要不紧急"
	case QuickNotePrioritySimpleNotImportant:
		return "简单不重要"
	case QuickNotePriorityComplexNotImportant:
		return "不简单不重要"
	default:
		return "未知优先级"
	}
}

// QuickNoteState 是“AI随口记”链路在 graph 节点间传递的统一状态容器。
// 设计目标：
// 1) 把本次请求的上下文收拢到一个结构里，降低节点函数参数散落；
// 2) 让“识别、评估、写库、重试、回复”每一步都可追踪；
// 3) 便于后续扩展打点和可观测字段（例如时间解析失败原因）。
type QuickNoteState struct {
	// 基础上下文：用于日志关联与用户隔离。
	TraceID        string
	UserID         int
	ConversationID string

	// RequestNow 记录“请求进入随口记链路时”的时间基准（分钟级）。
	// 所有相对时间（明天/后天/下周一）都必须基于这个时间计算，
	// 这样同一次请求内不会因为时间流逝产生口径漂移。
	RequestNow time.Time
	// RequestNowText 是 RequestNow 的字符串形式，主要用于 prompt 注入。
	RequestNowText string

	// 用户原始输入（例如：提醒我下周日之前完成大作业）。
	UserInput string

	// 意图判定结果。
	IsQuickNoteIntent bool
	IntentJudgeReason string

	// 结构化抽取结果：由“意图识别/信息抽取”节点写入。
	ExtractedTitle        string
	ExtractedDeadline     *time.Time
	ExtractedDeadlineText string
	// ExtractedUrgencyThreshold 表示“进入紧急象限的分界时间”。
	//
	// 语义说明：
	// 1. 该时间由模型规划后给出，并在后端做解析校验；
	// 2. 到达该时间后，任务可在“读时派生 + 异步落库”链路中被自动平移；
	// 3. 为空表示该任务不参与自动平移。
	ExtractedUrgencyThreshold *time.Time
	ExtractedPriority         int
	// ExtractedBanter 是聚合规划阶段生成的“轻松跟进句”。
	// 该字段非空时，最终回复阶段可直接复用，避免再触发一次独立润色模型调用。
	ExtractedBanter string
	// PlannedBySingleCall 标记本次是否走了“单请求聚合规划”快路径。
	// 用于在后续节点做更激进的性能策略（例如缺失字段时直接本地兜底，避免再触发模型调用）。
	PlannedBySingleCall bool

	// ExtractedPriorityReason 记录优先级评估理由，便于后续排查模型判断是否符合预期。
	ExtractedPriorityReason string
	// DeadlineValidationError 记录时间校验失败原因。
	// 只要该字段非空，就说明用户提供了无法解析的时间表达，本次请求不应落库。
	DeadlineValidationError string

	// 工具调用过程状态：用于重试与故障回溯。
	ToolAttemptCount int
	MaxToolRetry     int
	LastToolError    string

	// 最终持久化结果：由“写库工具”节点回填。
	PersistedTaskID int
	Persisted       bool

	// AssistantReply 是 graph 最终给用户的回复文案。
	AssistantReply string
}

// NewQuickNoteState 创建随口记状态对象并初始化默认重试次数。
func NewQuickNoteState(traceID string, userID int, conversationID, userInput string) *QuickNoteState {
	// 1. 在“进入链路”这一刻固化时间基准，后续所有相对时间都以它为准。
	requestNow := quickNoteNowToMinute()
	return &QuickNoteState{
		TraceID:        traceID,
		UserID:         userID,
		ConversationID: conversationID,
		RequestNow:     requestNow,
		RequestNowText: formatQuickNoteTimeToMinute(requestNow),
		UserInput:      userInput,
		MaxToolRetry:   3,
	}
}

// CanRetryTool 判断当前是否还能继续重试工具调用。
func (s *QuickNoteState) CanRetryTool() bool {
	// 规则：已尝试次数 < 最大重试次数 才允许继续。
	// 这里不做 <=，是为了让“第 MaxToolRetry 次失败后”及时停机并给用户明确反馈。
	return s.ToolAttemptCount < s.MaxToolRetry
}

// RecordToolError 记录一次工具调用失败。
func (s *QuickNoteState) RecordToolError(errMsg string) {
	// 1. 每失败一次都要累加计数，供分支节点判断是否继续重试。
	s.ToolAttemptCount++
	// 2. 保留最后一次错误，便于日志与排障定位“最终失败原因”。
	s.LastToolError = errMsg
}

// RecordToolSuccess 记录一次工具调用成功。
func (s *QuickNoteState) RecordToolSuccess(taskID int) {
	// 1. 成功同样计入尝试次数，便于还原完整调用轨迹。
	s.ToolAttemptCount++
	// 2. 回填 task_id 和成功标志，供后续节点拼接成功回复。
	s.PersistedTaskID = taskID
	s.Persisted = true
	// 3. 成功后清空错误，避免后续误读历史失败信息。
	s.LastToolError = ""
}

// quickNoteLocation 返回随口记链路使用的业务时区。
func quickNoteLocation() *time.Location {
	// 1. 优先加载业务固定时区，保证“明天/今晚”等语义与用户预期一致。
	loc, err := time.LoadLocation(quickNoteTimezoneName)
	if err != nil {
		// 2. 极端情况下回退到系统本地时区，避免因时区加载失败导致链路整体不可用。
		return time.Local
	}
	return loc
}

// quickNoteNowToMinute 返回当前时间并截断到分钟级。
func quickNoteNowToMinute() time.Time {
	// 统一截断到分钟，避免秒级抖动导致“同一次请求前后解析口径不一致”。
	return time.Now().In(quickNoteLocation()).Truncate(time.Minute)
}

// formatQuickNoteTimeToMinute 将时间格式化为分钟级字符串。
func formatQuickNoteTimeToMinute(t time.Time) string {
	// 输出前统一转换到业务时区，避免日志和 prompt 出现跨时区混淆。
	return t.In(quickNoteLocation()).Format(QuickNoteDatetimeMinuteLayout)
}
