package org.jeecg.modules.activiti.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.ApiOperation;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.VariableInstance;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.utils.ActivitiTracingChart;
import org.jeecg.common.utils.AjaxResult;
import org.jeecg.common.utils.BeanUtils;
import org.jeecg.modules.activiti.model.entity.EntityUtil;
import org.jeecg.modules.activiti.model.entity.FlowInfo;
import org.jeecg.modules.activiti.model.entity.TaskInfo;
import org.jeecg.modules.activiti.model.entity.VariableInfo;
import org.jeecg.modules.customform.entity.CustomForm;
import org.jeecg.modules.customform.service.ICustomFormService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 模型控制
 *
 * @author yao
 * @date 2019/06/13
 */
@RestController
@RequestMapping("/service")
public class ServiceController {
    @Autowired
    ObjectMapper objectMapper;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private IdentityService identityService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    ProcessEngineConfiguration configuration;
    @Autowired
    FormService formService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    private ICustomFormService customFormService;
    @Resource
    private ActivitiTracingChart activitiTracingChart;

    /**
     * stencilset
     *
     * @return stencilset.json
     */
    @RequestMapping(value = "/act/userSyn", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
    public Result<LoginUser> actUserSyn(HttpServletRequest request) {
        LoginUser sysUser = (LoginUser) request.getAttribute("user");
        String selectedRoles = request.getAttribute("selectedRoles").toString();
        String selectedDeparts = request.getAttribute("selectedDeparts").toString();
        Result<LoginUser> result = (Result<LoginUser>) request.getAttribute("result");

        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        IdentityService identityService = engine.getIdentityService();
        User user = identityService.newUser(sysUser.getId());
        user.setFirstName(sysUser.getUsername());
        user.setLastName(sysUser.getRealname());
        user.setEmail(sysUser.getEmail());
        user.setPassword(sysUser.getPassword());
        identityService.saveUser(user);


        return result;
    }

    /**
     * 获取角色信息
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/act/role/getRoleList", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
    public List<Group> getRoleList(HttpServletRequest request) {
        List<Group> result = new ArrayList<>();
        result = identityService.createGroupQuery().list();
        return result;
    }

    /**
     * 获取用户信息
     *
     * @param request
     * @param page
     * @param limit
     * @param keyword
     * @return
     */
    @RequestMapping(value = "/act/user/actUserList/{page}/{limit}", method = RequestMethod.GET,
            produces = "application/json;charset=utf-8")
    public Result actUserList(HttpServletRequest request, @PathVariable("page") int page,
                              @PathVariable("limit") int limit,
                              @RequestParam String keyword) {
        List<User> list = new ArrayList<>();
        Integer total = 0;
        if (null != keyword && !keyword.equals("")) {
            list = identityService.createNativeUserQuery().sql("select id_,rev_ ,first_,last_,email_,pwd_ FROM act_id_user where first_ like #{first_}")
                    .parameter("first_", "%" + keyword + "%").listPage((page - 1) * limit, limit);
            total = identityService.createNativeUserQuery().sql("select id_,rev_ ,first_,last_,email_,pwd_ FROM act_id_user where first_ like #{first_}")
                    .parameter("first_", "%" + keyword + "%").list().size();

        } else {
            //查询所有角色
            list = identityService.createNativeUserQuery().sql("select id_,rev_ ,first_,last_,email_,pwd_ FROM act_id_user ").listPage((page - 1) * limit, limit);
            total = identityService.createNativeUserQuery().sql("select id_,rev_ ,first_,last_,email_,pwd_ FROM act_id_user ").list().size();


        }
        return Result.OKK(list, total);
    }

    /**
     * 获取流程信息
     *
     * @return
     */
    @RequestMapping(value = "/act/rep/actRepList", method = RequestMethod.GET,
            produces = "application/json;charset=utf-8")
    public Object actRepList() {
        List<ProcessDefinition> processDefinitions = repositoryService.createNativeProcessDefinitionQuery()
                .sql("SELECT * FROM act_re_procdef WHERE suspension_state_ = 1 AND deployment_id_ in (SELECT deployment_id_ FROM `act_re_model`)").list();
        List<Map<String, Object>> mapList = EntityUtil.activitiResult(processDefinitions);
        return Result.OK(mapList);
    }

    /**
     * 通过模型id 查询流程图片
     *
     * @param pdid
     * @param response
     * @throws Exception
     */
    @ApiOperation("查看工作流图片")
    @RequestMapping(value = "/act/showResource", method = RequestMethod.GET)
    public void showResource(@RequestParam("pdid") String pdid,
                             HttpServletResponse response) throws Exception {
        System.out.println("查看工作流图片" + pdid);
        Model model = repositoryService.createModelQuery().modelId(pdid).singleResult();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(model.getDeploymentId()).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
        InputStream is = diagramGenerator.generateDiagram(bpmnModel, "png", "宋体", "宋体", "宋体",
                configuration.getClassLoader(), 1.0);
        ServletOutputStream output = response.getOutputStream();
        IOUtils.copy(is, output);
    }

    /**
     * 直接通过 流程定义id 查询流程图片
     *
     * @param pdid
     * @param response
     * @throws Exception
     */
    @ApiOperation("查看工作流图片")
    @RequestMapping(value = "/act/showResourceForProc", method = RequestMethod.GET)
    public void showResourceForProc(@RequestParam("pdid") String pdid,
                                    HttpServletResponse response) throws Exception {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pdid);
        ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
        InputStream is = diagramGenerator.generateDiagram(bpmnModel, "png", "宋体", "宋体", "宋体",
                configuration.getClassLoader(), 1.0);
        ServletOutputStream output = response.getOutputStream();
        IOUtils.copy(is, output);
    }

    /**
     * 直接通过 流程定义id 查询流程图片
     *
     * @param pdid
     * @param response
     * @throws Exception
     */
    @ApiOperation("发起流程前返回自定义表单")
    @RequestMapping(value = "/act/beforeStartProc", method = RequestMethod.GET)
    public Result beforeStartProc(@RequestParam("pdid") String pdid,
                                  HttpServletResponse response) throws Exception {
        // 根据流程定义id获取启动节点的formKey，也就是获取自定义表单值,启动节点就是那个流程图的第一个圈
        String formKey = formService.getStartFormKey(pdid);
        // 根据任务id获取任务id的formKey,也就是获取任务节点的自定义表单值
        //formService.getTaskFormData(taskId).getFormKey();
        CustomForm customForm = customFormService.getById(formKey);
        return Result.OK("", customForm.getFormcontent());
    }

    @ApiOperation("发起流程")
    @RequestMapping(value = "/act/startProc", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
    public Result startProc(@RequestParam("procKey") String procKey,
                            @RequestParam("businessKey") String businessKey,
                            @RequestParam("formData") String formData, HttpServletResponse response) throws Exception {
        // 设置申请人,将之保存在流程变量中
        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        Map<String, Object> variables = new HashMap<>();
        //variables.put("applyuserid", user.getId());
        variables.put("userid", user.getId());
        variables.put("applyUser", user.getId());
        variables.put("formData", formData);
        //  String[] person ={user.getId()};
        //  variables.put("people",Arrays.asList(person));
        Authentication.setAuthenticatedUserId(String.valueOf(user.getId()));
        ProcessInstance processInstance = null;
        try {
            processInstance = runtimeService.startProcessInstanceByKeyAndTenantId(procKey, businessKey, variables, "0");
        } catch (Exception e) {
            processInstance = runtimeService.startProcessInstanceByKey(procKey, businessKey, variables);
        }

        //
        Task autoTask = taskService.createTaskQuery()
                .processDefinitionKey(procKey).processInstanceId(processInstance.getId()).singleResult();
        autoTask.setOwner(user.getId());
        taskService.saveTask(autoTask);


//        taskService.complete(autoTask.getId());
        return Result.OK("请求成功");
    }

    @ApiOperation("查询我的待办任务列表")
    @GetMapping("/act/mylist")
    @ResponseBody
    public Result mylist(@RequestParam("pageNo") int pageNo, @RequestParam("pageSize") int pageSize, @RequestParam(value = "key", required = false) String key) {
        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        String userId = user.getId();
        TaskQuery condition = taskService.createTaskQuery().taskAssignee(userId);

        // 过滤掉流程挂起的待办任务
        int total = condition.active().orderByTaskCreateTime().desc().list().size();
        List<Task> taskList ;
        if (StringUtils.isNotEmpty(key)) {
            taskList = condition.active().orderByTaskCreateTime().processInstanceId(key).desc().listPage((pageNo - 1) * pageSize, pageSize);

        } else {
            taskList = condition.active().orderByTaskCreateTime().desc().listPage((pageNo - 1) * pageSize, pageSize);

        }
        List<TaskInfo> tasks = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        taskList.stream().forEach(a -> {
            ProcessInstance process = runtimeService.createProcessInstanceQuery().processInstanceId(a.getProcessInstanceId()).singleResult();
            TaskInfo info = new TaskInfo();
            info.setAssignee(identityService.createUserQuery().userId(a.getAssignee()).singleResult().getFirstName());
            info.setBusinessKey(process.getBusinessKey());
            info.setCreateTime(sdf.format(a.getCreateTime()));
            info.setTaskName(a.getName());
            info.setExecutionId(a.getExecutionId());
            info.setProcessInstanceId(a.getProcessInstanceId());
            info.setProcessName(process.getProcessDefinitionName());
            info.setStarter(process.getStartUserId() == null ? "0" : identityService.createUserQuery().userId(process.getStartUserId()).singleResult().getFirstName());
            info.setStartTime(sdf.format(process.getStartTime()));
            info.setTaskId(a.getId());
            String formKey = formService.getTaskFormData(a.getId()).getFormKey();
            info.setFormKey(formKey);
            tasks.add(info);
        });
        Result.OKK(tasks, total);
        return Result.OKK(tasks, total);
    }

    @ApiOperation("流程图进度追踪")
    @RequestMapping(value = {"/traceProcess/{processInstanceId}"}, method = RequestMethod.GET)
    public void traceprocess(@PathVariable String processInstanceId, HttpServletResponse response) throws IOException {
        activitiTracingChart.generateFlowChart(processInstanceId, response.getOutputStream());
    }

    @ApiOperation("办理一个用户任务")
    @RequestMapping(value = "/getTaskInfo/{executionId}/{taskId}", method = RequestMethod.POST)
    @ResponseBody
    public Result completeTask(@PathVariable("executionId") String executionId, @PathVariable("taskId") String taskId) {
        VariableInstance variableInstance = runtimeService.getVariableInstance(executionId, "formData");

        List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(executionId)
                .variableName("formData").list();
        List<VariableInfo> infos = new ArrayList<>();
        variableInstances.forEach(v -> {
            VariableInfo info = new VariableInfo();
            BeanUtils.copyBeanProp(info, v);
            info.setValue(v.getValue().toString());
            infos.add(info);
        });
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 根据任务id获取任务id的formKey,也就是获取任务节点的自定义表单值
        //formService.getTaskFormData(taskId).getFormKey();
        CustomForm customForm = customFormService.getById(task.getFormKey());
        Map<String, Object> map = new HashMap<>();
        map.put("fromData1", infos);
        map.put("fromData2", customForm.getFormcontent());
        return Result.OK("查询成功", map);
    }

    @ApiOperation("提交一个用户任务")
    @RequestMapping(value = "/completeTask/{taskId}", method = RequestMethod.POST)
    @ResponseBody
    public AjaxResult completeTask(@PathVariable("taskId") String taskId,
                                   @RequestBody(required = false) String comment,
                                   @RequestBody(required = false) String formData) {
        LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        String username = user.getId();
        taskService.setAssignee(taskId, username);
        Map<String, Object> variables = new HashMap<>();
        variables.put("formData", formData);
        // 查出流程实例id
        String processInstanceId = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
        if (comment == null || comment.equals("")) {
            taskService.complete(taskId, variables);
        } else {
            // 添加审批意见
            if (comment != null && !comment.equals("")) {
                taskService.addComment(taskId, processInstanceId, comment);
            }
            taskService.complete(taskId, variables);
        }
        return AjaxResult.success();
    }

    /**
     * stencilset
     *
     * @return stencilset.json
     */
    @RequestMapping(value = "/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
    public String getStencilset() {
        InputStream stencilsetStream = this.getClass().getResourceAsStream("/static/stencilset.json");
        try {
            return IOUtils.toString(stencilsetStream, "utf-8");
        } catch (Exception e) {
            throw new ActivitiException("Error while loading stencil set", e);
        }
    }


    /**
     * 模型详情
     *
     * @param modelId
     * @return
     */
    @RequestMapping(value = "/model/{modelId}/json", method = RequestMethod.GET)
    public ObjectNode getEditorJson(@PathVariable String modelId) {
        ObjectNode modelNode = null;

        Model model = repositoryService.getModel(modelId);

        if (model != null) {
            try {
                if (StringUtils.isNotEmpty(model.getMetaInfo())) {
                    modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
                } else {
                    modelNode = objectMapper.createObjectNode();
                    modelNode.put("name", model.getName());
                }
                modelNode.put("modelId", model.getId());
                ObjectNode editorJsonNode = (ObjectNode) objectMapper
                        .readTree(new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
                modelNode.set("model", editorJsonNode);

            } catch (Exception e) {
                throw new ActivitiException("Error creating model JSON", e);
            }
        }
        return modelNode;
    }

    /**
     * 保存
     *
     * @param modelId
     * @param name
     * @param jsonXml
     * @param svgXml
     * @param description
     */
    @RequestMapping(value = "/model/{modelId}/save", method = RequestMethod.PUT)
    public void saveModel(@PathVariable String modelId, @RequestParam("name") String name,
                          @RequestParam("json_xml") String jsonXml, @RequestParam("svg_xml") String svgXml,
                          @RequestParam("description") String description, HttpServletRequest req) {
        try {
            String tenantId = req.getHeader("tenant-id");
            Model model = repositoryService.getModel(modelId);
            ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
            modelJson.put("name", name);
            modelJson.put("description", description);
            model.setMetaInfo(modelJson.toString());
            model.setName(name);
            model.setVersion(model.getVersion() + 1);
            model.setTenantId(tenantId);
            repositoryService.saveModel(model);
            repositoryService.addModelEditorSource(model.getId(), jsonXml.getBytes(StandardCharsets.UTF_8));

            InputStream svgStream = new ByteArrayInputStream(svgXml.getBytes(StandardCharsets.UTF_8));
            TranscoderInput input = new TranscoderInput(svgStream);
            PNGTranscoder transcoder = new PNGTranscoder();
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            TranscoderOutput output = new TranscoderOutput(outStream);
            transcoder.transcode(input, output);
            final byte[] result = outStream.toByteArray();
            repositoryService.addModelEditorSourceExtra(model.getId(), result);
            outStream.close();
        } catch (Exception e) {
            throw new ActivitiException("Error saving model", e);
        }
    }

    @ApiOperation("查询所有流程实例列表-包含在运行和已结束")
    @RequestMapping(value = "/listHistoryProcess", method = RequestMethod.GET)
    @ResponseBody
    public Result listHistoryProcess(@RequestParam(required = false) String bussinesskey, @RequestParam(required = false) String name,
                                     @RequestParam(required = false) String processInstanceId,
                                     Integer pageSize, Integer pageNo) {
        int start = (pageNo - 1) * pageSize;
        HistoricProcessInstanceQuery condition = historyService.createHistoricProcessInstanceQuery();
        // historyService.createHistoricActivityInstanceQuery();   //历史活动
        // historyService.createHistoricTaskInstanceQuery();       //历史任务
        // historyService.createHistoricVariableInstanceQuery();   //历史变量
        if (StringUtils.isNotEmpty(bussinesskey)) {
            condition.processInstanceBusinessKey(bussinesskey);
        }
        if (StringUtils.isNotEmpty(name)) {
            condition.processDefinitionName(name);
        }
        if (StringUtils.isNotEmpty(processInstanceId)) {
            condition.processInstanceId(processInstanceId);
        }
        int total = condition.orderByProcessInstanceStartTime().desc().list().size();
        List<HistoricProcessInstance> processList = condition.orderByProcessInstanceStartTime().desc().listPage(start, pageSize);
        List<FlowInfo> flows = new ArrayList<>();
        processList.stream().forEach(p -> {
            FlowInfo info = new FlowInfo();
            info.setProcessInstanceId(p.getId());
            info.setBusinessKey(p.getBusinessKey());
            info.setName(p.getProcessDefinitionName());
            info.setStartTime(p.getStartTime());
            info.setEndTime(p.getEndTime());
            info.setStartUserId(p.getStartUserId());
            if (StringUtils.isNotEmpty(p.getStartUserId()) && (!p.getStartUserId().equals("")) && (!p.getStartUserId().equals("null"))) {
                User user1 = identityService.createUserQuery().userId(p.getStartUserId()).singleResult();
                info.setStartUserId(user1.getFirstName());
            }
            if (p.getEndTime() == null) {
                info.setEnded(false);
                // 查看当前活动任务
                List<Task> tasks = taskService.createTaskQuery().processInstanceId(p.getId()).list();
                if (tasks.size() > 0) {
                    String taskName = "";
                    String assignee = "";
                    for (Task t : tasks) {
                        taskName += t.getName() + ",";
                        assignee += t.getAssignee() + ",";
                    }
                    taskName = taskName.substring(0, taskName.length() - 1);
                    assignee = assignee.substring(0, assignee.length() - 1);
                    info.setCurrentTask(taskName);
                    info.setAssignee(assignee);
                    if (StringUtils.isNotEmpty(assignee) && !assignee.equals("") && !assignee.equals("null")) {
                        User user = identityService.createUserQuery().userId(assignee).singleResult();
                        info.setAssignee(user.getFirstName());
                    }
                }
            } else {
                info.setEnded(true);
            }
            flows.add(info);
        });

        Page p = new Page();
        p.setRecords(flows);
        p.setTotal(total);
        p.setSize(pageSize);
        p.setCurrent(pageNo);
        p.setOrders(new ArrayList<>());
        p.setOptimizeCountSql(true);
        p.setSearchCount(true);
        return Result.OK(p);

    }
}
