package com.dayou.service.workflow;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dayou.common.BaseEntity;
import com.dayou.common.FixedWorkNode;
import com.dayou.dto.InstanceNodeDTO;
import com.dayou.entity.WorkFlow;
import com.dayou.entity.WorkFlowNodeInstance;
import com.dayou.entity.WorkNode;
import com.dayou.enums.MainBusinessEnum;
import com.dayou.enums.ProductionEnum;
import com.dayou.enums.workflow.CallbackEnum;
import com.dayou.enums.workflow.NodeStateEnum;
import com.dayou.enums.workflow.WorkflowNodeEnum;
import com.dayou.mapper.WorkFlowNodeInstanceMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dayou.vo.NightingaleEchartsVO;
import com.dayou.vo.WorkNodeInstanceVO;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

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

import static com.dayou.enums.MainBusinessEnum.ASSET_BUSINESS;
import static com.dayou.enums.MainBusinessEnum.MAJOR_BUSINESS;
import static com.dayou.enums.workflow.CallbackEnum.CHECK_PRODUCTION_TYPE;
import static com.dayou.enums.workflow.WorkflowNodeEnum.*;

/**
 * <p>
 * 工作流节点实例 服务实现类
 * </p>
 *
 * @author wucl
 * @since 2023-10-17
 */
@DS("mbs")
@Service
public class WorkFlowNodeInstanceServiceImpl extends ServiceImpl<WorkFlowNodeInstanceMapper, WorkFlowNodeInstance> implements IWorkFlowNodeInstanceService {

    @Autowired
    private WorkFlowNodeInstanceMapper workFlowNodeInstanceMapper;

    @Autowired
    private IWorkFlowService workFlowService;

    @Autowired
    private IWorkNodeService workNodeService;

    @Override
    @SuppressWarnings("unchecked")
    public Page<WorkFlowNodeInstance> selectPage(Page page,WorkFlowNodeInstance workFlowNodeInstance){
        return this.page(page, new QueryWrapper<WorkFlowNodeInstance>(workFlowNodeInstance));
    }


    @Override
    public WorkFlowNodeInstance detail(Long id){
        return this.getById(id);
    }

    @Override
    public Boolean add(WorkFlowNodeInstance workFlowNodeInstance){
        return  this.save(workFlowNodeInstance);
    }

    @Override
    public Boolean update(WorkFlowNodeInstance workFlowNodeInstance){
        return  this.updateById(workFlowNodeInstance);
    }

    @Override
    public Boolean delete(Long id){
        //逻辑删除
        return this.removeById(id);
    }

    @Override
    public List<WorkNodeInstanceVO> filterWaitingNodeBeforePendingNode(InstanceNodeDTO instance){
        List<WorkNodeInstanceVO> nodesSorted = this.instanceQueue(instance);
        List<WorkNodeInstanceVO> pendingNodes = nodesSorted.stream().filter(x -> x.getState().equals(NodeStateEnum.PENDING.name())).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(pendingNodes)){
            //当其中一个子号流程已完成，的特殊处理。
            if (nodesSorted.get(nodesSorted.size()-1).getState().equals(NodeStateEnum.FINISHED.name())){
                pendingNodes.get(0).setState(NodeStateEnum.FINISHED.name());
                return nodesSorted;
            }else{
                WorkNodeInstanceVO pending = pendingNodes.get(pendingNodes.size() - 1);
                int index = nodesSorted.indexOf(pending);
                List<WorkNodeInstanceVO> aheadNodes = nodesSorted.subList(0, index).stream().filter(x -> !x.getState().equals(NodeStateEnum.WAITING.name())).collect(Collectors.toList());
                //为什么要这么处理： 分支节点上还有另外的流程没有提交但此流程已提交，在此流程上分支节点上的显示状态改为已完成。但DB中还是进行中 （特殊 处理）
                aheadNodes.stream().forEach(x->{
                    if (x.getState().equals(NodeStateEnum.PENDING.name())){
                        x.setState(NodeStateEnum.FINISHED.name());
                    }
                });
                List<WorkNodeInstanceVO> backendNodes = nodesSorted.subList(index, nodesSorted.size());
                aheadNodes.addAll(backendNodes);
                return aheadNodes;
            }
        }
        List<WorkNodeInstanceVO> allFinishedNodes = nodesSorted.stream().filter(x -> x.getState().equals(NodeStateEnum.FINISHED.name())).collect(Collectors.toList());
        return allFinishedNodes;
    }

    @Override
    public List<NightingaleEchartsVO> instanceNodeCountByCode(String businessType) {
        List<NightingaleEchartsVO> list = workFlowNodeInstanceMapper.instanceNodeCountByCode(businessType);
        return list;
    }

    @Override
    public Boolean activateInstanceNode(Long id) {
        return this.update(new LambdaUpdateWrapper<WorkFlowNodeInstance>().eq(BaseEntity::getId,id).set(WorkFlowNodeInstance::getState,NodeStateEnum.PENDING.name()).eq(BaseEntity::getDeleted,Boolean.FALSE));
    }

    @Override
    public List<WorkNodeInstanceVO> instanceArray(InstanceNodeDTO instance) {
        Long businessId = instance.getBusinessId();
        String mainBusiness = instance.getMainBusiness();

        List<WorkNodeInstanceVO> instanceNodeFlow = workFlowNodeInstanceMapper.getInstanceNodeFlow(mainBusiness, businessId, null,null);

        List<WorkNodeInstanceVO> nodesSorted = Lists.newArrayList();
        Optional<WorkNodeInstanceVO> first = instanceNodeFlow.stream().filter(x -> x.getPNodeId() == null).findFirst();
        if (first.isPresent()){
            WorkNodeInstanceVO fistNode = first.get();
            nodesSorted.add(fistNode);
            //根据业务类型确定节点组
            for (WorkNodeInstanceVO workNodeInstanceVO : instanceNodeFlow){
                WorkNodeInstanceVO next = findNext(fistNode.getNodeId(), instanceNodeFlow);
                if (next !=null && !next.getNodeId().equals(fistNode.getNodeId())) {
                    fistNode = next;
                    nodesSorted.add(next);
                }
            }
        }
        return nodesSorted;
    }

    @Override
    public Boolean checkIfReverse(String productionType, String productionNo) {
        if (StrUtil.isNotBlank(productionType) && StrUtil.isNotBlank(productionNo) && productionNo.contains("-")){
            productionNo = productionNo.split("-")[0];
            List<String> nodeCodes = workFlowNodeInstanceMapper.checkIfReverse(productionType,productionNo);
            return CollectionUtil.isEmpty(nodeCodes);
        }
        return Boolean.TRUE;
    }

    /**
     * 查询节点完成时间
     * @param businessEnum
     * @param nodeEnum
     * @param businessId
     * @param businessSubId
     * @param businessMinId
     * @return
     */
    @Override
    public Date getNodeFinishedTime(MainBusinessEnum businessEnum, WorkflowNodeEnum nodeEnum, Long businessId, String businessSubId, String businessMinId) {
        WorkFlow flow = workFlowService.getOne(new LambdaQueryWrapper<WorkFlow>().eq(WorkFlow::getCode, businessEnum.name()).eq(BaseEntity::getDeleted, Boolean.FALSE).select(BaseEntity::getId));
        WorkNode node = workNodeService.getOne(new LambdaQueryWrapper<WorkNode>().eq(WorkNode::getFlowId, flow.getId()).eq(WorkNode::getCode, nodeEnum.name()).eq(BaseEntity::getDeleted, Boolean.FALSE).select(BaseEntity::getId));
        return workFlowNodeInstanceMapper.getNodeFinishedTime(flow.getId(),node.getId(),businessId,businessSubId,businessMinId,businessEnum.name());
    }

    @Override
    public Date getMajorProductionFinishedTime(Long businessId, Long nodeId) {
        return workFlowNodeInstanceMapper.getMajorProductionFinishedTime(businessId,nodeId);
    }

    @Override
    public List<WorkNodeInstanceVO> instanceQueue(InstanceNodeDTO instance) {
        String businessMinId = instance.getBusinessMinId();
        String businessSubId = instance.getBusinessSubId();
        Long businessId = instance.getBusinessId();
        String mainBusiness = instance.getMainBusiness();

        List<WorkNodeInstanceVO> instanceNodeFlow = workFlowNodeInstanceMapper.getInstanceNodeFlow(mainBusiness, businessId, businessSubId,businessMinId);

        List<WorkNodeInstanceVO> nodesSorted = Lists.newArrayList();
        Optional<WorkNodeInstanceVO> first = instanceNodeFlow.stream().filter(x -> x.getPNodeId() == null).findFirst();
        if (first.isPresent()){
            WorkNodeInstanceVO fistNode = first.get();
            nodesSorted.add(fistNode);

            List<ProductionEnum> production = instance.getProduction();

            //业务上还没有确定出具的产品类型时
            if (CollectionUtil.isEmpty(production)){
                for (WorkNodeInstanceVO workNodeInstanceVO : instanceNodeFlow){
                    WorkNodeInstanceVO next = findNext(fistNode.getNodeId(), instanceNodeFlow);
                    if (next ==null) return nodesSorted;
                    nodesSorted.add(next);
                    fistNode = next;
                    // 判断是资产业务需要多获取一个节点:产品选择节点 (资产的产品选择在客户反馈节点之后,略有不同)
                    if (instance.getMainBusiness().equals(ASSET_BUSINESS.name())){
                        if (CollectionUtil.isEmpty(production) && next.getNodeCode().equals(PRODUCT_CHOICE.name())) return nodesSorted;
                    }else {
                        if (CollectionUtil.isEmpty(production) && next.getNodeCode().equals(QUOTATION_FEEDBACK.name())) return nodesSorted;
                    }
                }

            }
            else {
                List<WorkflowNodeEnum> fixedWorkNodes = FixedWorkNode.getFixedNodesByBusinessType(mainBusiness);
                List<WorkflowNodeEnum> removeNodes = Lists.newArrayList();
                if (instance.getIfPay()!=null && !instance.getIfPay()){
                    removeNodes = FixedWorkNode.removeSaveFileNode(mainBusiness);
                }
                //根据业务类型确定节点组
                for (WorkNodeInstanceVO workNodeInstanceVO : instanceNodeFlow){
                    WorkNodeInstanceVO next = findNext(fistNode.getNodeId(), instanceNodeFlow);
                    if (next ==null)  return nodesSorted;
                    Map<String,List<WorkflowNodeEnum>> necessaryNodes = CallbackEnum.NecessaryNode.getByProduction(mainBusiness,production);
                    for (Map.Entry<String,List<WorkflowNodeEnum>> map : necessaryNodes.entrySet()){
                        List<String> codes = map.getValue().stream().map(WorkflowNodeEnum::getCode).collect(Collectors.toList());
                        fistNode = next;
                        if (codes.contains(next.getNodeCode())){
                            nodesSorted.add(next);
                            break;
                        }else{
                            WorkflowNodeEnum wne = valueOf(next.getNodeCode());
                            if (fixedWorkNodes.contains(wne) && !removeNodes.contains(wne)){
                                nodesSorted.add(next);
                                break;
                            }
                        }
                    }
                }
            }

        }
       return nodesSorted;
    }

    @Override
    public WorkNodeInstanceVO instanceCurrent(InstanceNodeDTO instance) {
        WorkNodeInstanceVO current = workFlowNodeInstanceMapper.instanceCurrent(instance.getMainBusiness(),instance.getBusinessId(),instance.getBusinessSubId(),instance.getBusinessMinId());
        return current;
    }

    private WorkNodeInstanceVO findNext(Long priviousId,List<WorkNodeInstanceVO> nodes){
        Optional<WorkNodeInstanceVO> target = nodes.stream().filter(n->n.getPNodeId()!=null)
                .filter(x -> (x.getPNodeId().equals(priviousId))).findFirst();
        return target.orElse(null);
    }

}
