package com.sifang.modules.flowable.service;

import com.jfinal.plugin.activerecord.Record;
import com.sifang.modules.flowable.dao.ProcessDefAuditStepDao;
import com.sifang.modules.flowable.dao.ProcessDefDao;
import com.sifang.modules.flowable.dao.ProcessDefLatestDao;
import com.sifang.modules.flowable.dao.ProcessDefSubmitStepDao;
import com.sifang.modules.flowable.entity.ProcessDef;
import com.sifang.modules.flowable.entity.ProcessDefAuditStep;
import com.sifang.modules.flowable.entity.ProcessDefLatest;
import com.sifang.modules.flowable.entity.ProcessDefSubmitStep;
import com.sifang.modules.flowable.utils.FormUtils;
import com.sifang.modules.flowable.utils.TaskVo;
import com.sifang.modules.flowable.vo.AddProcessVo;
import com.sifang.modules.flowable.vo.ProcessDefStep;
import com.sifang.modules.sys.common.UserUtils;
import com.sifang.modules.sys.common.entity.User;
import com.sifang.modules.sys.common.service.BaseService;
import com.sifang.modules.sys.common.utils.IdGen;
import com.sifang.modules.sys.common.utils.StringUtil;
import com.sifang.modules.sys.util.GuardUtils;
import com.sifang.modules.sys.util.LayuiPage;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.engine.*;
import org.flowable.engine.repository.Deployment;
import org.flowable.validation.ProcessValidator;
import org.flowable.validation.ProcessValidatorFactory;
import org.flowable.validation.ValidationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;


@Service
public class ProcessDefService extends BaseService {

    @Autowired
    ProcessDefDao defDao;

    @Autowired
    ProcessDefSubmitStepDao stepDao;

    @Autowired
    ProcessDefLatestDao latestDao;

    @Autowired
    ProcessDefAuditStepDao auditStepDao;

    @Autowired
    RepositoryService repositoryService;



    //列表数据
    public void processFormList(LayuiPage<Record> page, Map<String, String> map){
        defDao.processFormList(page,map);
    }

    //获取表单 对应的字段
    public List<Record> getAllFields(String formId){
        return latestDao.getAllFields(formId);
    }

    // 启用/禁用/删除 流程
    public int changeProcessStatus(String processId,String type){
        logger.info(">>>>>>>>>>>>>startUsing  processId:{},type:{} ",processId,type);
        return defDao.changeProcessStatus(processId,type);
    }

    //获取流程详情
    public Map<String,Object>  getProcessDefDetail(String processId){
        logger.info(">>>>>>>>>>>>>getProcessDefDetail  processId:{}",processId);
        Map<String,Object> map=new HashMap<>();
        Record processDef= defDao.getDefDetail(processId);
        map.put("processDef",processDef);
        Record submitStep= stepDao.getSubmitStep(processId);
        map.put("submitStep",submitStep);
        List<Record> stepList=auditStepDao.processStep(processId);
        map.put("stepList",stepList);
        map.put("stepCount",stepList==null?0:stepList.size());
        return map;
    }

    //取操作表单的下拉选(还没有设置流程的表单)
    public List<Record> getForm(){
        return defDao.getForm();
    }

    public List<Record> getAllRole(){
        return defDao.getAllRole();
    }

    //获取所有平台员工
    public List<Record> getAllPlateEmp(){
        List<Record> list=defDao.getAllPlateEmp();
        return list;
    }

    //保存流程
    @Transactional(rollbackFor = Exception.class)
    public void  saveProcessDef(AddProcessVo addProcessVo){
        ProcessDef def=addProcessVo.getDef();
        logger.info(">>>>>>>>>>>>>>>>>>>saveProcessDef def:{}",def.toString());
        ProcessDefSubmitStep submitStep=addProcessVo.getSubmitStep();
        logger.info(">>>>>>>>>>>>>>>>>>>saveProcessDef submitStep:{}",submitStep.toString());
        GuardUtils.notBlank(def.getFormId(),"流程表单不能为空！");
        //判断该表单是否已经有流程了
        Record oldDef= latestDao.getLatest(def.getFormId());
        if(oldDef!=null && !"2".equals(oldDef.getStr("status"))){
            throw new IllegalArgumentException("该表单流程已存在，请勿重复添加！");
        }
        GuardUtils.notBlank(def.getName(),"流程名称不能为空！");
        GuardUtils.notBlank(def.getDescription(),"流程描述不能为空！");
        GuardUtils.notBlank(submitStep.getSubmitType(),"提交人角色类型不能为空！");
        if("1".equals(submitStep.getSubmitType())){
            GuardUtils.notBlank(submitStep.getRoleId(),"提交人角色id不能为空！");
        }else if("2".equals(submitStep.getSubmitType())){
            GuardUtils.notBlank(submitStep.getEmpUserId(),"提交人的员工不能为空！");
        }
        logger.info(">> 保存新建的流程 saveProcessDef {name} :" +def.getName());
        Date date=new Date();
        User currentUser = UserUtils.getUser();
        def.setCreateTime(date);
        def.setCreateBy(currentUser.getId());
        def.setStatus("0");

        ProcessDefLatest defLatest;
        if(oldDef!=null){
            def.setKeyValue(oldDef.getStr("key_value"));
            defLatest=latestDao.getByKey(oldDef.getStr("key_value"));

        }else{
            def.setKeyValue(getonlyKey(date));
            defLatest=new ProcessDefLatest();
            defLatest.setCreateTime(date);
            defLatest.setCreateBy(currentUser.getId());
            defLatest.setKeyValue(def.getKeyValue());

        }
        logger.info(">>>>>>>>>>>>>>>>>>>saveProcessDef defLatest:{}",defLatest.toString());
        defDao.save(def);
        defLatest.setProcessDefId(def.getId());
        latestDao.save(defLatest);

        Map<String,ProcessDefAuditStep> map=new HashMap<>();  //放每个节点  的名称和id   用于驳回设置中保存返回的节点id


        submitStep.setCreateTime(date);
        submitStep.setProcDefId(def.getId());
        submitStep.setCreateBy(currentUser.getId());
        stepDao.save(submitStep);

        List<ProcessDefStep> steps=addProcessVo.getSteps();
        List<ProcessDefAuditStep> auditSteps=new ArrayList<>();

        if(FormUtils.SUPPLY_ORDER_APPLICATION.equals(def.getFormId()) && steps.size()<2){
            throw new IllegalArgumentException("订单申请的流程设置必须超过2级节点!");
        }

        int count=1;
        for(ProcessDefStep step:steps){

            GuardUtils.notBlank(step.getName(),"第"+ count +"个审批的 节点名称不能为空！");
            GuardUtils.notBlank(step.getAuditType(),"第"+ count +"个审批，审批人角色类型不能为空！");
            GuardUtils.notBlank(step.getAuditMethod(),"第"+ count +"个审批，审批方式不能为空");
            if("1".equals(step.getAuditType())){
                GuardUtils.notBlank(step.getRoleId(),"审批人角色id不能为空！");
            }else if("2".equals(step.getAuditType())){
                GuardUtils.notBlank(step.getEmpUserId(),"审批人的员工不能为空！");
            }
            ProcessDefAuditStep auditStep=new ProcessDefAuditStep();
            auditStep.setId(IdGen.uuid());
            auditStep.setName(step.getName());
            auditStep.setRoleId(step.getRoleId());
            auditStep.setItemName(step.getItemName());
            auditStep.setAuditType(step.getAuditType());
            auditStep.setRoleName(step.getRoleName());
            auditStep.setEmpUserId(step.getEmpUserId());
            auditStep.setAuditMethod(step.getAuditMethod());
            auditStep.setProcDefId(def.getId());
            auditStep.setCcUserId(step.getCcUserId());
            auditStep.setRejectStepName(step.getRejectStepName());
            auditStep.setRejectStepId(step.getRejectStepName());
            auditStep.setCreateBy(currentUser.getId());
            auditStep.setCreateTime(date);
            auditStep.setSort(Integer.parseInt(step.getSort()));
            auditStep.setTransDepartment("0");
            auditStep.setStepName(step.getName());
            auditSteps.add(auditStep);
            map.put(auditStep.getItemName(),auditStep);

            count++;
            logger.info(">>>>>>>>>>>>>>>>>>>saveProcessDef step:{}",step.toString());
        }

        for(ProcessDefAuditStep fiStep:auditSteps){
            String rejectName=fiStep.getRejectStepId();
            if(StringUtil.isNotBlank(rejectName)){
                ProcessDefAuditStep step=map.get(rejectName);
                if(step!=null){
                    fiStep.setRejectStepId(step.getId());
                    fiStep.setRejectStepName(step.getName());
                    fiStep.setRejectStepType("1");
                }else if(step==null && ProcessDefStep.STEPONE.equals(rejectName)){
                    fiStep.setRejectStepId(submitStep.getId());
                    fiStep.setRejectStepName(ProcessDefStep.STEPONE);
                    fiStep.setRejectStepType("0");
                }else{
                    throw new IllegalArgumentException("未找到驳回节点!"+rejectName);
                }
            }
            logger.info(">>>>>>>>>>>>>>>>>>>saveProcessDef fiStep:{}",fiStep.toString());
        }
        logger.info(">>>>>>>>>>>>>>>>>>>saveProcessDef auditSteps.size:{}",auditSteps.size());
        auditStepDao.save(auditSteps);
        saveProcess(def,auditSteps);
    }

    public synchronized String getonlyKey(Date date){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        return sdf.format(date);
    }

    public void saveProcess(ProcessDef def,List<ProcessDefAuditStep> auditSteps){

        StartEvent startEvent = new StartEvent();
        startEvent.setId("start_event");
        startEvent.setName("开始事件");

        EndEvent endEvent = new EndEvent();
        endEvent.setId("end_event");
        endEvent.setName("结束事件");

        FlowableListener endListener = new FlowableListener();
        endListener.setEvent("end");
        endListener.setImplementationType("delegateExpression");
        endListener.setImplementation("${myListener}");

        List<FlowableListener> listeners = new ArrayList<>();
        listeners.add(endListener);
        endEvent.setExecutionListeners(listeners);


        FlowableListener rejectStart=new FlowableListener();
        rejectStart.setEvent("create");
        rejectStart.setImplementationType("delegateExpression");
        rejectStart.setImplementation("${rejectStartListener}");
        List<FlowableListener> listener2 = new ArrayList<>();
        listener2.add(rejectStart);

        FlowableListener oneTaskCompleteTask=new FlowableListener();
        oneTaskCompleteTask.setEvent("create");
        oneTaskCompleteTask.setImplementationType("delegateExpression");
        oneTaskCompleteTask.setImplementation("${oneTaskCompleteListener}");
        List<FlowableListener> listener3=new ArrayList<>();
        listener3.add(oneTaskCompleteTask);

        /*FlowableListener oneTaskFinishTask=new FlowableListener();
        oneTaskFinishTask.setEvent("complete");
        oneTaskFinishTask.setImplementationType("delegateExpression");
        oneTaskFinishTask.setImplementation("${oneTaskFinishListener}");
        listener3.add(oneTaskFinishTask);*/

        EndEvent rejectEvent = new EndEvent();
        rejectEvent.setId("reject_event");
        rejectEvent.setName("拒绝事件");

        org.flowable.bpmn.model.Process p = new org.flowable.bpmn.model.Process();
        p.setName(def.getName());
        p.setId("Process_"+def.getId());

        p.addFlowElement(startEvent);
        p.addFlowElement(endEvent);
        p.addFlowElement(rejectEvent);

        ExclusiveGateway gwser = new ExclusiveGateway();
        gwser.setId("gwser1");
        gwser.setName("gwser1");
        p.addFlowElement(gwser);

        String gwId="";
        String StepId="";
        ExclusiveGateway gw1=null;
        /* SequenceFlow flow_gwser_task1=null;*/
        String task1="";
        UserTask task1F= new UserTask();
        for(int i=0;i<=auditSteps.size();i++) {

            if (i == auditSteps.size()) {


                SequenceFlow flow_gw_end = new SequenceFlow(gwId, endEvent.getId());
                SequenceFlow flow_gw_reject = new SequenceFlow(gwId, StepId);//审核不通过，跳转到设置好的驳回节点
                //  SequenceFlow flow_gw_StepId = new SequenceFlow(gwId, StepId);//拒绝到达的节点
                flow_gw_end.setConditionExpression("${approve}");
                flow_gw_reject.setConditionExpression("${!approve}");
                gw1.setOutgoingFlows(Arrays.asList(flow_gw_end, flow_gw_reject));
                p.addFlowElement(flow_gw_end);
                p.addFlowElement(flow_gw_reject);
                break;
            }

            ProcessDefAuditStep co = auditSteps.get(i);
            String role_id = co.getRoleId();
            String name = co.getStepName();
            String id = co.getId();
            UserTask task = new UserTask();
            task.setId("customer_Task_"+id);
            task.setName(name);
            task.setFormKey(def.getFormId());
            //这一级存上一级的抄送id
            if(i>0){
                ProcessDefAuditStep coNext = auditSteps.get(i-1);
                task.setOwner(coNext.getCcUserId());
            }
            task.setCategory(TaskVo.TYPE_ONE);
            p.addFlowElement(task);
            task.setTaskListeners(listener3);

            ExclusiveGateway gw = new ExclusiveGateway();
            gw.setId("GATEWAY_" + i);
            gw.setName("GATEWAY_" + i);

            if(StringUtil.equalsAny(co.getAuditMethod(),"1","2","3")){ //会签 . 依次审批  //或签
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics=new MultiInstanceLoopCharacteristics();
                if("2".equals(co.getAuditMethod()) || "1".equals(co.getAuditMethod())){
                    multiInstanceLoopCharacteristics.setSequential(false);
                }else {
                    multiInstanceLoopCharacteristics.setSequential(true);
                }
                //设置审批人参数
                multiInstanceLoopCharacteristics.setInputDataItem("assigneeList"+co.getSort());
                multiInstanceLoopCharacteristics.setElementVariable("assignee");
                if("1".equals(co.getAuditMethod())){
                    multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances==1}");
                }else {
                    multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances==nrOfInstances || !approve}");

                }
                task.setAssignee("${assignee}");

                task.setLoopCharacteristics(multiInstanceLoopCharacteristics);
            }else if("4".equals(co.getAuditMethod())){  //任意人
                task.setAssignee("${assignee"+co.getSort()+"}");
            }

            if (i == 0) {
                task1=task.getId();
                SequenceFlow flow_start_sertask = new SequenceFlow(startEvent.getId(), gwser.getId());
                SequenceFlow flow_gwser_task1 = new SequenceFlow(gwser.getId(), task.getId());
                //直接到最后一个节点
                //  CrmProcessDefAuditStep lastStep = auditSteps.get(auditSteps.size()-1);
                //   SequenceFlow flow_gwser_task4 = new SequenceFlow(gwser.getId(), "customer_Task_"+lastStep.getId());
                //  flow_gwser_task1.setConditionExpression("${service}");
                //   flow_gwser_task4.setConditionExpression("${!service}");
                //   gwser.setOutgoingFlows(Arrays.asList(flow_gwser_task1, flow_gwser_task4));

                //   p.addFlowElement(flow_start_sertask);
                /*  SequenceFlow flow_start_task1 = new SequenceFlow(gwser.getId(), task.getId());*/
                SequenceFlow flow_task_gw = new SequenceFlow(task.getId(), gw.getId());
                p.addFlowElement(flow_gwser_task1);
                p.addFlowElement(flow_task_gw);
                //    p.addFlowElement(flow_gwser_task4);
                p.addFlowElement(flow_start_sertask);
            } else {
                SequenceFlow flow_gw_task = new SequenceFlow(gwId, task.getId());
                SequenceFlow flow_gw_event = new SequenceFlow(gwId, StepId);
                //  SequenceFlow flow_gw_StepId = new SequenceFlow(gwId, StepId);//拒绝到达的节点

                flow_gw_task.setConditionExpression("${approve}");
                //  flow_gw_StepId.setConditionExpression("${back}");//驳回到某一节点
                flow_gw_event.setConditionExpression("${!approve}");
                gw1.setOutgoingFlows(Arrays.asList(flow_gw_event,flow_gw_task));
                SequenceFlow flow_task_gw2 = new SequenceFlow(task.getId(), gw.getId());

                p.addFlowElement(flow_gw_task);
                p.addFlowElement(flow_task_gw2);
                p.addFlowElement(flow_gw_event);
                //   p.addFlowElement(flow_gw_StepId);
            }
            if("0".equals(co.getRejectStepType())){
                /* StepId = startEvent.getId();*/

                if(StringUtil.isBlank(task1F.getId())){
                    task1F.setId("customer_Task_"+co.getRejectStepId());
                    task1F.setName("驳回");
                    task1F.setFormKey(def.getFormId());
                    task1F.setCategory(TaskVo.TYPE_TWO);
                    task1F.setAssignee("${assigneeUserId}");
                    task1F.setTaskListeners(listener2);
                    p.addFlowElement(task1F);


                    SequenceFlow flow_gw_task = new SequenceFlow(task1F.getId(), task1);
                    flow_gw_task.setConditionExpression("${approve}");
                    p.addFlowElement(flow_gw_task);
                }
                StepId=task1F.getId();
            }else{
                StepId = "customer_Task_"+co.getRejectStepId();
            }
            gwId= gw.getId();
            gw1 = gw;
            p.addFlowElement(gw);
            System.out.println("StepId: "+StepId);

        }

        BpmnModel bpmnModel = new BpmnModel();
        bpmnModel.addProcess(p);

        ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory();
        ProcessValidator defaultProcessValidator = processValidatorFactory.createDefaultProcessValidator();
//        // 验证失败信息的封装ValidationError
        List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
        System.out.println(validate);
        /*    if (validate.size() > 0) {
                throw new RuntimeException("流程有误，请检查后重试");
            }*/

        BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
        byte[] convertToXML = bpmnXMLConverter.convertToXML(bpmnModel);
        String bytes = new String(convertToXML);
        System.out.println(bytes);
        Deployment deploy = repositoryService.createDeployment()
                .name(def.getName())
                .key(def.getId())
                .addString( p.getId()+".bpmn20.xml", bytes)
                .deploy();

    }

    /**
     * 编辑流程
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateProcessDef(AddProcessVo addProcessVo){
        String processId=addProcessVo.getProcessId();
        logger.info(">>>>>>>>>>>>updateProcessDef processId:{} ",processId);
        ProcessDef def=addProcessVo.getDef();
        GuardUtils.notBlank(processId,"要编辑的流程id,不能为空");
        GuardUtils.notBlank(def.getFormId(),"流程表单不能为空！");
        GuardUtils.notBlank(def.getName(),"流程名称不能为空！");
        GuardUtils.notBlank(def.getDescription(),"流程描述不能为空！");

        ProcessDefSubmitStep submitStep=addProcessVo.getSubmitStep();
        logger.info(">>>>>>>>>>>>updateProcessDef submitStep:{} ",submitStep);
        GuardUtils.notBlank(submitStep.getSubmitType(),"提交人角色类型不能为空！");
        if("1".equals(submitStep.getSubmitType())){
            GuardUtils.notBlank(submitStep.getRoleId(),"提交人角色id不能为空！");
        }else if("2".equals(submitStep.getSubmitType())){
            GuardUtils.notBlank(submitStep.getEmpUserId(),"提交人的员工不能为空！");
        }

        //获得该表单之前的流程
        ProcessDef oldDef=defDao.get(processId);

        logger.info(">> 编辑流程 updateProcessDef {name} :" +def.getName());
        Date date=new Date();
        User currentUser = UserUtils.getUser();
        def.setCreateTime(date);
        def.setCreateBy(currentUser.getId());
        def.setStatus(oldDef.getStatus());
        def.setKeyValue(oldDef.getKeyValue());
        defDao.save(def);
        logger.info(">> 编辑流程 updateProcessDef def :{} " ,def.toString());
        Map<String,ProcessDefAuditStep> map=new HashMap<>();  //放每个节点  的名称和id   用于驳回设置中保存返回的节点id

        submitStep.setCreateTime(date);
        submitStep.setProcDefId(def.getId());
        submitStep.setCreateBy(currentUser.getId());
        stepDao.save(submitStep);


        List<ProcessDefStep> steps=addProcessVo.getSteps();
        List<ProcessDefAuditStep> auditSteps=new ArrayList<>();

        int count=1;
        if(FormUtils.SUPPLY_ORDER_APPLICATION.equals(def.getFormId()) && steps.size()<2){
            throw new IllegalArgumentException("订单申请的流程设置必须超过2级节点!");
        }
        for(ProcessDefStep step:steps){

            GuardUtils.notBlank(step.getName(),"第"+ count +"个审批的 节点名称不能为空！");
            GuardUtils.notBlank(step.getAuditType(),"第"+ count +"个审批，审批人角色类型不能为空！");
            GuardUtils.notBlank(step.getAuditMethod(),"第"+ count +"个审批，审批方式不能为空");
            if("1".equals(step.getAuditType())){
                GuardUtils.notBlank(step.getRoleId(),"审批人角色id不能为空！");
            }else if("2".equals(step.getAuditType())){
                GuardUtils.notBlank(step.getEmpUserId(),"审批人的员工不能为空！");
            }
            ProcessDefAuditStep auditStep=new ProcessDefAuditStep();
            auditStep.setId(IdGen.uuid());
            auditStep.setName(step.getName());
            auditStep.setRoleId(step.getRoleId());
            auditStep.setItemName(step.getItemName());
            auditStep.setAuditType(step.getAuditType());
            auditStep.setRoleName(step.getRoleName());
            auditStep.setEmpUserId(step.getEmpUserId());
            auditStep.setAuditMethod(step.getAuditMethod());
            auditStep.setCcUserId(step.getCcUserId());
            auditStep.setRejectStepId(step.getRejectStepName());
            auditStep.setRejectStepName(step.getRejectStepName());
            auditStep.setProcDefId(def.getId());
            auditStep.setCreateBy(currentUser.getId());
            auditStep.setCreateTime(date);
            auditStep.setSort(Integer.parseInt(step.getSort()));
            auditStep.setTransDepartment("0");
            auditStep.setStepName(step.getName());
            auditSteps.add(auditStep);

            map.put(auditStep.getItemName(),auditStep);
            logger.info(">> 编辑流程 updateProcessDef auditStep :{} " ,auditStep.toString());
            count++;
        }

        for(ProcessDefAuditStep fiStep:auditSteps){
            String rejectName=fiStep.getRejectStepId();
            if(StringUtil.isNotBlank(rejectName)){
                ProcessDefAuditStep step=map.get(rejectName);
                if(step!=null){
                    fiStep.setRejectStepId(step.getId());
                    fiStep.setRejectStepName(step.getName());
                    fiStep.setRejectStepType("1");
                }else if(step==null && ProcessDefStep.STEPONE.equals(rejectName)){
                    fiStep.setRejectStepId(submitStep.getId());
                    fiStep.setRejectStepName(ProcessDefStep.STEPONE);
                    fiStep.setRejectStepType("0");
                }else{
                    throw new IllegalArgumentException("未找到驳回节点!");
                }
            }
            logger.info(">> 编辑流程 updateProcessDef fiStep :{} " ,fiStep.toString());
        }
        ProcessDefLatest  defLatest=latestDao.getByKey(oldDef.getKeyValue());
        defLatest.setProcessDefId(def.getId());
        latestDao.save(defLatest);

        auditStepDao.save(auditSteps);
        logger.info(">> 编辑流程 updateProcessDef auditSteps :{} " ,auditSteps.size());
        saveProcess(def,auditSteps);

    }

}
