package com.qqchen.deploy.backend.workflow.model;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * 节点执行上下文（强类型）
 * 统一封装节点的配置、输入映射和输出结果
 *
 * <p>数据结构：
 * <pre>
 * {
 *   "configs": { ... },      // 节点配置（来自 BPMN）
 *   "inputMapping": { ... }, // 输入映射（运行时数据，强类型）
 *   "outputs": { ... },      // 输出结果（完整嵌套，强类型，用于问题调查）
 *   // ... outputs 的字段也会平铺到顶层（供 BPMN 表达式访问）
 * }
 * </pre>
 *
 * @param <I> InputMapping 类型
 * @param <O> Outputs 类型
 * @author qqchen
 */
@Data
public class NodeContext<I, O> {

    /**
     * 节点配置（来自 BPMN XML 的 configs 字段）
     */
    private Map<String, Object> configs;

    /**
     * 输入映射（运行时动态数据，强类型）
     */
    private I inputMapping;

    /**
     * 输出结果（节点执行后生成，强类型）
     */
    private O outputs;

    /**
     * 转换为 Map（用于 Flowable 变量存储）
     * 
     * 存储格式：
     * - configs、inputMapping、outputs 保留嵌套结构
     * - outputs 的字段同时平铺到顶层（供 BPMN 表达式访问）
     */
    public Map<String, Object> toMap(ObjectMapper objectMapper) {
        Map<String, Object> map = new HashMap<>();
        
        if (configs != null) {
            map.put("configs", configs);
        }
        
        if (inputMapping != null) {
            Map<String, Object> inputMap = objectMapper.convertValue(inputMapping,
                new TypeReference<Map<String, Object>>() {});
            map.put("inputMapping", inputMap);
        }
        
        if (outputs != null) {
            Map<String, Object> outputsMap = objectMapper.convertValue(outputs,
                new TypeReference<Map<String, Object>>() {});
            
            // 保存完整的嵌套 outputs（用于问题调查）
            map.put("outputs", outputsMap);
            
            // 同时将 outputs 字段平铺到顶层（供 BPMN 表达式访问）
            map.putAll(outputsMap);
        }
        
        return map;
    }

    /**
     * 从 Map 恢复 NodeContext
     */
    @SuppressWarnings("unchecked")
    public static <I, O> NodeContext<I, O> fromMap(
        Map<String, Object> map,
        Class<I> inputClass,
        Class<O> outputClass,
        ObjectMapper objectMapper
    ) {
        NodeContext<I, O> context = new NodeContext<>();
        
        if (map.containsKey("configs")) {
            context.setConfigs((Map<String, Object>) map.get("configs"));
        }
        
        if (map.containsKey("inputMapping") && inputClass != null) {
            Object inputObj = map.get("inputMapping");
            I input = objectMapper.convertValue(inputObj, inputClass);
            context.setInputMapping(input);
        }
        
        if (map.containsKey("outputs") && outputClass != null) {
            Object outputsObj = map.get("outputs");
            O output = objectMapper.convertValue(outputsObj, outputClass);
            context.setOutputs(output);
        }
        
        return context;
    }
}

