package com.gzzm.lobster.thread;

import com.gzzm.lobster.common.MessageRole;
import net.cyan.thunwind.annotation.ColumnDescription;
import net.cyan.thunwind.annotation.Entity;
import net.cyan.thunwind.annotation.Index;

import java.util.Date;

/**
 * Thread 内的消息 / Message inside a thread.
 *
 * <p>这是 transcript 事实层。短文本直存 {@code content}，
 * 超过 4KB 的大 payload 通过 {@code contentRef} 外置到 ContentStore，
 * 打开 thread 时按需读取以避免内存膨胀。
 * <br>
 * This is the transcript source of truth. Short text is stored in
 * {@code content}; payloads larger than 4KB are externalized via
 * {@code contentRef} to the ContentStore and loaded on demand.
 */
@Entity(table = "AI_THREAD_MESSAGE", keys = "messageId")
public class ThreadMessage {

    @ColumnDescription(type = "varchar(40)")
    private String messageId;

    @Index
    @ColumnDescription(type = "varchar(40)", nullable = false)
    private String threadId;

    private MessageRole role;

    /** 短内容直存（≤4KB），超长内容置 null 并走 contentRef / Short content inline. */
    @ColumnDescription(type = "blob")
    private String content;

    /** 大 payload 外置引用 / External content reference. */
    @ColumnDescription(type = "varchar(200)")
    private String contentRef;

    /** 可选 content-type / Optional MIME / content type. */
    @ColumnDescription(type = "varchar(80)")
    private String contentType;

    /** 来源：user_input / assistant / tool_result / external_event / pending_resolve. */
    @ColumnDescription(type = "varchar(40)")
    private String source;

    /** 所属 run（可空；系统消息可能不属于 run）/ Related run id (nullable). */
    @ColumnDescription(type = "varchar(40)")
    private String runId;

    /** 工具调用 id，用于对齐 OpenAI / Ollama tool_call_id. */
    @ColumnDescription(type = "varchar(80)")
    private String toolCallId;

    /** 工具名（role=tool 时必填）/ Tool name when {@code role == tool}. */
    @ColumnDescription(type = "varchar(220)")
    private String toolName;

    /** 单轮内 assistant 的工具调用 JSON 字符串 / Tool calls JSON (for role=assistant). */
    @ColumnDescription(type = "blob")
    private String toolCallsJson;

    /**
     * Assistant 消息的思考内容（thinking-mode 模型专用）/ Reasoning content for thinking-mode models.
     *
     * <p>DeepSeek-v4-flash / deepseek-reasoner / Qwen-QwQ 等 thinking 模型会在响应里
     * 返回 {@code reasoning_content} 字段——多轮对话时**必须把历史的 reasoning_content 也发回**
     * 给 API，否则 DeepSeek 返 400 "The `reasoning_content` in the thinking mode must be
     * passed back to the API.". 这里持久化存储，ContextAssembler 装回 LobsterMessage，
     * adapter 序列化到 OpenAI 请求的 assistant 消息上.
     *
     * <p>仅 role=assistant 使用；其它角色该字段为 null.
     */
    @ColumnDescription(type = "blob")
    private String reasoningContent;

    /** 序号，保证同 thread 内严格有序 / Monotonic sequence within a thread. */
    @ColumnDescription(type = "number(18)", nullable = false, defaultValue = "0")
    private Long seq;

    /** 附件 JSON：[{mediaId, kind}]（多模态）/ Attachments JSON for multimodal input. */
    @ColumnDescription(type = "varchar(2000)")
    private String attachmentsJson;

    private Date createTime;

    public ThreadMessage() {}

    public String getMessageId() { return messageId; }
    public void setMessageId(String messageId) { this.messageId = messageId; }
    public String getThreadId() { return threadId; }
    public void setThreadId(String threadId) { this.threadId = threadId; }
    public MessageRole getRole() { return role; }
    public void setRole(MessageRole role) { this.role = role; }
    public String getContent() { return content; }
    public void setContent(String content) { this.content = content; }
    public String getContentRef() { return contentRef; }
    public void setContentRef(String contentRef) { this.contentRef = contentRef; }
    public String getContentType() { return contentType; }
    public void setContentType(String contentType) { this.contentType = contentType; }
    public String getSource() { return source; }
    public void setSource(String source) { this.source = source; }
    public String getRunId() { return runId; }
    public void setRunId(String runId) { this.runId = runId; }
    public String getToolCallId() { return toolCallId; }
    public void setToolCallId(String toolCallId) { this.toolCallId = toolCallId; }
    public String getToolName() { return toolName; }
    public void setToolName(String toolName) { this.toolName = toolName; }
    public String getToolCallsJson() { return toolCallsJson; }
    public void setToolCallsJson(String toolCallsJson) { this.toolCallsJson = toolCallsJson; }
    public String getReasoningContent() { return reasoningContent; }
    public void setReasoningContent(String reasoningContent) { this.reasoningContent = reasoningContent; }
    public Long getSeq() { return seq; }
    public void setSeq(Long seq) { this.seq = seq; }
    public String getAttachmentsJson() { return attachmentsJson; }
    public void setAttachmentsJson(String attachmentsJson) { this.attachmentsJson = attachmentsJson; }
    public Date getCreateTime() { return createTime; }
    public void setCreateTime(Date createTime) { this.createTime = createTime; }
}
