package com.infoepoch.pms.agent.platform.chat.domain;

import com.infoepoch.pms.agent.common.utils.SnowFlake;
import com.infoepoch.pms.agent.config.JsonUtils;
import com.infoepoch.pms.agent.platform.agent.domain.Agent;
import com.infoepoch.pms.agent.platform.shared.exception.BusinessException;
import com.infoepoch.pms.agent.platform.shared.response.ResultCode;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 对话消息实体。
 * <p>
 * 代表会话中的一条消息，角色为 user（用户）、assistant（AI）或 tool（工具调用结果）。
 * 消息一经创建不可修改（对话历史的不变性）。
 * </p>
 */
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Conversation {

    /**
     * 消息角色：用户。
     */
    public static final String ROLE_USER = "user";
    /**
     * 消息角色：AI 助手。
     */
    public static final String ROLE_ASSISTANT = "assistant";
    /**
     * 消息角色：工具调用结果。
     */
    public static final String ROLE_TOOL = "tool";

    private String id;

    /**
     * 所属会话（多对一，懒加载）。
     */
    private Session session;

    /**
     * 会话id
     */
    private String sessionId;

    /**
     * 消息角色：user / assistant / tool。
     */
    private String role;

    /**
     * 消息内容。
     */
    private String content;

    /**
     * 关联的智能体（当角色为 tool 时记录触发了哪个智能体），可选。
     */
    private Agent agent;

    /**
     * 智能体id 关联的智能体（当角色为 tool 时记录触发了哪个智能体）
     */
    private String agentId;

    /**
     * 扩展元数据（如 token 用量、耗时等），JSON 格式，可选。
     */
    private Map<String, Object> metadata;

    /**
     * 扩展元数据（如 token 用量、耗时等），json字符串
     */
    private String metadataJson;

    /**
     * 结构化消息块，assistant 消息可选。
     */
    private List<ChatMessageBlock> blocks;

    /**
     * 结构化消息块 json字符串
     */
    private String blocksJson;

    /**
     * 记录创建时间。
     */
    private LocalDateTime createdAt;

    /**
     * 消息所属的上下文线程 ID。
     */
    private String threadId;

    /**
     * 仓储还原
     */
    public Conversation(String id, String sessionId, String role, String content, String agentId, String metadataJson, String blocksJson, LocalDateTime createdAt, String threadId) {
        this.id = id;
        this.sessionId = sessionId;
        this.role = role;
        this.content = content;
        this.agentId = agentId;
        this.metadataJson = metadataJson;
        this.blocksJson = blocksJson;
        this.createdAt = createdAt;
        this.threadId = threadId;
        //还原json实体
        if (StringUtils.hasText(metadataJson)){
            this.metadata = JsonUtils.jsonToMap(metadataJson);
        }
        if (StringUtils.hasText(blocksJson)){
            this.blocks = JsonUtils.jsonToList(blocksJson, ChatMessageBlock.class);
        }
    }

    /**
     * 创建对话消息。消息一经创建不可修改。
     *
     * @param session  所属会话，不能为 null
     * @param role     消息角色（user/assistant/tool），必填
     * @param content  消息内容，必填
     * @param agent    关联智能体，可选
     * @param metadata 扩展元数据，可选
     * @param blocks   结构化消息块，可选
     */
    public Conversation(Session session, String threadId, String role, String content,
                        Agent agent, Map<String, Object> metadata,
                        List<ChatMessageBlock> blocks) {
        if (session == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "所属会话不能为空");
        }
        if (!StringUtils.hasText(threadId)) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "消息所属线程不能为空");
        }
        if (!StringUtils.hasText(role)) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "消息角色不能为空");
        }
        if (!StringUtils.hasText(content)) {
            throw new BusinessException(ResultCode.PARAM_ERROR, "消息内容不能为空");
        }
        this.id = SnowFlake.instant().nextId().toString();
        this.session = session;
        this.sessionId = session.getId();
        this.role = role.trim();
        this.content = content.trim();
        this.agent = agent;
        this.agentId = agent == null ? null : agent.getId();
        this.metadata = metadata;
        this.blocks = blocks;
        this.createdAt = LocalDateTime.now();
        this.threadId = threadId.trim();
        this.metadataJson = toJsonOrNull(metadata);
        this.blocksJson = toJsonOrNull(blocks);
    }

    public static Conversation restore(String id,
                                       Session session,
                                       String role,
                                       String content,
                                       Agent agent,
                                       Map<String, Object> metadata,
                                       List<ChatMessageBlock> blocks,
                                       LocalDateTime createdAt,
                                       String threadId) {
        Conversation conversation = new Conversation();
        conversation.id = id;
        conversation.session = session;
        conversation.sessionId = session == null ? null : session.getId();
        conversation.role = role;
        conversation.content = content;
        conversation.agent = agent;
        conversation.agentId = agent == null ? null : agent.getId();
        conversation.metadata = metadata;
        conversation.blocks = blocks;
        conversation.metadataJson = toJsonOrNull(metadata);
        conversation.blocksJson = toJsonOrNull(blocks);
        conversation.createdAt = createdAt;
        conversation.threadId = threadId;
        return conversation;
    }

    public static Conversation restore(String id,
                                       Session session,
                                       String role,
                                       String content,
                                       Agent agent,
                                       Map<String, Object> metadata,
                                       List<ChatMessageBlock> blocks,
                                       LocalDateTime createdAt) {
        return restore(id, session, role, content, agent, metadata, blocks, createdAt,
                session == null ? null : session.getActiveThreadId());
    }

    public void setSession(Session session) {
        this.session = session;
        this.sessionId = session == null ? null : session.getId();
    }

    public void setAgent(Agent agent) {
        this.agent = agent;
        this.agentId = agent == null ? null : agent.getId();
    }

    /**
     * 创建不带结构化消息块的对话消息。
     *
     * @param session  所属会话，不能为 null
     * @param role     消息角色（user/assistant/tool），必填
     * @param content  消息内容，必填
     * @param agent    关联智能体，可选
     * @param metadata 扩展元数据，可选
     */
    public Conversation(Session session, String role, String content,
                        Agent agent, Map<String, Object> metadata) {
        this(session, session == null ? null : session.getActiveThreadId(), role, content, agent, metadata, null);
    }

    public Conversation(Session session, String threadId, String role, String content,
                        Agent agent, Map<String, Object> metadata) {
        this(session, threadId, role, content, agent, metadata, null);
    }

    private static String toJsonOrNull(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Map<?, ?> map && map.isEmpty()) {
            return null;
        }
        if (value instanceof List<?> list && list.isEmpty()) {
            return null;
        }
        return JsonUtils.objectToJson(value);
    }
}
