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

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.utils.JsonUtils;
import lombok.Data;

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

/**
 * 节点执行上下文（强类型）
 * 统一封装节点的配置、输入映射和输出结果
 *
 * <p>数据结构（规范）：
 * <pre>
 * {
 *   "configs": { ... },      // 节点配置（来自 BPMN）
 *   "inputMapping": { ... }, // 输入映射（运行时数据，强类型）
 *   "outputs": { ... }       // 输出结果（强类型，所有输出数据只存储在此）
 * }
 * </pre>
 *
 * <p>BPMN 表达式访问规范：
 * <pre>
 * ${nodeId.outputs.字段名}
 * 示例：${sid_xxx.outputs.approvalResult == 'APPROVED'}
 * </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": { ... }        // 节点输出（所有输出数据只存储在此）
     * }
     * 
     * BPMN 表达式访问：${nodeId.outputs.字段名}
     */
    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 = JsonUtils.toMapFast(inputMapping);
            map.put("inputMapping", inputMap);
        }
        
        if (outputs != null) {
            Map<String, Object> outputsMap = JsonUtils.toMapFast(outputs);
            
            // 只保存嵌套的 outputs（保持数据结构规范）
            map.put("outputs", 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 = JsonUtils.convert(inputObj, inputClass);
            context.setInputMapping(input);
        }
        
        if (map.containsKey("outputs") && outputClass != null) {
            Object outputsObj = map.get("outputs");
            O output = JsonUtils.convert(outputsObj, outputClass);
            context.setOutputs(output);
        }
        
        return context;
    }
}

