package com.huigou.bpm.management.application.impl;

import com.huigou.bpm.management.application.ProcessDeploymentApplication;
import com.huigou.bpm.management.domain.model.ProcessDefinition;
import com.huigou.bpm.management.domain.model.ProcessDeployment;
import com.huigou.bpm.management.domain.query.ProcessDeploymentQueryRequest;
import com.huigou.bpm.management.mapper.ProcessDeploymentMapper;
import com.huigou.bpm.management.repository.ProcessDefinitionRepository;
import com.huigou.bpm.management.repository.ProcessDeploymentRepository;
import com.huigou.bpm.management.translate.ActivitiXmlTranslator;
import com.huigou.domain.ValidStatus;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.List;

/**
 * @author yonghuan
 */
@Service
public class ProcessDeploymentApplicationImpl implements ProcessDeploymentApplication {

    private ProcessDeploymentRepository processDeploymentRepository;
    private ProcessDefinitionRepository processDefinitionRepository;
    private ProcessDeploymentMapper processDeploymentMapper;
    private RepositoryService repositoryService;
    private ActivitiXmlTranslator activitiXmlTranslator;

    @Autowired
    public void setProcessDeploymentRepository(ProcessDeploymentRepository processDeploymentRepository) {
        this.processDeploymentRepository = processDeploymentRepository;
    }

    @Autowired
    public void setProcessDefinitionRepository(ProcessDefinitionRepository processDefinitionRepository) {
        this.processDefinitionRepository = processDefinitionRepository;
    }

    @Autowired
    public void setProcessDeploymentMapper(ProcessDeploymentMapper processDeploymentMapper) {
        this.processDeploymentMapper = processDeploymentMapper;
    }

    @Autowired
    public void setRepositoryService(RepositoryService repositoryService) {
        this.repositoryService = repositoryService;
    }

    @Autowired
    public void setActivitiXmlTranslator(ActivitiXmlTranslator activitiXmlTranslator) {
        this.activitiXmlTranslator = activitiXmlTranslator;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ProcessDeployment deployProcessDefinition(String processDefinitionId) {
        ProcessDefinition definition = processDefinitionRepository.getOne(processDefinitionId);
        Assert.notNull(definition, "流程定义不存在");
        Assert.isTrue(ValidStatus.ENABLED.id.equals(definition.getStatus()), "流程定义未启用");
        ProcessDeployment deployment = new ProcessDeployment();
        deployment.setProcdefId(processDefinitionId);
        deployment.setRemark(definition.getRemark());
        deployment.setCode(definition.getCode());
        deployment.setName(definition.getName());
        deployment.setStatus(ValidStatus.ENABLED.id);
        deployToActiviti(definition, deployment);
        // id格式为:  流程定义code:发布版本号:流程定义id
        String id = String.join(":", definition.getCode(), deployment.getProcVersion().toString(), definition.getId());
        Assert.isTrue(!processDeploymentRepository.exists(id), "版本号不唯一");
        deployment.setId(id);
        disableHistoricalDeployment(definition);
        deployment.setXml(definition.getXml());
        deployment.setImageXml(definition.getImageXml());
        definition.setImageWidth(definition.getImageWidth());
        definition.setImageHeight(definition.getImageHeight());
        deployment = processDeploymentRepository.save(deployment);
        return deployment;
    }

    /**
     * 停用历史版本
     */
    private void disableHistoricalDeployment(ProcessDefinition definition) {
        Specification<ProcessDeployment> spec = (root, query, cb) -> cb.and(cb.equal(root.get("procdefId"), definition.getId()), cb.equal(root.get("status"), ValidStatus.ENABLED.id));
        List<ProcessDeployment> historicalDeployments = processDeploymentRepository.findAll(spec);
        historicalDeployments.forEach(deployment -> deployment.setStatus(ValidStatus.DISABLED.id));
        processDeploymentRepository.save(historicalDeployments);
    }

    /**
     * 部署到 activiti
     */
    private void deployToActiviti(ProcessDefinition definition, ProcessDeployment deployment) {
        Deployment activitiDeployment = repositoryService.createDeployment().addInputStream(definition.getCode() + ".bpmn", activitiXmlTranslator.translate(definition))
                .deploy();
        org.activiti.engine.repository.ProcessDefinition activitiProcessDefinition =
                repositoryService.createProcessDefinitionQuery()
                        .processDefinitionKey(definition.getCode())
                        .deploymentId(activitiDeployment.getId())
                        .singleResult();
        // 取activiti流程定义id
        deployment.setActProcdefId(activitiProcessDefinition.getId());
        // 流程版本需要跟activiti的流程定义版本保持一致
        deployment.setProcVersion(activitiProcessDefinition.getVersion());
    }

    @Override
    public Page<?> sliceQueryByProcessDefinitionId(ProcessDeploymentQueryRequest queryRequest) {
        return processDeploymentMapper.sliceQuery(queryRequest);
    }

    @Override
    public ProcessDeployment findById(String id) {
        return processDeploymentRepository.getOne(id);
    }
}
