package com.agent.management.engine.hermes;

import com.agent.management.common.JsonUtils;
import com.agent.management.engine.NodeStreamSink;
import com.agent.management.model.entity.WorkflowPause;
import com.agent.management.repository.WorkflowPauseRepository;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;

import java.time.LocalDateTime;
import java.util.Optional;

/**
 * Clarify 落库拦截工具。
 *
 * <p>把 {@link NodeStreamSink} 包装一层，在事件流中拦截 {@code clarify_request} 事件，
 * 同步写入 {@link WorkflowPause} 表。供 {@code WorkflowLevelExecutor}（工作流引擎）
 * 与 {@code ChatRunService}（Agent 对话）两条链路共用。</p>
 *
 * <p>容错：DB 写入失败仅记录日志，不阻塞主流程（事件仍会推送给前端）。</p>
 *
 * <p>去重：{@code clarify_id} 列已加唯一索引（防 TOCTOU）。重复写入会被
 * {@link DataIntegrityViolationException} 捕获并视为"已存在"。</p>
 */
@Slf4j
public final class ClarifyHooks {

    private ClarifyHooks() {
    }

    /**
     * 包装一个 sink，使其在发出 {@code clarify_request} 事件时同步落库。
     *
     * @param delegate  原始 sink（事件先转发给它，确保前端实时收到）
     * @param runId     当前 run 的 id
     * @param pauseRepo 暂停点 repository（可为 {@code null}，null 时跳过落库）
     * @return 包装后的 sink
     */
    public static NodeStreamSink wrap(NodeStreamSink delegate, String runId,
                                      WorkflowPauseRepository pauseRepo) {
        return (nodeId, kind, content, toolName) -> {
            // 先转发原始事件，确保前端实时收到
            delegate.emit(nodeId, kind, content, toolName);
            // 仅拦截 clarify_request
            if (!"clarify_request".equals(kind) || pauseRepo == null || content == null) {
                return;
            }
            try {
                JsonNode payload = JsonUtils.MAPPER.readTree(content);
                String clarifyId = payload.path("clarify_id").asText("");
                String question = payload.path("question").asText("");
                String choicesJson = payload.has("choices")
                        ? JsonUtils.MAPPER.writeValueAsString(payload.get("choices")) : "[]";
                if (clarifyId.isEmpty()) {
                    log.warn("[ClarifyHooks] clarify_request 缺少 clarify_id: {}", content);
                    return;
                }
                // 先查一次以减少唯一约束冲突（高速路径）；DB 唯一索引作为最终防线
                Optional<WorkflowPause> existing = pauseRepo.findByClarifyId(clarifyId);
                if (existing.isPresent()) {
                    return;
                }
                WorkflowPause pause = new WorkflowPause();
                pause.setRunId(runId);
                pause.setNodeId(nodeId);
                pause.setClarifyId(clarifyId);
                pause.setQuestion(question);
                pause.setChoices(choicesJson);
                pause.setStatus("UNANSWERED");
                pause.setCreatedAt(LocalDateTime.now());
                try {
                    pauseRepo.save(pause);
                    log.info("[ClarifyHooks] 已持久化 clarify 暂停点 runId={} clarifyId={} nodeId={}",
                            runId, clarifyId, nodeId);
                } catch (DataIntegrityViolationException dup) {
                    // 并发场景下另一线程已写入，按"已存在"处理
                    log.debug("[ClarifyHooks] clarify_id={} 已被并发写入，跳过", clarifyId);
                }
            } catch (Exception e) {
                log.warn("[ClarifyHooks] 写入 WorkflowPause 失败（不影响主流程）: {}", e.getMessage());
            }
        };
    }
}
