/**
 * Visit 链辅助工具（DAG / AGENTIC 模式统一）。
 *
 * <p>WorkflowEditor / RunHistory 两处共用：实时运行 + 历史回放均渲染同一组 Visit 卡片，
 * 字段解析逻辑（outputJson / variablesJson / decisionJson / logsJson 反序列化、未知节点类型兜底）
 * 抽到此工具，避免两份实现漂移。</p>
 *
 * <p>对应设计文档 {@code docs/plans/agentic-workflow-plan.md} §3 数据结构。</p>
 */
import { unwrapNodeOutput } from '../composables/useWorkflowRunner'
import {
  extractStreamingThinking,
  extractToolHistory,
  extractLatestTodos,
  extractNonThinkingLogs
} from './runResultReplay'

/**
 * 未知节点类型的兜底配置（灰色），避免 nodeTypeInfo 查不到时模板渲染崩。
 * 各页面在 nodeTypeInfo 上层做 lookup 时使用。
 */
export const UNKNOWN_NODE_TYPE_INFO = Object.freeze({
  color: '#6b7280',
  typeLabel: '未知',
  icon: null
})

/**
 * Visit.outputJson 是节点输出 envelope 序列化后的 JSON 字符串；
 * unwrapNodeOutput 期望对象，故先 parse 再 unwrap。
 *
 * @param {string|object|null} jsonStr
 * @returns {object|null} unwrap 后的 data 字段；解析失败或为空返回 null
 */
export function parseVisitOutput(jsonStr) {
  if (!jsonStr) return null
  try {
    const obj = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr
    return unwrapNodeOutput(obj)
  } catch { return null }
}

/**
 * Visit.variablesJson 是工作区变量快照（含初始输入 + 此前所有访问节点输出，
 * 按 nodeId__varName 形式存放）。
 *
 * @param {string|object|null} jsonStr
 * @returns {Record<string, any>} 解析失败或非对象返回空对象
 */
export function parseVisitVariables(jsonStr) {
  if (!jsonStr) return {}
  try {
    const obj = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr
    return obj && typeof obj === 'object' ? obj : {}
  } catch { return {} }
}

/**
 * Visit.decisionJson 是跳转元数据。当前仅记录"非画布跳转"标志：
 *   - null / 空 → 按画布连线跳转（与 DAG 一致，前端不展示"来自 X"徽章）
 *   - {"offCanvas":true} → AGENTIC 自由跳转（前端展示"来自 X"徽章）
 *
 * <p>兼容字段：未来若同时记录 Planner 决策（nextNodeId / reason），扩展本函数即可。</p>
 *
 * @param {string|object|null} jsonStr
 * @returns {{offCanvas: boolean, nextNodeId: string|null, reason: string}|null}
 */
export function parseVisitDecision(jsonStr) {
  if (!jsonStr) return null
  try {
    const obj = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr
    if (!obj || typeof obj !== 'object') return null
    return {
      offCanvas: obj.offCanvas === true,
      nextNodeId: obj.nextNodeId || obj.next || null,
      reason: obj.reason || ''
    }
  } catch { return null }
}

/**
 * 把后端 WorkflowRunVisit 还原为前端 Visit 卡片格式（含 streamingThinking / toolHistory / todos / logs）。
 *
 * <p>用于 RunHistory 页面回放，与 WorkflowEditor 实时态 Visit 字段一致：
 *   - 元数据：visitUuid / nodeId / nodeType / nodeLabel / iterSeq / fromVisitUuid / fromNodeId / sortOrder
 *   - 结果：status / output / logs / contextSnapshot / variablesJson / outputJson / errorMsg
 *   - 流式回放：从 logsJson 还原 streamingThinking / toolHistory / todos
 *   - 已完成态：currentStatus / currentTool 留空</p>
 *
 * @param {Object} visit 后端 WorkflowRunVisit（含 outputJson / variablesJson / logsJson / decisionJson）
 * @param {{color:string,typeLabel:string,icon?:any}} info 节点类型信息（由调用方查 nodeTypeInfo 后传入）
 * @returns {Object} 前端 Visit 卡片格式
 */
export function buildReplayVisit(visit, info) {
  const v = visit || {}
  const logsJson = v.logsJson || ''
  const decision = parseVisitDecision(v.decisionJson)
  return {
    visitUuid: v.visitUuid || null,
    nodeId: v.nodeId,
    nodeType: v.nodeType,
    nodeLabel: v.nodeLabel || v.nodeId || '节点',
    iterSeq: v.iterSeq || 1,
    fromVisitUuid: v.fromVisitUuid || null,
    fromNodeId: v.fromNodeId || null,
    // offCanvas：仅当 decisionJson 显式标记为 true 才为 true（按画布走或起始节点均为 false）
    offCanvas: decision?.offCanvas === true,
    sortOrder: v.sortOrder ?? 0,
    status: (v.status || '').toLowerCase(),
    errorMsg: v.errorMsg || null,
    // output：保留 envelope（envelope 在 useWorkflowRunner.unwrapNodeOutput 中解封）
    output: v.outputJson ? safeParse(v.outputJson) : null,
    variablesJson: v.variablesJson || '{}',
    outputJson: v.outputJson || null,
    contextSnapshot: null,  // Visit 不存 contextSnapshot；上下文展示回退到 variablesJson
    // 流式回放：从 logsJson 还原
    streamingThinking: extractStreamingThinking(logsJson),
    toolHistory: extractToolHistory(logsJson),
    todos: extractLatestTodos(logsJson),
    logs: extractNonThinkingLogs(logsJson),
    // 已完成态无实时
    currentStatus: '',
    currentTool: null,
    // 视觉信息（由调用方注入）
    color: info?.color || '#6b7280',
    typeLabel: info?.typeLabel || '未知'
  }
}

function safeParse(str) {
  try { return JSON.parse(str) } catch { return null }
}
