/**
 * BladeX Commercial License Agreement
 * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
 * <p>
 * Use of this software is governed by the Commercial License Agreement
 * obtained after purchasing a license from BladeX.
 * <p>
 * 1. This software is for development use only under a valid license
 * from BladeX.
 * <p>
 * 2. Redistribution of this software's source code to any third party
 * without a commercial license is strictly prohibited.
 * <p>
 * 3. Licensees may copyright their own code but cannot use segments
 * from this software for such purposes. Copyright of this software
 * remains with BladeX.
 * <p>
 * Using this software signifies agreement to this License, and the software
 * must not be used for illegal purposes.
 * <p>
 * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
 * not liable for any claims arising from secondary or illegal development.
 * <p>
 * Author: Chill Zhuang (bladejava@qq.com)
 */
package org.springblade.modules.flowable.processForm.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import jodd.util.StringUtil;
import me.zhyd.oauth.model.AuthUser;
import org.apache.commons.lang.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.validation.ProcessValidator;
import org.flowable.validation.ProcessValidatorFactory;
import org.flowable.validation.ValidationError;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.modules.flowable.processForm.pojo.dto.ProcessDefDTO;
import org.springblade.modules.flowable.processForm.pojo.entity.*;
import org.springblade.modules.flowable.processForm.pojo.vo.AddProcessVo;
import org.springblade.modules.flowable.processForm.pojo.vo.ProcessDefLatestVO;
import org.springblade.modules.flowable.processForm.pojo.vo.ProcessDefReq;
import org.springblade.modules.flowable.processForm.pojo.vo.ProcessDefVO;
import org.springblade.modules.flowable.processForm.excel.ProcessDefExcel;
import org.springblade.modules.flowable.processForm.mapper.ProcessDefMapper;
import org.springblade.modules.flowable.processForm.service.IProcessDefService;
import org.springblade.modules.flowable.processForm.utils.FormUtils;
import org.springblade.modules.flowable.processForm.utils.GuardUtils;
import org.springblade.modules.nh.pojo.entity.DeviceEntity;
import org.springblade.modules.order.utils.PlantformUtils;
import org.springblade.modules.system.pojo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 流程定义模板，flowable流程定义根据该模板动态部署一个流程定义。 服务实现类
 *
 * @author lzp
 * @since 2024-08-02
 */
@Service
public class ProcessDefServiceImpl extends ServiceImpl<ProcessDefMapper, ProcessDefEntity> implements IProcessDefService {

	@Autowired
	private ProcessDefLatestServiceImpl latestService;

	@Autowired
	private ProcessDefSubmitStepServiceImpl submitStepService;

	@Autowired
	private ProcessDefAuditStepServiceImpl auditStepService;

	@Autowired
	private ProcessFormServiceImpl formService;

	@Autowired
	RepositoryService repositoryService;



	@Override
	public ProcessDefDTO getProcessForm(String id) {

		ProcessDefDTO vo = new ProcessDefDTO();
		ProcessDefVO defEntity = baseMapper.getDefById(id);
		vo.setDef(defEntity);
		ProcessDefSubmitStepEntity submitStep = submitStepService.fingByFromId(defEntity.getId());
		vo.setSubmitStep(submitStep);
		List<ProcessDefAuditStepEntity> list = auditStepService.findListByDefId(defEntity.getId());
		vo.setSteps(list);
		return vo;
	}

	@Override
	public IPage<ProcessDefVO> selectProcessDefPage(IPage<ProcessDefVO> page, ProcessDefVO processDef) {
		return page.setRecords(baseMapper.selectProcessDefPage(page, processDef));
	}




	@Override
	public List<ProcessDefExcel> exportProcessDef(Wrapper<ProcessDefEntity> queryWrapper) {
		List<ProcessDefExcel> processDefList = baseMapper.exportProcessDef(queryWrapper);
		//processDefList.forEach(processDef -> {
		//	processDef.setTypeName(DictCache.getValue(DictEnum.YES_NO, ProcessDef.getType()));
		//});
		return processDefList;
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public R saveProcessDef(AddProcessVo req) {
		ProcessDefEntity def = req.getDef();

		ProcessDefSubmitStepEntity submitStep = req.getSubmitStep();

		ProcessDefLatestVO latest = latestService.findDefLatesByFormId(def.getFormId());
		if(latest!=null && !"2".equals(latest.getIsDeleted())){
			return R.fail("该表单流程已存在，请勿重复添加！");
		}
		if("1".equals(submitStep.getSubmitType()) && StringUtils.isBlank(submitStep.getRoleId())){
			return R.fail("提交人角色id不能为空！");
		}else if("2".equals(submitStep.getSubmitType()) && StringUtils.isBlank(submitStep.getEmpUserId())){
			return R.fail("提交人的员工不能为空！");
		}
		Date date=new Date();

		BladeUser user = AuthUtil.getUser();
		def.setCreateTime(date);
		def.setCreateBy(user.getUserId().toString());
		def.setIsDeleted(0);
		def.setCreateName(user.getNickName());
		ProcessDefLatestEntity defLatest;
		if(latest!=null){
			def.setKeyValue(latest.getKeyValue());
			defLatest=latestService.findProcessDefLatest(latest.getKeyValue());
		}else{
			def.setKeyValue(PlantformUtils.getSiteNumber());
			defLatest=new ProcessDefLatestEntity();
			defLatest.setCreateTime(date);
			defLatest.setCreateBy(user.getUserId().toString());
			defLatest.setKeyValue(def.getKeyValue());
		}
		if(!save(def)){
			return R.fail("保存失败!请稍后再试");
		}
		defLatest.setProcessDefId(def.getId());
		if(StringUtils.isNotBlank(defLatest.getId())){
			latestService.updateById(defLatest);
		}else{
			latestService.save(defLatest);
		}
		Map<String, ProcessDefAuditStepEntity> map= Maps.newHashMap();  //放每个节点  的名称和id   用于驳回设置中保存返回的节点id
		submitStep.setCreateTime(date);
		submitStep.setProcDefId(def.getId());
		submitStep.setCreateBy(user.getUserId().toString());
		submitStepService.save(submitStep);
		List<ProcessDefAuditStepEntity> steps=req.getSteps();
		if(FormUtils.SUPPLY_ORDER_APPLICATION.equals(def.getFormId()) && steps.size()<2){
			return R.fail("订单申请的流程设置必须超过2级节点!");
		}
	//	try {
		int count=1;
		for(ProcessDefAuditStepEntity 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(),"审批人的员工不能为空！");
			}
			step.setId(IdUtil.randomUUID().replaceAll("-", ""));
			step.setProcDefId(def.getId());
			step.setCreateBy(user.getUserId().toString());
			step.setCreateTime(date);
			step.setTransDepartment("0");
			step.setStepName(step.getName());
			step.setSort(count);
			map.put(step.getItemName(),step);
			count++;
			String rejectName=step.getRejectStepId();
			if(StringUtils.isNotBlank(rejectName)){
				ProcessDefAuditStepEntity stepq=map.get(rejectName);
				if(FormUtils.STEPONE.equals(rejectName)){
					step.setRejectStepId(submitStep.getId());
					step.setRejectStepName(FormUtils.STEPONE);
					step.setRejectStepType("0");
				}else if(stepq != null){
					step.setRejectStepId(stepq.getId());
					step.setRejectStepName(stepq.getName());
					step.setRejectStepType("1");
				}
			}else{
				throw new ServiceException("未找到驳回节点!"+rejectName);
			}
		}
	/*	}catch (Exception e){
			log.error("添加流程解析错误：{}",e);
			return R.fail(e.getMessage());
		}*/
		if(!auditStepService.saveBatch(steps)){
			throw new ServiceException("保存失败，请稍后再试!");
		}
		saveProcess(def,steps);

		return R.status(true);
	}

	@Override
	public R updateProcessDef(AddProcessVo req) {

		String processId=req.getProcessId();
		ProcessDefEntity def = req.getDef();

		ProcessDefSubmitStepEntity submitStep = req.getSubmitStep();
		if("1".equals(submitStep.getSubmitType()) && StringUtils.isBlank(submitStep.getRoleId())){
			return R.fail("提交人角色id不能为空！");
		}else if("2".equals(submitStep.getSubmitType()) && StringUtils.isBlank(submitStep.getEmpUserId())){
			return R.fail("提交人的员工不能为空！");
		}

		//获得该表单之前的流程
		ProcessDefEntity oldDef=this.getById(processId);
		Date date=new Date();
		BladeUser user = AuthUtil.getUser();
		def.setCreateTime(date);
		def.setCreateBy(user.getUserId().toString());
		def.setIsDeleted(oldDef.getIsDeleted());
		def.setCreateName(user.getNickName());
		def.setKeyValue(oldDef.getKeyValue());
		if(!this.save(def)){
			return R.fail("保存失败！稍后再试");
		}
		submitStep.setCreateTime(date);
		submitStep.setProcDefId(def.getId());
		submitStep.setCreateBy(user.getUserId().toString());
		submitStepService.save(submitStep);
		Map<String,ProcessDefAuditStepEntity> map=new HashMap<>();  //放每个节点  的名称和id   用于驳回设置中保存返回的节点id
		List<ProcessDefAuditStepEntity> steps=req.getSteps();
		int count=1;

		for(ProcessDefAuditStepEntity 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(),"审批人的员工不能为空！");
			}
			step.setSort(count);
			step.setId(IdUtil.randomUUID().replaceAll("-", ""));
			step.setProcDefId(def.getId());
			step.setCreateBy(user.getUserId().toString());
			step.setCreateTime(date);
			step.setTransDepartment("0");
			step.setStepName(step.getName());
			map.put(step.getItemName(),step);
			count++;
			String rejectName=step.getRejectStepId();
			if(StringUtil.isNotBlank(rejectName)){
				ProcessDefAuditStepEntity stepq=map.get(rejectName);
				 if(FormUtils.STEPONE.equals(rejectName)){
					 step.setRejectStepId(submitStep.getId());
					 step.setRejectStepName(FormUtils.STEPONE);
					 step.setRejectStepType("0");
				}else if(stepq != null){
					 step.setRejectStepId(stepq.getId());
					 step.setRejectStepName(stepq.getName());
					 step.setRejectStepType("1");
				}
			}
		}
		ProcessDefLatestEntity  defLatest=latestService.findProcessDefLatest(oldDef.getKeyValue());
		defLatest.setProcessDefId(def.getId());
		if(!latestService.updateById(defLatest)){
			throw new ServiceException("保存失败，请稍后再试");
		}

		if(!auditStepService.saveBatch(steps)){
			throw new ServiceException("保存失败，请稍后再试");
		}

		saveProcess(def,steps);
		return R.status(true);
	}


	@Override
	public List<ProcessFormEntity> findFromDefList() {
		return formService.findFromDefList();
	}

	@Override
	public boolean changeProcessStatus(String id, String type) {
		boolean defEntity = baseMapper.updateStatus(type,id);
		return defEntity;

	}

	@Override
	public String findUserProcess(String formId) {
		BladeUser user = AuthUtil.getUser();
		ProcessDefReq submitRecord = baseMapper.getDefByFormId(formId);
		if(submitRecord!=null){
			//(3.服务商 4.平台员工 5.服务商人员 6.工程师)
			String submitType=submitRecord.getSubmitType();// 提交人类型 1平台角色 2 平台员工 3 代理商
			if("3".equals(submitType)){
				if("6".equals(user.getDeptId()) ||"5".equals(user.getDeptId())){
					return "200";
				}
			}else if("1".equals(submitType)){
				if(user.getRoleId().contains(submitRecord.getRoleId())){
					return "200";
				}
			}else if("2".equals(submitType)){
				if(submitRecord.getEmpUserId().contains(user.getUserId().toString()) && "4".equals(user.getDeptId())){
					return "200";
				}
			}
		}
		return "201";
	}


	public void saveProcess(ProcessDefEntity def,List<ProcessDefAuditStepEntity> 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;
			}

			ProcessDefAuditStepEntity 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){
				ProcessDefAuditStepEntity coNext = auditSteps.get(i-1);
				task.setOwner(coNext.getCcUserId());
			}
			task.setCategory(FormUtils.TYPE_ONE);
			p.addFlowElement(task);
			task.setTaskListeners(listener3);

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

			if("1".equals(co.getAuditMethod())|| "2".equals(co.getAuditMethod())  || "3".equals(co.getAuditMethod()) ){ //会签 . 依次审批  //或签
				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(FormUtils.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();

	}




}
