package com.qqchen.deploy.backend.workflow.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.converter.WorkflowInstanceConverter;
import com.qqchen.deploy.backend.workflow.converter.WorkflowNodeInstanceConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowHistoricalInstancesDTO;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
import com.qqchen.deploy.backend.workflow.dto.WorkflowTemplateWithInstancesDTO;
import com.qqchen.deploy.backend.workflow.dto.query.WorkflowInstanceQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowNodeInstance;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums;
import com.qqchen.deploy.backend.workflow.dto.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.dto.query.WorkflowHistoricalInstancesQuery;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowNodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.util.FlowableUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

@Slf4j
@Service
public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, WorkflowInstanceQuery, Long> implements IWorkflowInstanceService {

    @Resource
    private IWorkflowDefinitionRepository workflowDefinitionRepository;

    @Resource
    private IWorkflowInstanceRepository workflowInstanceRepository;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private HistoryService historyService;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private WorkflowInstanceConverter workflowInstanceConverter;

    @Resource
    private IWorkflowNodeInstanceRepository workflowNodeInstanceRepository;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private WorkflowNodeInstanceConverter workflowNodeInstanceConverter;

    @Override
    public WorkflowInstanceDTO createWorkflowInstance(Long workflowDefinitionId, String businessKey, ProcessInstance processInstance) {
        // 1. 查询流程定义，获取 graph 快照
        WorkflowDefinition definition = workflowDefinitionRepository.findById(workflowDefinitionId)
            .orElseThrow(() -> new RuntimeException("Workflow definition not found: " + workflowDefinitionId));
        
        // 2. 创建实例并保存 graph 快照
        WorkflowInstance workflowInstance = new WorkflowInstance();
        workflowInstance.setProcessInstanceId(processInstance.getId());
        workflowInstance.setProcessDefinitionId(processInstance.getProcessDefinitionId());
        workflowInstance.setWorkflowDefinitionId(workflowDefinitionId);
        workflowInstance.setBusinessKey(businessKey);
        workflowInstance.setGraphSnapshot(definition.getGraph());  // ✅ 保存画布数据快照
        workflowInstance.setStatus(WorkflowInstanceStatusEnums.NOT_STARTED);
        workflowInstance.setStartTime(LocalDateTime.now());
        workflowInstanceRepository.save(workflowInstance);
        
        log.info("创建工作流实例: instanceId={}, definitionId={}, graph已保存", 
            workflowInstance.getId(), workflowDefinitionId);
        
        // 3. 返回创建结果
        return workflowInstanceConverter.toDto(workflowInstance);
    }

    @Override
    public WorkflowInstance updateInstanceStatus(String processInstanceId, WorkflowInstanceStatusEnums status, LocalDateTime endTime) {
        WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId)
            .orElseThrow(() -> new RuntimeException("Workflow instance not found: " + processInstanceId));
        instance.setStatus(status);
        instance.setEndTime(endTime);
        return workflowInstanceRepository.save(instance);
    }

    @Override
    public WorkflowInstanceDTO getInstanceDetails(String processInstanceId) {
        WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId)
            .orElseThrow(() -> new RuntimeException("Workflow instance not found: " + processInstanceId));

//        WorkflowInstanceDTO dto = new WorkflowInstanceDTO();
////        dto.setId(instance.getProcessInstanceId());
//        dto.setProcessDefinitionId(String.valueOf(instance.getProcessDefinitionId()));
//        dto.setBusinessKey(instance.getBusinessKey());
//        dto.setStatus(instance.getStatus());
//
//        // Convert LocalDateTime to Date
//        dto.setStartTime(java.sql.Timestamp.valueOf(instance.getStartTime()));
//        if (instance.getEndTime() != null) {
//            dto.setEndTime(java.sql.Timestamp.valueOf(instance.getEndTime()));
//        }
//
//        // Calculate duration if both start and end time exist
//        if (instance.getStartTime() != null && instance.getEndTime() != null) {
//            dto.setDurationInMillis(java.time.Duration.between(instance.getStartTime(), instance.getEndTime()).toMillis());
//        }
//
//        // Get variables
//        try {
//            @SuppressWarnings("unchecked")
//            Map<String, Object> variables = objectMapper.readValue(instance.getVariables(), Map.class);
//            dto.setVariables(variables);
//        } catch (JsonProcessingException e) {
//            log.error("Failed to deserialize variables", e);
//            dto.setVariables(new HashMap<>());
//        }
//
//        // Get activity instances
//        List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
//                .processInstanceId(processInstanceId)
//                .orderByHistoricActivityInstanceStartTime().asc()
//                .list();
//
//        dto.setActivities(activities.stream().map(this::convertToActivityInstance).collect(Collectors.toList()));

//        return dto;
        return null;
    }

    @Override
    public List<WorkflowInstanceDTO> findByBusinessKey(String businessKey) {
        return workflowInstanceRepository.findByBusinessKey(businessKey).stream()
            .map(instance -> getInstanceDetails(instance.getProcessInstanceId()))
            .collect(toList());
    }

    @Override
    @Transactional
    public void completeInstance(String processInstanceId, Map<String, Object> variables) {
        // Update Flowable variables
        runtimeService.setVariables(processInstanceId, variables);

        // Update our instance
        WorkflowInstance instance = workflowInstanceRepository.findByProcessInstanceId(processInstanceId)
            .orElseThrow(() -> new RuntimeException("Workflow instance not found: " + processInstanceId));

        try {
            instance.setVariables(objectMapper.writeValueAsString(variables));
        } catch (JsonProcessingException e) {
            log.error("Failed to serialize variables", e);
        }

        instance.setStatus(WorkflowInstanceStatusEnums.COMPLETED);
        instance.setEndTime(LocalDateTime.now());
        workflowInstanceRepository.save(instance);
    }

    @Override
    @Transactional
    public WorkflowInstanceDTO startWorkflow(WorkflowInstanceStartRequest request) {
        try {
            WorkflowDefinition workflowDefinition = workflowDefinitionRepository.findByKey(request.getProcessKey()).orElseThrow(() -> new RuntimeException("Workflow definition process key not found: " + request.getProcessKey()));
            ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .processDefinitionKey(request.getProcessKey())
                .variables(request.getVariables())
                .businessKey(request.getBusinessKey())
                .variables(request.getVariables())
                .startAsync();  // 异步启动，会自动执行 shell 任务
//                .start();
            return createWorkflowInstance(workflowDefinition.getId(), request.getBusinessKey(), processInstance);
        } catch (Exception e) {
            log.error("Failed to create workflow: {}", request.getProcessKey(), e);
            throw new RuntimeException("Failed to create workflow", e);
        }
    }

    @Override
    public Page<WorkflowTemplateWithInstancesDTO> findTemplatesWithRecentInstances(WorkflowDefinitionQuery query) {
        Pageable pageable = PageRequest.of(query.getPageNum(), query.getPageSize());
        Page<WorkflowDefinition> definitionPage = workflowDefinitionRepository.findByStatus(WorkflowDefinitionStatusEnums.PUBLISHED, pageable);
        List<WorkflowTemplateWithInstancesDTO> result = definitionPage.getContent().stream().map(definition -> {
            List<WorkflowInstance> workflowInstances = workflowInstanceRepository.findTop1ByWorkflowDefinitionIdOrderByCreateTimeDesc(definition.getId());
            WorkflowTemplateWithInstancesDTO workflowTemplateWithInstancesDTO = new WorkflowTemplateWithInstancesDTO();
            Optional<WorkflowInstance> optional = workflowInstances.stream().findFirst();
            if (optional.isPresent()) {
                workflowTemplateWithInstancesDTO.setLastExecutionStatus(optional.get().getStatus());
                workflowTemplateWithInstancesDTO.setLastExecutionTime(optional.get().getCreateTime());
            }
            workflowTemplateWithInstancesDTO.setId(definition.getId());
            workflowTemplateWithInstancesDTO.setName(definition.getName());
            workflowTemplateWithInstancesDTO.setBusinessKey(definition.getKey());
            return workflowTemplateWithInstancesDTO;
        }).collect(toList());
        return new PageImpl<>(result, definitionPage.getPageable(), definitionPage.getTotalElements());
    }

    @Override
    public Page<WorkflowHistoricalInstancesDTO> historicalInstances(WorkflowHistoricalInstancesQuery query) {
        Pageable pageable = PageRequest.of(query.getPageNum(), query.getPageSize());
        Page<WorkflowInstance> workflowInstances = workflowInstanceRepository.findByWorkflowDefinitionIdOrderByCreateTimeDesc(query.getWorkflowDefinitionId(), pageable);
        List<WorkflowHistoricalInstancesDTO> result = new ArrayList<>();
        workflowInstances.getContent().stream().map(workflowInstance -> {
            WorkflowHistoricalInstancesDTO workflowHistoricalInstancesDTO = workflowInstanceConverter.toWorkflowHistoricalInstancesDTO(workflowInstance);
            
            // ✅ 设置画布数据快照（从实例中读取，无需查询定义）
            workflowHistoricalInstancesDTO.setGraph(workflowInstance.getGraphSnapshot());
            
            BpmnModel bpmnModel = repositoryService.getBpmnModel(workflowInstance.getProcessDefinitionId());
            Process process = bpmnModel.getMainProcess();
            //排序
            List<FlowElement> flowElements = FlowableUtils.sortFlowElements(process);
            List<WorkflowNodeInstance> nodeInstances = workflowNodeInstanceRepository.findByWorkflowInstanceId(workflowInstance.getId());
            Map<String, WorkflowNodeInstance> nodeWorkflowNodeInstance = nodeInstances.stream().collect(Collectors.toMap(WorkflowNodeInstance::getNodeId, nodeInstance -> nodeInstance));
            List<WorkflowNodeInstance> nodeResult = new ArrayList<>();
            flowElements.forEach(v -> {
                WorkflowNodeInstance workflowNodeInstance = nodeWorkflowNodeInstance.get(v.getId());
                if (Objects.isNull(workflowNodeInstance)) {
                    WorkflowNodeInstance empty = new WorkflowNodeInstance();
                    empty.setStatus(WorkflowNodeInstanceStatusEnums.NOT_STARTED);
                    empty.setNodeId(v.getId());
                    empty.setNodeName(v.getName());
                    nodeResult.add(empty);
                } else {
                    nodeResult.add(workflowNodeInstance);
                }
            });
            workflowHistoricalInstancesDTO.setStages(workflowNodeInstanceConverter.toDtoList(nodeResult));
            result.add(workflowHistoricalInstancesDTO);
            return result;
        }).collect(toList());
        return new PageImpl<>(result, workflowInstances.getPageable(), workflowInstances.getTotalElements());
    }

//    private WorkflowInstanceDTO.ActivityInstance convertToActivityInstance(HistoricActivityInstance hai) {
//        WorkflowInstanceDTO.ActivityInstance ai = new WorkflowInstanceDTO.ActivityInstance();
//        ai.setId(hai.getId());
//        ai.setActivityId(hai.getActivityId());
//        ai.setActivityName(hai.getActivityName());
//        ai.setActivityType(hai.getActivityType());
//        ai.setStartTime(hai.getStartTime());
//        ai.setEndTime(hai.getEndTime());
//        ai.setDurationInMillis(hai.getDurationInMillis());
//
//        // Get shell task execution details from variables if available
//        if ("serviceTask".equals(hai.getActivityType())) {
//            Map<String, Object> variables = runtimeService.getVariables(hai.getExecutionId());
//            ai.setShellOutput((String) variables.get("shellOutput"));
//            ai.setShellError((String) variables.get("shellError"));
//            ai.setShellExitCode((Integer) variables.get("shellExitCode"));
//        }
//
//        return ai;
//    }
}
