package cn.axzo.workflow.es.service.aggregation;

import cn.axzo.basics.common.BeanMapper;
import cn.axzo.workflow.common.enums.WorkspaceType;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.es.InstanceSearchReqDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.es.ProcessInstanceDocumentVO;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.service.BpmnProcessInstanceForEsService;
import cn.axzo.workflow.es.model.ProcessInstanceDocument;
import cn.axzo.workflow.es.model.ProcessTaskDocument;
import cn.axzo.workflow.es.service.EsProcessInstanceService;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import static cn.axzo.workflow.common.constant.BpmnConstants.ES_FIXED_ROUTING;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_142;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_AGENT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_WORKSPACE_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;

/**
 * 实例纬度的聚合操作
 *
 * @author wangli
 * @since 2024-09-30 11:46
 */
@Component
@AllArgsConstructor
@Slf4j
public class AggregateProcessInstanceService {
    private final BpmnProcessInstanceForEsService bpmnProcessInstanceForEsService;
    private final EsProcessInstanceService esProcessInstanceService;
    private final AggregateProcessTaskService aggregateProcessTaskService;

    /**
     * 同步结束审批的数据至 ES
     */
    public List<ProcessTaskDocument> syncProcessInstance(HistoricProcessInstance hpi, Function<List<BpmnTaskDelegateAssigner>, Map<Long, BpmnTaskDelegateAssigner>> function) {
        if (Objects.isNull(hpi)) {
            return Collections.emptyList();
        }
        ProcessInstanceDocument processInstanceDocument = new ProcessInstanceDocument();
        processInstanceDocument.setId(hpi.getId());
        processInstanceDocument.setInstanceId(hpi.getId());
        processInstanceDocument.setProcessInstanceName(hpi.getName());
        processInstanceDocument.setBusinessKey(hpi.getBusinessKey());
        processInstanceDocument.setProcessDefinitionId(hpi.getProcessDefinitionId());
        processInstanceDocument.setInstanceStartTime(hpi.getStartTime());
        processInstanceDocument.setInstanceEndTime(hpi.getEndTime());
        processInstanceDocument.setDurationInMillis(hpi.getDurationInMillis());
        processInstanceDocument.setInstanceTenantId(hpi.getTenantId());
        processInstanceDocument.setBusinessStatus(hpi.getBusinessStatus());

        Map<String, Object> variables = hpi.getProcessVariables();
        if (CollectionUtils.isEmpty(variables)) {
            variables.putAll(bpmnProcessInstanceForEsService.queryInstanceVariables(hpi.getId(),
                    Lists.newArrayList(WORKFLOW_ENGINE_VERSION, INTERNAL_PROCESS_WORKSPACE_TYPE, INTERNAL_PROCESS_AGENT, INTERNAL_INITIATOR, OLD_INTERNAL_INITIATOR)));
        }

        BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(variables.getOrDefault(INTERNAL_INITIATOR, variables.getOrDefault(OLD_INTERNAL_INITIATOR, null)));
        if (Objects.nonNull(initiator)) {
            BpmnTaskDelegateAssigner fullAssigner = null;
            if (Objects.nonNull(function) && !StringUtils.hasText(initiator.getAssignerName()) && NumberUtil.isNumber(initiator.getPersonId())) {
                fullAssigner = function.apply(Lists.newArrayList(initiator)).getOrDefault(initiator.parsePersonId(), null);
            }
            log.info("insert es check fullAssigner: {}", Objects.nonNull(fullAssigner));
            if (Objects.nonNull(fullAssigner)) {
                fullAssigner.setAssignerName(StringUtils.hasText(initiator.getAssignerName()) ? initiator.getAssignerName() : "未知");
                processInstanceDocument.setInitiatorName(fullAssigner.getAssignerName());
                processInstanceDocument.setInitiatorJsonStr(JSON.toJSONString(fullAssigner));
            } else {
                processInstanceDocument.setInitiatorName(StringUtils.hasText(initiator.getAssignerName()) ? initiator.getAssignerName() : "未知");
                processInstanceDocument.setInitiatorJsonStr(JSON.toJSONString(initiator));
            }

        }
        processInstanceDocument.setWorkflowEngineVersion(String.valueOf(variables.getOrDefault(WORKFLOW_ENGINE_VERSION, FLOW_SERVER_VERSION_121)));
        processInstanceDocument.setProcessCategoryType(WorkspaceType.getType((Integer) variables.getOrDefault(INTERNAL_PROCESS_WORKSPACE_TYPE, WorkspaceType.UN_KNOW.getCode())).getDesc());
        processInstanceDocument.setAgent((Boolean) variables.getOrDefault(INTERNAL_PROCESS_AGENT, false));

        BpmnModel bpmnModel = bpmnProcessInstanceForEsService.queryBpmnModel(hpi.getProcessDefinitionId());
        BpmnMetaParserHelper.getProcessApproveConf(bpmnModel.getMainProcess()).ifPresent(cfg -> {
            processInstanceDocument.setSupportBatch(cfg.getSupportBatchOperation());
            processInstanceDocument.setUserAgreeSignature(cfg.getUserAgreeSignature());
        });

        // 实例纬度数据同步 ES
        esProcessInstanceService.insert(ES_FIXED_ROUTING, processInstanceDocument);

        String instanceVersion = String.valueOf(variables.getOrDefault(WORKFLOW_ENGINE_VERSION, FLOW_SERVER_VERSION_121))
                .replaceAll("-SNAPSHOT", "")
                .replaceAll("-RELEASE", "");
        DefaultArtifactVersion version = new DefaultArtifactVersion(instanceVersion);
        DefaultArtifactVersion supportVersion = new DefaultArtifactVersion(FLOW_SERVER_VERSION_142);
        List<ProcessTaskDocument> toEsProcessTaskDocuments = new ArrayList<>();
        if (version.compareTo(supportVersion) >= 0) {
            log.info("通过新日志表进行任务纬度的数据同步");
            // 用新日志表数据任务同步
            toEsProcessTaskDocuments.addAll(aggregateProcessTaskService.syncProcessTaskForNew(hpi, instanceVersion));
        } else {
            log.info("通过引擎的任务表进行任务纬度的数据同步");
            // 用引擎的任务表处理任务同步
            toEsProcessTaskDocuments.addAll(aggregateProcessTaskService.syncProcessTaskForOld(hpi, instanceVersion, bpmnModel, function));
        }

        //更新实例上的最后操作时间
        if (Objects.equals(PROCESSING.getStatus(), hpi.getBusinessStatus())) {
            toEsProcessTaskDocuments.stream()
                    .filter(i -> StringUtils.hasText(i.getTaskStatus()) && !Objects.equals(i.getTaskStatus(), PROCESSING.getStatus()))
                    .filter(i -> Objects.nonNull(i.getTaskEndTime()))
                    .max(Comparator.comparing(ProcessTaskDocument::getTaskEndTime))
                    .ifPresent(processTaskDocument -> {
                        processInstanceDocument.setLastOperationTime(processTaskDocument.getTaskEndTime());
                    });
        } else {
            processInstanceDocument.setLastOperationTime(null);
        }

        // 更新实例维度的 LastOperationTime 字段为 null
        esProcessInstanceService.update(ES_FIXED_ROUTING, processInstanceDocument);
        return toEsProcessTaskDocuments;
    }

    /**
     * 从 ES 中搜索数据, 并转换响应模型
     *
     * @param dto
     * @return
     */
    public BpmPageResult<ProcessInstanceDocumentVO> search(InstanceSearchReqDTO dto) {
        BpmPageResult<ProcessInstanceDocument> searchResult = esProcessInstanceService.search(dto);
        List<ProcessInstanceDocumentVO> vos = BeanMapper.copyList(searchResult.getList(), ProcessInstanceDocumentVO.class);
        return new BpmPageResult<>(vos, searchResult.getTotal());
    }

    /**
     * 删除指定流程实例的父子文档
     *
     * @param processInstanceId
     * @return
     */
    public Boolean deleteDocumentParentAndChild(String processInstanceId) {
        try {
            esProcessInstanceService.delete(processInstanceId);
            aggregateProcessTaskService.deleteByParentId(processInstanceId);
        } catch (Exception e) {
            log.warn("删除文档失败：{}", e.getMessage(), e);
            return false;
        }
        return true;
    }
}
