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

import com.huigou.bpm.management.application.ProcessDefinitionApplication;
import com.huigou.bpm.management.application.ServiceRegistrationApplication;
import com.huigou.bpm.management.domain.model.*;
import com.huigou.bpm.management.domain.query.ProcessDefinitionQueryRequest;
import com.huigou.bpm.management.mapper.ProcessDefinitionMapper;
import com.huigou.bpm.management.repository.*;
import com.huigou.data.domain.model.CommonDomainConstants;
import com.huigou.data.domain.query.QueryPageRequest;
import com.huigou.data.domain.service.CommonDomainService;
import com.huigou.domain.ValidStatus;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author yonghuan
 * @since 1.1.3
 */
@Service
public class ProcessDefinitionApplicationImpl implements ProcessDefinitionApplication {

    private ProcessDefinitionRepository processDefinitionRepository;
    private ProcessDefinitionMapper processDefinitionMapper;
    private CommonDomainService commonDomainService;
    private ServiceMappingRepository serviceMappingRepository;
    private ServiceRegistrationApplication serviceRegistrationApplication;
    private ServiceParamMappingRepository serviceParamMappingRepository;

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

    @Autowired
    public void setProcessDefinitionMapper(ProcessDefinitionMapper processDefinitionMapper) {
        this.processDefinitionMapper = processDefinitionMapper;
    }

    @Autowired
    public void setCommonDomainService(CommonDomainService commonDomainService) {
        this.commonDomainService = commonDomainService;
    }

    @Autowired
    public void setServiceMappingRepository(ServiceMappingRepository serviceMappingRepository) {
        this.serviceMappingRepository = serviceMappingRepository;
    }

    @Autowired
    public void setServiceRegistrationApplication(ServiceRegistrationApplication serviceRegistrationApplication) {
        this.serviceRegistrationApplication = serviceRegistrationApplication;
    }

    @Autowired
    public void setServiceParamMappingRepository(ServiceParamMappingRepository serviceParamMappingRepository) {
        this.serviceParamMappingRepository = serviceParamMappingRepository;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ProcessDefinition add(ProcessDefinition processDefinition) {
        processDefinition.setStatus(ValidStatus.ENABLED.id);
        switch (processDefinition.getNodeKindId()) {
            case 0:
                return addProcessDefinitionKind(processDefinition);
            case 1:
                return addProcessDefinition(processDefinition);
            default:
                throw new IllegalArgumentException("无效的节点分类");
        }
    }

    /**
     * 添加流程定义分类
     */
    private ProcessDefinition addProcessDefinitionKind(ProcessDefinition definitionKind) {
        return (ProcessDefinition) commonDomainService.saveTreeEntity(definitionKind, processDefinitionRepository, false, false);
    }

    /**
     * 添加流程定义
     */
    private ProcessDefinition addProcessDefinition(ProcessDefinition definition) {
        checkProcessDefinition(definition);
        Assert.isNull(processDefinitionRepository.findByCode(definition.getCode()), "编码不能重复");
        return (ProcessDefinition) commonDomainService.saveTreeEntity(definition, processDefinitionRepository, false, false);
    }

    @Override
    public ProcessDefinition queryById(String id) {
        return processDefinitionRepository.findOne(id);
    }

    @Override
    public ProcessDefinition queryByCode(String code) {
        return processDefinitionRepository.findByCode(code);
    }

    @Override
    public Page<?> sliceQueryProcessDefinition(ProcessDefinitionQueryRequest queryRequest) {
        queryRequest.setNodeKindId(1);
        return processDefinitionMapper.sliceQuery(queryRequest);
    }

    @Override
    public List<?> queryProcessDefinitionKind(String parentId) {
        Assert.notNull(parentId, "父分类id不能为空");
        ProcessDefinitionQueryRequest queryRequest = new ProcessDefinitionQueryRequest();
        queryRequest.setParentId(parentId);
        queryRequest.setNodeKindId(0);
        QueryPageRequest pageModel = new QueryPageRequest();
        pageModel.setSortFieldName("sequence");
        queryRequest.setPageModel(pageModel);
        return processDefinitionMapper.sliceQuery(queryRequest).getContent();
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void deleteProcessDefinitionKind(String id) {
        ProcessDefinition definitionKind = processDefinitionRepository.getOne(id);
        if (definitionKind != null) {
            Assert.isTrue(processDefinitionRepository.countByParentId(id) == 0, "分类下存在子分类或者流程定义,无法删除");
            processDefinitionRepository.delete(definitionKind);
        }
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void deleteProcessDefinition(List<String> ids) {
        List<ProcessDefinition> definitions = processDefinitionRepository.findAll(ids).stream()
                .filter(pd -> pd.getNodeKindId() == 1)
                .collect(Collectors.toList());
        processDefinitionRepository.delete(definitions);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void moveProcessDefinition(List<String> ids, String parentId) {
        this.commonDomainService.moveForTree(ProcessDefinition.class, ids, CommonDomainConstants.PARENT_ID_COLUMN_NAME, parentId);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ProcessDefinition updateProcessDefinitionKind(ProcessDefinition definitionKind) {
        ProcessDefinition old = processDefinitionRepository.getOne(definitionKind.getId());
        Assert.isTrue(old.getNodeKindId() == 0, "节点不是流程定义分类");
        old.setName(definitionKind.getName());
        old.setRemark(definitionKind.getRemark());
        old.setParentId(definitionKind.getParentId());
        return (ProcessDefinition) commonDomainService.saveTreeEntity(old, processDefinitionRepository, false, false);
    }

    private void checkProcessDefinition(ProcessDefinition definition) {
        Assert.hasText(definition.getParentId(), "流程定义分类不能为空");
        Assert.hasText(definition.getCode(), "流程定义编码不能为空");
        Assert.hasText(definition.getChartKindId(), "流程图类型不能为空");
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public ProcessDefinition updateProcessDefinitionById(ProcessDefinition definition) {
        ProcessDefinition old = processDefinitionRepository.getOne(definition.getId());
        Assert.isTrue(old.getNodeKindId() == 1, "节点不是流程定义");
        ProcessDefinition sameCodeDefinition = processDefinitionRepository.findByCode(definition.getCode());
        boolean uniqueCode = sameCodeDefinition == null || sameCodeDefinition.getId().equals(old.getId());
        Assert.isTrue(uniqueCode, "编码不能重复");
        checkProcessDefinition(definition);
        return (ProcessDefinition) commonDomainService.saveTreeEntity(definition, processDefinitionRepository, false, false);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void updateSequence(Map<String, Integer> idAndSequencePair) {
        commonDomainService.updateSequence(ProcessDefinition.class, idAndSequencePair);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void updateStatus(List<String> ids, Integer status) {
        Assert.notNull(status, "状态不能为空");
        boolean blankId = ids.stream().filter(StringUtils::isBlank)
                .findAny().isPresent();
        Assert.isTrue(!blankId, "id不能为空");
        processDefinitionRepository.updateStatusByIds(status, ids);
    }

    @Override
    public ProcessDefinition queryProcessDefinitionKindById(String id) {
        ProcessDefinition definition = processDefinitionRepository.getOne(id);
        if (definition != null && definition.getNodeKindId() == 0) {
            return definition;
        }
        return null;
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void bindService(ServiceMapping mapping, List<ServiceParamMapping> paramMappings) {
        serviceParamMappingRepository.deleteAll(mapping.getProcdefId(), mapping.getActivityCode());
        serviceMappingRepository.deleteByProcdefIdAndActivityCode(mapping.getProcdefId(), mapping.getActivityCode());
        serviceMappingRepository.save(mapping);
        paramMappings.removeIf(pm -> StringUtils.isBlank(pm.getVariableId()));
        paramMappings.stream().forEach(pm -> pm.setActivityCode(mapping.getActivityCode()));
        serviceParamMappingRepository.save(paramMappings);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public void clearService(String procdefId, String activityCode) {
        serviceParamMappingRepository.deleteAll(procdefId, activityCode);
        serviceMappingRepository.deleteByProcdefIdAndActivityCode(procdefId, activityCode);
    }

    @Override
    public ServiceRegistration queryServiceByActivityCode(String procdefId, String activityCode) {
        ServiceMapping mapping = serviceMappingRepository.findByProcdefIdAndActivityCode(procdefId, activityCode);
        if (mapping != null) {
            return serviceRegistrationApplication.findById(mapping.getServiceId());
        }
        return null;
    }
}
