package cn.axzo.workflow.core.service.impl;

import cn.axzo.workflow.common.model.dto.es.HistoricProcessInstanceSearchForEsDTO;
import cn.axzo.workflow.core.engine.cmd.CustomGetHistoricVariablesCmd;
import cn.axzo.workflow.core.service.BpmnProcessInstanceForEsService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 专用与对接 ES 的流程实例相关操作
 *
 * @author wangli
 * @since 2024-09-27 14:23
 */
@Service
@Slf4j
public class BpmnProcessInstanceForEsServiceImpl implements BpmnProcessInstanceForEsService {
    @Resource
    @Lazy
    private HistoryService historyService;
    @Resource
    @Lazy
    private RepositoryService repositoryService;
    @Resource
    @Lazy
    private SpringProcessEngineConfiguration processEngineConfiguration;

    @Override
    public Long queryHistoricProcessInstanceTotalCount(HistoricProcessInstanceSearchForEsDTO search) {
        if (Objects.isNull(search)) {
            return 0L;
        }
        return buildHistoricProcessInstanceQuery(search).count();
    }

    @Override
    public List<HistoricProcessInstance> queryHistoricProcessInstance(HistoricProcessInstanceSearchForEsDTO search, IPage page) {
        if (Objects.isNull(search)) {
            return Collections.emptyList();
        }
        HistoricProcessInstanceQuery query = buildHistoricProcessInstanceQuery(search)
                .orderByProcessInstanceId().asc();
        if (Objects.nonNull(page)) {
            int firstResult = Math.toIntExact((page.getCurrent() - 1) * page.getSize());
            int maxResult = Math.toIntExact(page.getSize());
            return query.listPage(firstResult, maxResult);
        }
        return query.list();
    }

    @Override
    public BpmnModel queryBpmnModel(String processDefinitionId) {
        return repositoryService.getBpmnModel(processDefinitionId);
    }

    @Override
    public Map<String, Object> queryInstanceVariables(String processInstanceId, List<String> variableNames) {
        CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
        List<HistoricVariableInstance> variables = commandExecutor.execute(new CustomGetHistoricVariablesCmd(processInstanceId, variableNames));
        return variables.stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue, (s, t) -> s));
    }

    private HistoricProcessInstanceQuery buildHistoricProcessInstanceQuery(HistoricProcessInstanceSearchForEsDTO search) {
        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
        if (StringUtils.hasText(search.getProcessInstanceId())) {
            historicProcessInstanceQuery.processInstanceId(search.getProcessInstanceId());
        }
        if (StringUtils.hasText(search.getBusinessKey())) {
            historicProcessInstanceQuery.processInstanceBusinessKey(search.getBusinessKey());
        }
        if (StringUtils.hasText(search.getProcessDefinitionKey())) {
            historicProcessInstanceQuery.processDefinitionKey(search.getProcessDefinitionKey());
        }
        if (StringUtils.hasText(search.getProcessInstanceName())) {
            historicProcessInstanceQuery.processInstanceName(search.getProcessInstanceName());
        }
        if (StringUtils.hasText(search.getBusinessStatus())) {
            historicProcessInstanceQuery.processInstanceBusinessStatus(search.getBusinessStatus());
        }
        if (StringUtils.hasText(search.getTenantId())) {
            historicProcessInstanceQuery.processInstanceTenantId(search.getTenantId());
        }
        // 只有传值后, 才加入查询条件
        if (Objects.equals(Boolean.TRUE, search.getFinished())) {
            historicProcessInstanceQuery.finished();
        } else if (Objects.equals(Boolean.FALSE, search.getFinished())) {
            historicProcessInstanceQuery.unfinished();
        }
        if (Objects.nonNull(search.getStartTime())) {
            // 引擎默认仅支持两种
            switch (search.getStartTimeDirection()) {
                case BEFORE:
                    historicProcessInstanceQuery.startedBefore(search.getStartTime());
                    break;
                default:
                    historicProcessInstanceQuery.startedAfter(search.getStartTime());
                    break;
            }
        }
        if (Objects.nonNull(search.getEndTime())) {
            // 引擎默认仅支持两种
            switch (search.getEndTimeDirection()) {
                case AFTER:
                    historicProcessInstanceQuery.finishedAfter(search.getEndTime());
                    break;
                default:
                    historicProcessInstanceQuery.finishedBefore(search.getEndTime());
                    break;
            }
        }
        if (search.getHasVariables()) {
            historicProcessInstanceQuery.includeProcessVariables();
        }
        return historicProcessInstanceQuery;
    }

    public Long queryHistoricProcessInstanceByUnfinishedAndAlterEndTimeTotalCount(Date endTime) {
        return historyService.createHistoricProcessInstanceQuery()
                .or()
                .unfinished()
                .finishedAfter(endTime)
                .endOr()
                .count();
    }

    public List<HistoricProcessInstance> queryHistoricProcessInstanceByUnfinishedAndAlterEndTime(Date endTime, IPage page) {
        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery()
                .or()
                .unfinished()
                .finishedAfter(endTime)
                .endOr();
        if (Objects.nonNull(page)) {
            int firstResult = Math.toIntExact((page.getCurrent() - 1) * page.getSize());
            int maxResult = Math.toIntExact(page.getSize());
            return query.listPage(firstResult, maxResult);
        }
        return query.list();
    }
}
