package com.flowable.devops.workflow.executor;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flowable.devops.entity.NodeExecutionLog;
import com.flowable.devops.expression.ExpressionEngine;
import com.flowable.devops.repository.NodeExecutionLogRepository;
import com.flowable.devops.workflow.model.NodeExecutionContext;
import com.flowable.devops.workflow.model.NodeExecutionResult;
import com.flowable.devops.workflow.model.NodeInput;
import com.flowable.devops.workflow.node.WorkflowNode;
import com.flowable.devops.workflow.node.registry.NodeTypeRegistry;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FieldExtension;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

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

/**
 * 通用节点执行器
 * 
 * 所有Service Task都使用这个执行器
 * 根据节点类型动态调用对应的实现类
 * 
 * 关键功能：
 * 1. 从Field Extension获取节点类型和配置
 * 2. 使用表达式引擎解析配置中的表达式
 * 3. 调用对应的节点实现类执行
 * 4. 保存节点输出到流程变量
 * 5. 记录执行日志
 */
@Slf4j
@Component("genericNodeExecutor")
public class GenericNodeExecutor implements JavaDelegate {
    
    @Autowired
    private NodeTypeRegistry nodeTypeRegistry;
    
    @Autowired
    private ExpressionEngine expressionEngine;
    
    @Autowired
    private NodeExecutionLogRepository nodeExecutionLogRepository;
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    @Override
    public void execute(DelegateExecution execution) {
        String nodeId = execution.getCurrentActivityId();
        LocalDateTime startTime = LocalDateTime.now();
        
        log.info("开始执行节点: {} (流程实例: {})", nodeId, execution.getProcessInstanceId());
        
        try {
            // 1. 获取节点类型和配置（从Field Extension）
            String nodeType = getFieldValue(execution, "nodeType");
            String nodeConfigJson = getFieldValue(execution, "nodeConfig");
            
            if (nodeType == null) {
                throw new RuntimeException("节点类型未定义: " + nodeId);
            }
            
            log.debug("节点 {} 类型: {}, 配置: {}", nodeId, nodeType, nodeConfigJson);
            
            // 2. 解析节点配置JSON
            Map<String, Object> nodeConfig = new HashMap<>();
            if (nodeConfigJson != null && !nodeConfigJson.trim().isEmpty()) {
                try {
                    nodeConfig = objectMapper.readValue(
                        nodeConfigJson,
                        new TypeReference<Map<String, Object>>() {}
                    );
                } catch (Exception e) {
                    log.warn("解析节点配置JSON失败: {} - {}", nodeId, e.getMessage());
                }
            }
            
            // 3. 解析表达式（处理数据映射）
            Map<String, Object> resolvedConfig = expressionEngine.resolveObject(nodeConfig, execution);
            
            log.debug("节点 {} 解析后配置: {}", nodeId, resolvedConfig);
            
            // 4. 获取节点实现类
            WorkflowNode nodeImpl = nodeTypeRegistry.getNodeInstance(nodeType);
            if (nodeImpl == null) {
                throw new RuntimeException("未找到节点实现: " + nodeType + " (节点: " + nodeId + ")");
            }
            
            // 5. 构建执行上下文
            NodeExecutionContext context = buildContext(execution, nodeId);
            
            // 6. 执行节点
            NodeInput input = new NodeInput(resolvedConfig);
            NodeExecutionResult result = nodeImpl.execute(input, context);
            
            LocalDateTime endTime = LocalDateTime.now();
            
            // 7. 保存节点输出到流程变量
            saveNodeOutput(execution, nodeId, resolvedConfig, result);
            
            // 8. 记录执行日志
            logExecution(execution, nodeId, nodeType, resolvedConfig, result, startTime, endTime);
            
            log.info("节点执行完成: {} - {} (耗时: {}ms)", 
                    nodeId, result.getStatus(), result.getDurationMs());
            
            // 9. 检查执行结果
            if (result.isFailed()) {
                log.error("节点执行失败: {} - {}", nodeId, result.getError());
                throw new RuntimeException("节点执行失败: " + result.getError());
            }
            
        } catch (Exception e) {
            LocalDateTime endTime = LocalDateTime.now();
            
            // 错误处理
            handleError(execution, nodeId, e, startTime, endTime);
            
            log.error("节点执行异常: {} - {}", nodeId, e.getMessage(), e);
            throw new RuntimeException("节点执行失败: " + nodeId, e);
        }
    }
    
    /**
     * 保存节点输出到流程变量（关键）
     * 
     * 结构:
     * {
     *   "nodes": {
     *     "node1": {
     *       "status": "success",
     *       "input": {...},
     *       "output": {...},
     *       "startTime": "...",
     *       "endTime": "..."
     *     }
     *   }
     * }
     */
    @SuppressWarnings("unchecked")
    private void saveNodeOutput(
        DelegateExecution execution,
        String nodeId,
        Map<String, Object> input,
        NodeExecutionResult result
    ) {
        // 获取或创建nodes对象
        Map<String, Object> nodesData = (Map<String, Object>) execution.getVariable("nodes");
        if (nodesData == null) {
            nodesData = new HashMap<>();
        }
        
        // 构建节点数据
        Map<String, Object> nodeData = new HashMap<>();
        nodeData.put("status", result.getStatus().name().toLowerCase());
        nodeData.put("input", input);
        nodeData.put("output", result.getOutput());
        nodeData.put("startTime", result.getStartTime().toString());
        nodeData.put("endTime", result.getEndTime().toString());
        nodeData.put("durationMs", result.getDurationMs());
        
        if (result.getError() != null) {
            nodeData.put("error", result.getError());
            nodeData.put("errorType", result.getErrorType());
        }
        
        // 保存
        nodesData.put(nodeId, nodeData);
        execution.setVariable("nodes", nodesData);
        
        log.debug("节点输出已保存到流程变量: {} -> nodes.{}", nodeId, nodeId);
    }
    
    /**
     * 构建执行上下文
     */
    @SuppressWarnings("unchecked")
    private NodeExecutionContext buildContext(DelegateExecution execution, String nodeId) {
        return NodeExecutionContext.builder()
                .workflowId(execution.getProcessDefinitionId())
                .executionId(execution.getProcessInstanceId())
                .nodeId(nodeId)
                .variables((Map<String, Object>) execution.getVariable("workflow"))
                .nodes((Map<String, Object>) execution.getVariable("nodes"))
                .env(System.getenv())
                .build();
    }
    
    /**
     * 记录执行日志
     */
    private void logExecution(
        DelegateExecution execution,
        String nodeId,
        String nodeType,
        Map<String, Object> input,
        NodeExecutionResult result,
        LocalDateTime startTime,
        LocalDateTime endTime
    ) {
        try {
            NodeExecutionLog log = new NodeExecutionLog();
            log.setExecutionId(execution.getProcessInstanceId());
            log.setNodeId(nodeId);
            log.setNodeName(execution.getCurrentFlowElement().getName());
            log.setNodeType(nodeType);
            
            // 转换输入输出为JsonNode
            log.setInput(objectMapper.valueToTree(input));
            log.setOutput(objectMapper.valueToTree(result.getOutput()));
            
            // 设置状态和时间
            log.setStatus(convertStatus(result.getStatus()));
            log.setStartedAt(startTime);
            log.setEndedAt(endTime);
            log.setDurationMs(result.getDurationMs().intValue());
            
            if (result.getError() != null) {
                log.setErrorMessage(result.getError());
            }
            
            nodeExecutionLogRepository.save(log);
            
        } catch (Exception e) {
            // 日志记录失败不应该影响流程执行
            this.log.warn("保存节点执行日志失败: {} - {}", nodeId, e.getMessage());
        }
    }
    
    /**
     * 转换执行状态
     */
    private NodeExecutionLog.ExecutionStatus convertStatus(NodeExecutionResult.ExecutionStatus status) {
        switch (status) {
            case SUCCESS:
                return NodeExecutionLog.ExecutionStatus.SUCCESS;
            case FAILED:
                return NodeExecutionLog.ExecutionStatus.FAILED;
            case SKIPPED:
                return NodeExecutionLog.ExecutionStatus.SKIPPED;
            default:
                return NodeExecutionLog.ExecutionStatus.FAILED;
        }
    }
    
    /**
     * 错误处理
     */
    @SuppressWarnings("unchecked")
    private void handleError(
        DelegateExecution execution, 
        String nodeId, 
        Exception e,
        LocalDateTime startTime,
        LocalDateTime endTime
    ) {
        Map<String, Object> nodesData = (Map<String, Object>) execution.getVariable("nodes");
        if (nodesData == null) {
            nodesData = new HashMap<>();
        }
        
        Map<String, Object> nodeData = new HashMap<>();
        nodeData.put("status", "failed");
        nodeData.put("error", e.getMessage());
        nodeData.put("errorType", e.getClass().getSimpleName());
        nodeData.put("startTime", startTime.toString());
        nodeData.put("endTime", endTime.toString());
        nodeData.put("durationMs", java.time.Duration.between(startTime, endTime).toMillis());
        
        nodesData.put(nodeId, nodeData);
        execution.setVariable("nodes", nodesData);
        
        // 记录错误日志
        try {
            NodeExecutionLog log = new NodeExecutionLog();
            log.setExecutionId(execution.getProcessInstanceId());
            log.setNodeId(nodeId);
            log.setNodeName(execution.getCurrentFlowElement().getName());
            log.setStatus(NodeExecutionLog.ExecutionStatus.FAILED);
            log.setStartedAt(startTime);
            log.setEndedAt(endTime);
            log.setErrorMessage(e.getMessage());
            
            nodeExecutionLogRepository.save(log);
            
        } catch (Exception logEx) {
            this.log.warn("保存错误日志失败: {}", logEx.getMessage());
        }
    }
    
    /**
     * 获取Field Extension的值
     */
    private String getFieldValue(DelegateExecution execution, String fieldName) {
        try {
            org.flowable.bpmn.model.FlowElement flowElement = execution.getCurrentFlowElement();
            if (flowElement instanceof ServiceTask) {
                ServiceTask serviceTask = (ServiceTask) flowElement;
                List<FieldExtension> fieldExtensions = serviceTask.getFieldExtensions();
                
                return fieldExtensions.stream()
                        .filter(field -> fieldName.equals(field.getFieldName()))
                        .map(FieldExtension::getStringValue)
                        .findFirst()
                        .orElse(null);
            }
        } catch (Exception e) {
            log.warn("获取Field Extension值失败: {} - {}", fieldName, e.getMessage());
        }
        
        return null;
    }
}