package org.springblade.flow.engine.controller;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.io.IOUtils;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.ProcessDefinition;
import org.springblade.flow.base.ActionResult;
import org.springblade.flow.base.util.FormCloumnUtil;
import org.springblade.flow.base.vo.DownloadVO;
import org.springblade.flow.base.vo.ListVO;
import org.springblade.flow.config.ConfigValueUtil;
import org.springblade.flow.database.exception.DataException;
import org.springblade.flow.engine.custom.constants.BasicActivitiConstants;
import org.springblade.flow.engine.entity.FlowEngineEntity;
import org.springblade.flow.engine.entity.FlowTaskEntity;
import org.springblade.flow.engine.enums.FlowTaskOperatorEnum;
import org.springblade.flow.engine.enums.FlowTaskStatusEnum;
import org.springblade.flow.engine.model.flowengine.FlowEngineCrForm;
import org.springblade.flow.engine.model.flowengine.FlowEngineInfoVO;
import org.springblade.flow.engine.model.flowengine.FlowEngineListVO;
import org.springblade.flow.engine.model.flowengine.FlowEngineModel;
import org.springblade.flow.engine.model.flowengine.FlowEngineSelectVO;
import org.springblade.flow.engine.model.flowengine.FlowEngineUpForm;
import org.springblade.flow.engine.model.flowengine.FlowExportModel;
import org.springblade.flow.engine.model.flowengine.PaginationFlowEngine;
import org.springblade.flow.engine.model.flowengine.shuntjson.childnode.ChildNode;
import org.springblade.flow.engine.service.FlowEngineService;
import org.springblade.flow.engine.service.FlowTaskService;
import org.springblade.flow.exception.WorkFlowException;
import org.springblade.flow.model.FormAllModel;
import org.springblade.flow.model.FormEnum;
import org.springblade.flow.model.visiual.FormDataField;
import org.springblade.flow.model.visiual.FormDataModel;
import org.springblade.flow.model.visiual.JnpfKeyConsts;
import org.springblade.flow.model.visiual.fields.FieldsModel;
import org.springblade.flow.permission.service.User4FlowService;
import org.springblade.flow.util.JsonUtil;
import org.springblade.flow.util.JsonUtilEx;
import org.springblade.flow.util.StringUtil;
import org.springblade.flow.util.file.fileinfo.DataFileExport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import cn.hutool.core.codec.Base64;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

/**
 * 流程设计
 *
 * @author JNPF开发平台组
 * @version V3.1.0
 * @copyright 引迈信息技术有限公司
 * @date 2019年9月27日 上午9:18
 */
@Api(tags = "流程引擎", value = "FlowEngine")
@RestController
@RequestMapping("/paas/workflow/Engine/FlowEngine")
@Slf4j
public class FlowEngineController {

	@Autowired
	private User4FlowService userService;

	@Autowired
	private FlowEngineService flowEngineService;
	@Autowired
	private FlowTaskService flowTaskService;
	@Autowired
	private DataFileExport fileExport;
	@Autowired
	private ConfigValueUtil configValueUtil;
	@Resource
	private RepositoryService repositoryService;
	@Resource
	private RuntimeService runtimeService;

	/////////////////////////////////////// 兼容activiti
	/////////////////////////////////////// 新增接口--start////////////////////////////////////////////////
	@ApiOperation("获取当前在activiti中的流程图png/bpmn文件 返回Base64")
	@GetMapping("/getProcessPic")
	public ActionResult<Object> getProcessPic(@RequestParam("enCode") String enCode,
			@RequestParam("type") String type) {
		InputStream resourceAsStream = null;
		try {
			String processDefinitionKey = BasicActivitiConstants.activitiPrefix + enCode;
			ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
					.processDefinitionKey(processDefinitionKey).latestVersion().singleResult();

			// 代表是bpmn文件
			if ("1".equals(type)) {
				resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
						processDefinition.getResourceName());
			}
			// 流程图片
			else {
				resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
						processDefinition.getDiagramResourceName());
			}
			String encode = Base64.encode(resourceAsStream);
			return ActionResult.success("查询成功", encode);
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("查询失败");
		} finally {
			try {
				resourceAsStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@ApiOperation("挂起或者激活 此流程")
	@GetMapping("/activateOrSuspendProcess")
	public ActionResult<Object> activateOrSuspendProcess(@RequestParam("enCode") String enCode,
			@RequestParam("state") Integer state) {
		String processDefinitionKey = BasicActivitiConstants.activitiPrefix + enCode;
		try {
			// 状态是激活 需要挂起
			if (1 == state) {
				repositoryService.suspendProcessDefinitionByKey(processDefinitionKey, true, null);
			} else {
				repositoryService.activateProcessDefinitionByKey(processDefinitionKey, true, null);
			}
			return ActionResult.success("操作成功");
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("操作失败");
		}
	}

	@ApiOperation("删除流程信息 通过部署id")
	@GetMapping("/deleteProcess")
	public ActionResult<Object> deleteProcess(@RequestParam("deploymentId") String deploymentId) {
		try {
			repositoryService.deleteDeployment(deploymentId, true);
			return ActionResult.success("删除成功");
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("删除失败");
		}
	}

	/**
	 * 从浏览器下载 xml 或者png
	 *
	 * @param deploymentId 流程部署的ID
	 * @param resourceName 资源名称
	 * @param response
	 * @throws Exception
	 */
	@GetMapping(value = "/downProcessXmlOrPic")
	public void downProcessXmlOrPic(@RequestParam("deploymentId") String deploymentId,
			@RequestParam("resourceName") String resourceName, HttpServletResponse response) {
		InputStream resourceAsStream = null;
		try {
			resourceAsStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
			// 设置response的Header
			response.setContentType("application/octet-stream");
			response.setHeader("Content-disposition",
					"attachment;filename=" + new String(resourceName.getBytes("utf-8"), "ISO8859-1"));
			IOUtils.copy(resourceAsStream, response.getOutputStream());
			response.flushBuffer();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (resourceAsStream != null) {
					resourceAsStream.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	@GetMapping("/processList")
	public ActionResult<Object> processList() {
		try {

			return ActionResult.success();
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("");
		}

	}

	/////////////////////////////////////// 兼容activiti
	/////////////////////////////////////// 新增接口--end////////////////////////////////////////////////

	/**
	 * 获取流程设计列表 (和工作流不产生关系 无需修改)
	 *
	 * @return
	 */
	@ApiOperation("获取流程引擎列表")
	@GetMapping
	public ActionResult<ListVO<FlowEngineListVO>> list(PaginationFlowEngine pagination) {
		List<FlowEngineListVO> treeList = flowEngineService.getTreeList(pagination, true, userService.getList());
		ListVO<FlowEngineListVO> vo = new ListVO<>();
		vo.setList(treeList);
		return ActionResult.success(vo);
	}

	/**
	 * 获取流程设计列表 (和工作流不产生关系 无需修改)
	 *
	 * @return
	 */
	@ApiOperation("流程引擎下拉框")
	@GetMapping("/Selector")
	public ActionResult<ListVO<FlowEngineListVO>> listSelect(String type) {
		PaginationFlowEngine pagination = new PaginationFlowEngine();
		pagination.setFormType(type);
		pagination.setEnabledMark("1");
		List<FlowEngineListVO> treeList = flowEngineService.getTreeList(pagination, true, userService.getList());
		ListVO<FlowEngineListVO> vo = new ListVO<>();
		vo.setList(treeList);
		return ActionResult.success(vo);
	}

	/**
	 * 主表属性 (和工作流不产生关系 无需修改)
	 *
	 * @return
	 */
	@ApiOperation("表单主表属性")
	@GetMapping("/{id}/FormDataFields")
	public ActionResult<ListVO<FormDataField>> getFormDataField(@PathVariable("id") String id)
			throws WorkFlowException {
		FlowEngineEntity entity = flowEngineService.getInfo(id);
		List<FormDataField> formDataFieldList = new ArrayList<>();
		if (entity.getFormType() == 1) {
			List<FlowEngineModel> list = JsonUtil.getJsonToList(entity.getFormData(), FlowEngineModel.class);
			for (FlowEngineModel model : list) {
				FormDataField formDataField = new FormDataField();
				formDataField.setLabel(model.getFiledName());
				formDataField.setVModel(model.getFiledId());
				formDataFieldList.add(formDataField);
			}
		} else {
			// formTempJson
			FormDataModel formData = JsonUtil.getJsonToBean(entity.getFormData(), FormDataModel.class);
			List<FieldsModel> list = JsonUtil.getJsonToList(formData.getFields(), FieldsModel.class);
			List<FormAllModel> formAllModel = new ArrayList<>();
			FormCloumnUtil.recursionForm(list, formAllModel);
			// 主表数据
			List<FormAllModel> mast = formAllModel.stream()
					.filter(t -> FormEnum.mast.getMessage().equals(t.getJnpfKey())).collect(Collectors.toList());
			for (FormAllModel model : mast) {
				FieldsModel fieLdsModel = model.getFormColumnModel().getFieLdsModel();
				String vmodel = fieLdsModel.getVModel();
				String jnpfKey = fieLdsModel.getConfig().getJnpfKey();
				if (StringUtil.isNotEmpty(vmodel) && !JnpfKeyConsts.RELATIONFORM.equals(jnpfKey)
						&& !JnpfKeyConsts.RELATIONFLOW.equals(jnpfKey)) {
					FormDataField formDataField = new FormDataField();
					formDataField.setLabel(fieLdsModel.getConfig().getLabel());
					formDataField.setVModel(fieLdsModel.getVModel());
					formDataFieldList.add(formDataField);
				}
			}
		}
		ListVO<FormDataField> listVO = new ListVO<>();
		listVO.setList(formDataFieldList);
		return ActionResult.success(listVO);
	}

	/**
	 * 列表(和工作流不产生关系 无需修改)
	 *
	 * @return
	 */
	@ApiOperation("表单列表")
	@GetMapping("/{id}/FieldDataSelect")
	public ActionResult<ListVO<FlowEngineSelectVO>> getFormData(@PathVariable("id") String id) {
		List<FlowTaskEntity> flowTaskList = flowTaskService.getTaskList(id).stream()
				.filter(t -> FlowTaskStatusEnum.Adopt.getCode().equals(t.getStatus())).collect(Collectors.toList());
		List<FlowEngineSelectVO> vo = new ArrayList<>();
		for (FlowTaskEntity taskEntity : flowTaskList) {
			FlowEngineSelectVO selectVO = JsonUtil.getJsonToBean(taskEntity, FlowEngineSelectVO.class);
			selectVO.setFullName(taskEntity.getFullName() + "/" + taskEntity.getEnCode());
			vo.add(selectVO);
		}
		ListVO<FlowEngineSelectVO> listVO = new ListVO<>();
		listVO.setList(vo);
		return ActionResult.success(listVO);
	}

	/**
	 * 列表 (和工作流不产生关系 无需修改)
	 *
	 * @return
	 */
	@ApiOperation("获取可见流程引擎列表")
	@GetMapping("/ListAll")
	public ActionResult<ListVO<FlowEngineListVO>> listAll() {
		PaginationFlowEngine pagination = new PaginationFlowEngine();
		List<FlowEngineListVO> treeList = flowEngineService.getTreeList(pagination, false, userService.getList());
		ListVO<FlowEngineListVO> vo = new ListVO<>();
		vo.setList(treeList);
		return ActionResult.success(vo);
	}

	/**
	 * 获取流程引擎信息 (和工作流不产生关系 无需修改)
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("获取流程引擎信息")
	@GetMapping("/{id}")
	public ActionResult<FlowEngineInfoVO> info(@PathVariable("id") String id) throws DataException, WorkFlowException {
		FlowEngineEntity flowEntity = flowEngineService.getInfo(id);
		FlowEngineInfoVO vo = JsonUtilEx.getJsonToBeanEx(flowEntity, FlowEngineInfoVO.class);
		ChildNode childNode = JsonUtil.getJsonToBean(flowEntity.getFlowTemplateJson(), ChildNode.class);
		int freeApprover = 0;
		if (childNode.getChildNode() != null) {
			String type = childNode.getChildNode().getProperties().getAssigneeType();
			if (String.valueOf(FlowTaskOperatorEnum.FreeApprover.getCode()).equals(type)) {
				freeApprover = 1;
			}
		}
		vo.setFreeApprover(freeApprover);
		return ActionResult.success(vo);
	}

	/**
	 * 新建流程设计
	 *
	 * @return
	 */
	@ApiOperation("新建流程引擎")
	@PostMapping
	public ActionResult<Object> create(@RequestBody @Valid FlowEngineCrForm flowEngineCrForm) {
		try {
			FlowEngineEntity flowEngineEntity = JsonUtil.getJsonToBean(flowEngineCrForm, FlowEngineEntity.class);
			if (flowEngineService.isExistByFullName(flowEngineEntity.getFullName(), flowEngineEntity.getId())) {
				return ActionResult.fail("流程名称不能重复");
			}
			if (flowEngineService.isExistByEnCode(flowEngineEntity.getEnCode(), flowEngineEntity.getId())) {
				return ActionResult.fail("流程编码不能重复");
			}
			flowEngineService.create(flowEngineEntity);
			return ActionResult.success("新建成功");
		} catch (Exception e) {
			log.error("新建流程失败 {}", e.getMessage());
			if (e.getMessage().contains("不是 'NCName' 的有效值")) {
				return ActionResult.fail("由于前端流程设计器节点id策略改变,需要重新绘制流程图");
			}
			return ActionResult.fail("新建流程失败");
		}
	}

	/**
	 * 更新流程设计
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("更新流程引擎")
	@PutMapping("/{id}")
	public ActionResult<Object> update(@PathVariable("id") String id,
			@RequestBody @Valid FlowEngineUpForm flowEngineUpForm) {
		try {
			FlowEngineEntity flowEngineEntity = JsonUtil.getJsonToBean(flowEngineUpForm, FlowEngineEntity.class);
			if (flowEngineService.isExistByFullName(flowEngineUpForm.getFullName(), id)) {
				return ActionResult.fail("流程名称不能重复");
			}
			if (flowEngineService.isExistByEnCode(flowEngineUpForm.getEnCode(), id)) {
				return ActionResult.fail("流程编码不能重复");
			}
			boolean flag = flowEngineService.updateVisible(id, flowEngineEntity);
			if (flag == false) {
				return ActionResult.fail("更新失败，数据不存在");
			}
			return ActionResult.success("更新成功");
		} catch (Exception e) {
			log.error("更新流程引擎失败:" + e.getMessage());
			if (e.getMessage().contains("不是 'NCName' 的有效值")) {
				return ActionResult.fail("由于前端流程设计器节点id策略改变,需要重新绘制流程图");
			}
			return ActionResult.fail("更新流程失败");
		}
	}

	/**
	 * 删除流程设计
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("删除流程引擎")
	@DeleteMapping("/{id}")
	public ActionResult<String> delete(@PathVariable("id") String id) throws WorkFlowException {
		FlowEngineEntity entity = flowEngineService.getInfo(id);
		String enCode = entity.getEnCode();
		String processDefinitionKey = BasicActivitiConstants.activitiPrefix + enCode;
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
				.processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
		long count = runtimeService.createProcessInstanceQuery().processDefinitionKey(processDefinitionKey).count();
		if (count > 0) {
			return ActionResult.fail("流程正在使用中,不可以删除");
		}

		flowEngineService.delete(entity);
		repositoryService.deleteDeployment(processDefinition.getDeploymentId(), true);
		return ActionResult.success("删除成功");
	}

	/**
	 * 复制流程表单
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("复制流程表单")
	@PostMapping("/{id}/Actions/Copy")
	public ActionResult<Object> copy(@PathVariable("id") String id) throws WorkFlowException {
		try {
			FlowEngineEntity flowEngineEntity = flowEngineService.getInfo(id);
			if (flowEngineEntity != null) {
				long time = System.currentTimeMillis();
				flowEngineEntity.setFullName(flowEngineEntity.getFullName() + "_" + time);
				flowEngineEntity.setEnCode(flowEngineEntity.getEnCode() + "_" + time);
				flowEngineEntity.setCreatorTime(new Date());
				flowEngineEntity.setId(null);
				flowEngineService.create(flowEngineEntity);
				return ActionResult.success("复制成功");
			}
			return ActionResult.fail("复制失败");
		} catch (Exception e) {
			log.error("复制失败 {}", e.getMessage());
			if (e.getMessage().contains("不是 'NCName' 的有效值")) {
				return ActionResult.fail("由于前端流程设计器节点id策略改变,需要重新绘制流程图");
			}
			return ActionResult.fail("复制失败");
		}
	}

	/**
	 * 流程表单状态(仅仅更改状态 和工作流不产生关系 无需修改)
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("更新流程表单状态")
	@PutMapping("/{id}/Actions/State")
	public ActionResult<Object> state(@PathVariable("id") String id) throws WorkFlowException {
		FlowEngineEntity entity = flowEngineService.getInfo(id);
		if (entity != null) {
			entity.setEnabledMark("1".equals(String.valueOf(entity.getEnabledMark())) ? 0 : 1);
			flowEngineService.update(id, entity);
			return ActionResult.success("更新表单成功");
		}
		return ActionResult.fail("更新失败，数据不存在");
	}

	/**
	 * 发布流程引擎 工作流激活挂起
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("发布流程设计")
	@PostMapping("/Release/{id}")
	public ActionResult<Object> release(@PathVariable("id") String id) throws WorkFlowException {
		FlowEngineEntity entity = flowEngineService.getInfo(id);
		if (entity != null) {
			entity.setEnabledMark(1);
			String enCode = entity.getEnCode();
			String processDefinitionKey = BasicActivitiConstants.activitiPrefix + enCode;
			ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
					.processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
			String processDefinitionId = processDefinition.getId();
			boolean processDefinitionSuspended = repositoryService.isProcessDefinitionSuspended(processDefinitionId);
			if (processDefinitionSuspended) {
				repositoryService.activateProcessDefinitionById(processDefinitionId);
			}
			flowEngineService.update(id, entity);

			return ActionResult.success("发布成功");
		}
		return ActionResult.fail("发布失败，数据不存在");
	}

	/**
	 * 停止流程引擎  工作流激活挂起
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("停止流程设计")
	@PostMapping("/Stop/{id}")
	public ActionResult<Object> stop(@PathVariable("id") String id) throws WorkFlowException {
		FlowEngineEntity entity = flowEngineService.getInfo(id);
		if (entity != null) {
			entity.setEnabledMark(0);
			String enCode = entity.getEnCode();
			String processDefinitionKey = BasicActivitiConstants.activitiPrefix + enCode;
			ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
					.processDefinitionKey(processDefinitionKey).latestVersion().singleResult();
			String processDefinitionId = processDefinition.getId();
			boolean processDefinitionSuspended = repositoryService.isProcessDefinitionSuspended(processDefinitionId);
			if (!processDefinitionSuspended) {
				repositoryService.suspendProcessDefinitionById(processDefinitionId);
			}
			flowEngineService.update(id, entity);
			return ActionResult.success("停止成功");
		}
		return ActionResult.fail("停止失败，数据不存在");
	}

	/**
	 * 工作流导出
	 * @param id 主键值
	 * @return
	 * @throws WorkFlowException
	 */
	@ApiOperation("工作流导出")
	@GetMapping("/{id}/Actions/ExportData")
	public ActionResult<Object> exportData(@PathVariable("id") String id) throws WorkFlowException {
		FlowExportModel model = flowEngineService.exportData(id);
		DownloadVO downloadVO = fileExport.exportFile(model, configValueUtil.getTemporaryFilePath());
		return ActionResult.success(downloadVO);
	}

	/**
	 * 工作流导入
	 * @param multipartFile 文件
	 * @return
	 * @throws WorkFlowException
	 */
	@ApiOperation("工作流导入")
	@PostMapping(value = "/Actions/ImportData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	public ActionResult<Object> ImportData(@RequestPart("file") MultipartFile multipartFile) throws WorkFlowException {
		return ActionResult.fail("工作流导入功能暂不考虑支持~");
//            //判断是否为.json结尾
//            if (FileUtil.existsSuffix(multipartFile)) {
//                return ActionResult.fail("导入文件格式错误");
//            }
//            //获取文件内容
//            String fileContent = FileUtil.getFileContent(multipartFile, configValueUtil.getTemporaryFilePath());
//            FlowExportModel vo = JsonUtil.getJsonToBean(fileContent, FlowExportModel.class);
//            return flowEngineService.ImportData(vo.getFlowEngine(), vo.getVisibleList());
	}

}
