package com.tykj.workflowcore.workflow_editer.service.impl;

import com.tykj.workflowcore.api.service.SpringBeanService;
import com.tykj.workflowcore.base.result.ApiException;
import com.tykj.workflowcore.base.util.FileUtil;
import com.tykj.workflowcore.workflow_editer.util.MapUtils;
import com.tykj.workflowcore.workflow_editer.validate.ProcessValidatorFactoryExt;
import com.tykj.workflowcore.workflow_editer.entity.*;
import com.tykj.workflowcore.workflow_editer.dao.FlowsInfoMapper;
import com.tykj.workflowcore.workflow_editer.entity.vo.*;
import com.tykj.workflowcore.workflow_editer.listener.ProcessEndListener;
import com.tykj.workflowcore.workflow_editer.service.NodeInfoService;
import com.tykj.workflowcore.workflow_editer.service.UserService;
import com.tykj.workflowcore.workflow_editer.service.VariableStorageService;
import com.tykj.workflowcore.workflow_editer.service.WorkFlowService;
import com.tykj.workflowcore.workflow_editer.util.UserServiceBeanUtil;
import io.swagger.annotations.Api;
import liquibase.pro.packaged.E;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.validation.ProcessValidator;
import org.flowable.validation.ValidationError;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ClassName:    FlowableServiceImpl
 * Package:    com.tykj.service
 * Description:
 * Datetime:    2021/2/23   13:46
 *
 * @Author: zsp
 */
@Service
public class WorkFlowServiceImpl implements WorkFlowService {

    private static String XML_FILE_PATH = "\\xml\\";
    private static String BASE_PATH = System.getProperty("user.dir");
    private final RepositoryService repositoryService;
    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final FlowsInfoMapper flowsInfoMapper;
    private final UserService userService;
    private final HistoryService historyService;
    private final ProcessEngineConfigurationImpl processEngineConfiguration;
    private final ProcessEngine processEngine;

    private final VariableStorageService variableStorageService;
    @Autowired
    ProcessValidatorFactoryExt processValidatorFactoryExt;

    final
    ClassLoader classLoader;

    @Autowired
    ProcessEndListener processEndListener;

    @Autowired
    NodeInfoService nodeInfoService;

    public WorkFlowServiceImpl(SpringBeanService springBeanService, HistoryService historyService, RepositoryService repositoryService, RuntimeService runtimeService, TaskService taskService, FlowsInfoMapper flowsInfoMapper, ProcessEngineConfigurationImpl processEngineConfiguration, ProcessEngine processEngine, VariableStorageService variableStorageService, ClassLoader classLoader) {
        this.historyService = historyService;
        this.repositoryService = repositoryService;
        this.runtimeService = runtimeService;
        this.taskService = taskService;
        this.flowsInfoMapper = flowsInfoMapper;
        this.userService = UserServiceBeanUtil.getUserService(springBeanService);
        this.processEngineConfiguration = processEngineConfiguration;
        this.processEngine = processEngine;
        this.variableStorageService = variableStorageService;
        this.classLoader = classLoader;
        System.out.println(userService != null ? "成功" : "失败");
    }

    @Override
    public String upLoadFile(@RequestParam("file") MultipartFile file) {
        String realPath = null;
        if (!file.isEmpty()) {
            //获取文件名
            String fileName = file.getOriginalFilename();
            System.out.println(fileName);
            //获取文件后缀
            String suffixName = Objects.requireNonNull(fileName).substring(fileName.lastIndexOf("."));
            //重新生成文件名
            fileName = UUID.randomUUID() + suffixName;
            System.out.println(fileName);
            //指定本地存入路径
            File fileNew = new File(classLoader.getResource("").getPath() + fileName);
            realPath = classLoader.getResource("").getPath() + fileName;
            try {
                file.transferTo(fileNew);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return realPath;
    }

//    @Override
//    public void flowXml(@RequestBody FlowsInfoVo flowsInfoVo) throws XMLStreamException, IOException {
//        String basePath = System.getProperty("user.dir") + "\\xml\\";
//        Integer id = flowsInfoVo.getId();
//        String flowKey = flowsInfoVo.getFlowKey();
//        String fileXml = flowsInfoVo.getFileXml();
//        //根据ID查询出一个flowsinfo
//        if (id != null){
//            FlowsInfo oldFlowInfo = flowsInfoMapper.findById(id).get();
//            //得到老的fileXml
//            String filePath = System.getProperty("user.dir")+oldFlowInfo.getFilePath();
//            File oldFile = new File(filePath);
//            File file = new File(filePath);
//            if(!file.exists()){
//                flowsInfoVo.setIsOld(1);
//            }else {
//                FileInputStream inputStream = new FileInputStream(file);
//                int length = inputStream.available();
//                byte bytes[] = new byte[length];
//                inputStream.read(bytes);
//                inputStream.close();
//                String oldXml =new String(bytes, StandardCharsets.UTF_8);
//                if (oldXml.equals(fileXml)){
//                    flowsInfoVo.setIsOld(0);
//                }else {
//                    flowsInfoVo.setIsOld(1);
//                }
//            }
//        }else {
//            flowsInfoVo.setIsOld(1);
//        }
//        //end
//
//        //生成xml文件
//        File file = FileUtil.createFileByString(basePath + flowKey + "bpmn20.xml",
//                flowsInfoVo.getFileXml().replaceAll("\\[\\?\\?[^\\]]+\\?\\?\\]", "").replaceAll("&lt;","<").replaceAll("&gt;",">"));
//
//        FileUtil.createFileByString(basePath + flowKey + "bpmnCustom20.xml", flowsInfoVo.getFileXml());
//
//        FlowsInfo flowsInfo = new FlowsInfo();
//        BeanUtils.copyProperties(flowsInfoVo, flowsInfo);
//        flowsInfo.setResourceName(flowKey + "bpmn20.xml");
//        flowsInfo.setFilePath("\\xml\\" + flowKey + "bpmn20.xml");
//        flowsInfo.setFileCustomPath("\\xml\\" + flowKey + "bpmnCustom20.xml");
//        flowsInfo.setId(id);
//        //进行第一部的校验
//        InputStream inputStream = new FileInputStream(file);//实例化FileInputStream
//        BpmnXMLConverter converter = new BpmnXMLConverter();
//        XMLInputFactory factory = XMLInputFactory.newInstance();
//        XMLStreamReader reader = factory.createXMLStreamReader(inputStream);//createXmlStreamReader
//        //将xml文件转换成BpmnModel
//        BpmnModel bpmnModel = converter.convertToBpmnModel(reader);
//        //做校验
//        ProcessValidator defaultProcessValidator = processValidatorFactoryExt.createDefaultProcessValidator();
//        List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
//        StringBuffer message = new StringBuffer();
//        if (validate.size()!=0){
//            for (int i =0;i<validate.size();i++){
//                ValidationError validationError = validate.get(i);
//                String problem = validationError.getProblem();
//                message.append(i+1+""+problem);
//                message.append("\r\n");
//            }
//            throw new ApiException(null,message.toString());
//        }
//        //更新并保存
//        flowsInfoMapper.save(flowsInfo);
////        else {
////                //自动部署
////            deployXml(save);
////
////        }
//
//    }

    @Override
    public void flowXml(@RequestBody FlowsInfoVo flowsInfoVo) throws XMLStreamException, IOException {
        Optional<FlowsInfo> byId = flowsInfoMapper.findById(flowsInfoVo.getId());
        if(!byId.isPresent()){
            throw new ApiException("该流程不存在");
        }
        FlowsInfo flowsInfo = byId.get();
        if (flowsInfo.getState()==0){
            throw new ApiException("请先暂停流程再进行编辑");
        }
        String flowKey = flowsInfoVo.getFlowKey();
        String flowXmlResourceName = flowKey + "bpmn20.xml";
        String CustomFlowXmlResourceName = flowKey + "bpmnCustom20.xml";
        //从数据库里查处来
        if (StringUtils.isEmpty(flowsInfo.getFilePath())){
            //生成xml文件
            flowsInfo = flowsInfoVo.toEntity(flowsInfo);
            flowsInfo.setResourceName(flowXmlResourceName);
            flowsInfo.setFilePath(XML_FILE_PATH + flowXmlResourceName);
            flowsInfo.setFileCustomPath(XML_FILE_PATH + CustomFlowXmlResourceName);
        }
        File file = FileUtil.createFileByString(BASE_PATH + XML_FILE_PATH + flowXmlResourceName,
                flowsInfoVo.getFileXml().replaceAll("\\[\\?\\?[^\\]]+\\?\\?\\]", "").replaceAll("&lt;","<").replaceAll("&gt;",">"));
        FileUtil.createFileByString(BASE_PATH + XML_FILE_PATH + CustomFlowXmlResourceName, flowsInfoVo.getFileXml());
        //获取bmpnModel
        BpmnModel bpmnModel = getBpmnModel(file);
        //设置开始节点id
        flowsInfo.setStartPageId(getStartPageId(bpmnModel));
        //进行第一次的校验
        checkXml(bpmnModel);
        //自动部署
        deployXml(flowsInfo.getId());
        //更新并保存
        flowsInfoMapper.save(flowsInfo);
    }

    /**
     * 获取流程开始页面的ID
     * @param bpmnModel
     * @return
     */
    public Integer getStartPageId(BpmnModel bpmnModel){
        Process process = bpmnModel.getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof StartEvent){
                String formKey = ((StartEvent) flowElement).getFormKey();
                if (formKey == null){
                    throw new ApiException("开始节点表单不能为空");
                }else {
                    return Integer.valueOf(formKey);
                }
            }
        }
        return -1;
    }

    /**
     * 获取bpmnModel
     * @param xmlFile
     * @return
     * @throws FileNotFoundException
     * @throws XMLStreamException
     */
    public BpmnModel getBpmnModel(File xmlFile) throws FileNotFoundException, XMLStreamException {
        InputStream inputStream = new FileInputStream(xmlFile);
        BpmnXMLConverter converter = new BpmnXMLConverter();
        XMLInputFactory factory = XMLInputFactory.newInstance();
        //createXmlStreamReader
        XMLStreamReader reader = factory.createXMLStreamReader(inputStream);
        //将xml文件转换成BpmnModel
        return converter.convertToBpmnModel(reader);
    }

    public void checkXml(BpmnModel bpmnModel){
        //做校验
        ProcessValidator defaultProcessValidator = processValidatorFactoryExt.createDefaultProcessValidator();
        List<ValidationError> validate = defaultProcessValidator.validate(bpmnModel);
        StringBuffer message = new StringBuffer();
        if (validate.size()!=0){
            for (int i =0;i<validate.size();i++){
                ValidationError validationError = validate.get(i);
                String problem = validationError.getProblem();
                message.append(i+1+""+problem);
                message.append("\r\n");
            }
            throw new ApiException(null,message.toString());
        }
    }


    @Override
    public Integer createFlow(FlowsInfo flowsInfo) {
        flowsInfo.setCreatedTime(new Date());
        FlowsInfo flowsInfo1 = flowsInfoMapper.save(flowsInfo);
        return flowsInfo1.getId();
    }


    @Override
    public void deployXml(Integer id) {
        FlowsInfo flowsInfo = flowsInfoMapper.findById(id).get();
        Deployment deploy = null;
        try {
            deploy = repositoryService.createDeployment().addInputStream(System.getProperty("user.dir") + flowsInfo.getResourceName(),
                    new FileInputStream(System.getProperty("user.dir") + flowsInfo.getFilePath())).deploy();

        } catch (Exception e) {
            throw new ApiException("流程图不符合规范");
        }
        //将流程自动挂起
        repositoryService.suspendProcessDefinitionByKey(flowsInfo.getFlowKey(), true, new Date());
        //修改状态
        flowsInfo.setState(1);
        flowsInfo.setDeployId(deploy.getId());
        flowsInfoMapper.save(flowsInfo);

    }

    @Override
    public void startFlow(@RequestBody StartFlowVo startFlowVo) {
        //设置流程发起人
        WorkFlowUser currentUser = userService.getCurrentUser();
        Authentication.setAuthenticatedUserId(currentUser.getId() + "");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(startFlowVo.getFlowKey(),startFlowVo.getFlowKey(),
                startFlowVo.getMap());
//        存储流程实例id
//        根据flowKey查询出一个FlowInfo
//        FlowsInfo flowsInfo = flowsInfoMapper.findByFlowKey(startFlowVo.getFlowKey());
        //开始页面的id
//        Integer startPageId = flowsInfo.getStartPageId();
//        flowsInfo.setProcessInstanceId(processInstance.getProcessInstanceId());
//        flowsInfoMapper.save(flowsInfo);
//        return startPageId;
    }


    @Override
    public List<Map<String, Object>> findTaskByUserId(NextTaskVo nextTaskVo) {
        List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
        TaskQuery taskQuery = taskService.createTaskQuery();
        if (nextTaskVo.getRoleId() != null || nextTaskVo.getUserId() != null) {
            taskQuery.or();
            if (nextTaskVo.getUserId() != null) {
                taskQuery.taskCandidateUser(nextTaskVo.getUserId()).orderByTaskCreateTime().desc();
//                taskQuery.taskAssignee(nextTaskVo.getUserId()).orderByTaskCreateTime().desc();
            }
            if (nextTaskVo.getRoleId() != null && nextTaskVo.getRoleId().size() > 0) {
                taskQuery.taskCandidateGroupIn(nextTaskVo.getRoleId()).orderByTaskCreateTime().desc();
            }
            taskQuery.endOr();
        }
        List<Task> listTask = taskQuery.list();
        DataHistory dataHistory = new DataHistory();
        for (Task task : listTask) {
            Map<String, Object> map = new HashMap<>();
            ProcessInstance processInstance =
                    runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            //节点id
            String taskDefinitionKey = task.getTaskDefinitionKey();
            //通过节点id查询出pageId
//            Integer currentPageId = nodeInfoService.findByNodeId(taskDefinitionKey);
            //节点id  formkey
            String formKey = task.getFormKey();
            map.put("taskId", task.getId());
            map.put("processKey", processInstance.getProcessDefinitionKey());
            map.put("taskName", task.getName());
            map.put("taskDesc", task.getDescription());
            map.put("createTime", task.getCreateTime());
            map.put("promoter", processInstance.getStartUserId());
            map.put("processInstanceId", processInstance.getId());
            map.put("currentPageId", formKey);
            listMap.add(map);
        }
        return listMap;

    }

    @Override
    public Map<String, Object> findTaskDetail(String taskId) {
        Map<String, Object> variables = new HashMap<>();
        //流程还在运行
        if (taskId != null) {
            variables = taskService.getVariables(taskId);
        } else {
            //流程已经结束
            //从历史中查询
            HistoricTaskInstance historicTaskInstance =
                    historyService.createHistoricTaskInstanceQuery().taskId(taskId).finished().singleResult();
            variables.put("historicTaskInstance", historicTaskInstance);
        }
        return variables;
    }

    @Override
    public void completeTask(@RequestBody TaskVo taskVo) {

//        Map<String, Object> map = new HashMap<>();
//        if (taskVo.getMap() != null) {
//            map = setVariables(taskVo.getMap());
//        }
        ConcurrentHashMap flowMap = new ConcurrentHashMap<>();
        ConcurrentHashMap oldFlowMap = new ConcurrentHashMap<>();
        ConcurrentHashMap userMap = new ConcurrentHashMap<>();

        Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult();
        if (task==null){
            throw new ApiException(null,"该任务已经被完成了");
        }

        //用户map
        if (taskVo.getMap()!=null){
            userMap.putAll(taskVo.getMap());
        }
        //流程map
        oldFlowMap.putAll(taskService.getVariables(taskVo.getTaskId()));
        flowMap.putAll(taskService.getVariables(taskVo.getTaskId()));
        MapUtils.loopMap(flowMap,"",userMap);
        String processInstanceId = task.getProcessInstanceId();
        Map<String, Object> localMap = new HashMap<>();
        //如果 存在任务评论的话 向任务中添加任务评论
        if (taskVo.getComments() != null) {
            taskService.addComment(task.getId(), processInstanceId, taskVo.getComments());
        }
        //先判断
        if (taskVo.getHandlingOpinion() != null){
            //局部变量
           localMap.put("handlingOpinion",taskVo.getHandlingOpinion());
        }
        try {
            taskService.setVariables(task.getId(),flowMap);
            claimTask(task.getId());
            taskService.complete(task.getId(),localMap);
        } catch (Exception e) {
            e.printStackTrace();
            taskService.removeVariableLocal(task.getId(),"handlingOpinion");
            taskService.removeVariables(task.getId(),taskService.getVariables(task.getId()).keySet());
            taskService.setVariables(task.getId(),oldFlowMap);
            //归还
            taskService.setAssignee(task.getId(),null);
        }


    }

    @Override
    public void claimTask(String taskId) {
        //当前登录人的Id
        taskService.claim(taskId, Integer.toString(userService.getCurrentUser().getId()));
    }

    @Override
    public void transferTask(TransferTask transferTask) {
        taskService.setAssignee(transferTask.getTaskId(), transferTask.getUserId());
    }


    @Override
    public void suspendOrActivateProcessDefinitionByKey(Integer id) {
        //通过流程ID 查询出flowsInfo
        FlowsInfo flowsInfo = flowsInfoMapper.findById(id).get();
        String flowKey = flowsInfo.getFlowKey();
        List<Deployment> list = repositoryService.createDeploymentQuery().processDefinitionKey(flowKey).list();
        if (list != null && list.size() > 0) {
            //部署
            if (flowsInfo.getState() == 0) {
                //挂起
                repositoryService.suspendProcessDefinitionByKey(flowKey, true, new Date());
                flowsInfo.setState(1);
            } else {
                //激活
                repositoryService.activateProcessDefinitionByKey(flowKey, true, new Date());
                flowsInfo.setState(0);
            }
        } else {
            deployXml(id);
        }

        flowsInfoMapper.save(flowsInfo);
    }


    @Override
    public void deleteFlow(Integer id) {
        //通过流程id查询出flowsInfo
        Optional<FlowsInfo> flowsInfoMapperById = flowsInfoMapper.findById(id);
        if (flowsInfoMapperById.isPresent()) {
            FlowsInfo flowsInfo = flowsInfoMapperById.get();
            if (flowsInfo.getDeleted() == 0) {
                flowsInfoMapper.deleteById(id);
            }
        } else {
            throw new ApiException(null,"流程不存在");
        }

    }

    @Override
    public void flowProgress(HttpServletResponse httpServletResponse, String processId) throws IOException {
//
//        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
//        //流程走完的不显示图
//        if (pi == null) {
//            return;
//        }
//        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
//        //使用流程实例ID，查询正在执行的执行对象表，返回流程实例对象
//        String processInstanceId = task.getProcessInstanceId();
//        List<Execution> executions = runtimeService
//                .createExecutionQuery()
//                .processInstanceId(processInstanceId)
//                .list();
//
//        //得到正在执行的Activity的Id
//        List<String> activityIds = new ArrayList<>();
//        List<String> flows = new ArrayList<>();
//        for (Execution exe : executions) {
//            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
//            activityIds.addAll(ids);
//        }
//
//        //获取流程图
//        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
//        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
//        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
//        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0, true);
//        OutputStream out = null;
//        byte[] buf = new byte[1024];
//        int legth = 0;
//        try {
//            out = httpServletResponse.getOutputStream();
//            while ((legth = in.read(buf)) != -1) {
//                out.write(buf, 0, legth);
//            }
//        } finally {
//            if (in != null) {
//                in.close();
//            }
//            if (out != null) {
//                out.close();
//            }
//        }

    }

    @Override
    public List<Object> findHistoryTask() {
//        List<HistoricTaskInstance> taskInstanceList =
//                historyService.createHistoricTaskInstanceQuery().taskCandidateUser(userId).finished().list();
        List<HistoricTaskInstance> taskInstanceList =
                historyService.createHistoricTaskInstanceQuery().taskAssignee(userService.getCurrentUser().getId()+"").orderByTaskCreateTime().desc().list();
        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.addAll(taskInstanceList);
        return arrayList;
    }

    @Override
    public String getCurrentNodeId(String taskId) {
        if (taskId!=null){
            return taskService.createTaskQuery().taskId(taskId).singleResult().getTaskDefinitionKey();
        }else {
            throw new ApiException(null,"任务已经不存在了");
        }

    }

    @Override
    public String getCurrentProcId(String taskId) {

        return null;
    }

    @Override
    public Map<String, Object> setVariables(@RequestBody Map<String, Object> maxMap) {
        Map<String, Object> map = new HashMap<>();
        map.putAll(maxMap);
        return map;
    }


}
