package org.springblade.flow.engine.controller;

import static org.springblade.flow.base.util.BpmnModelUtils.getAttributeValueByTaskDefinitionKey;
import static org.springblade.flow.base.util.BpmnModelUtils.getBpmnModelMainProcess;
import static org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants.BUSINESS_TITLE;
import static org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants.FORM_INFO_JSON;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springblade.flow.base.ActionResult;
import org.springblade.flow.base.UserInfo;
import org.springblade.flow.base.util.ActivitiUtils;
import org.springblade.flow.engine.assembly.GlobalExPropertyDTO;
import org.springblade.flow.engine.assembly.UserTaskExPropertyDTO;
import org.springblade.flow.engine.assembly.prop.UserTaskBasicPropDTO;
import org.springblade.flow.engine.custom.cmd.tasks.CancelDelegateTaskCmd;
import org.springblade.flow.engine.custom.constants.GlobalProcessVariableConstants;
import org.springblade.flow.engine.custom.variables.entity.FormDataInfo;
import org.springblade.flow.engine.entity.FlowTaskEntity;
import org.springblade.flow.engine.enums.FlowStatusEnum;
import org.springblade.flow.engine.model.flowtask.CounterSignModel;
import org.springblade.flow.engine.model.flowtask.FlowTaskForm;
import org.springblade.flow.engine.model.flowtask.FlowTaskInfoVO;
import org.springblade.flow.engine.service.FlowDynamicService;
import org.springblade.flow.engine.service.FlowTaskService;
import org.springblade.flow.exception.WorkFlowException;
import org.springblade.flow.util.UserProvider;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

import cn.hutool.core.map.MapUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

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

	@Autowired
	private FlowDynamicService flowDynamicService;
	@Autowired
	private FlowTaskService flowTaskService;
	@Resource
	private RuntimeService runtimeService;
	@Resource
	private ManagementService managementService;
	@Resource
	private HistoryService historyService;
	@Resource
	private TaskService taskService;
	@Resource
	private UserProvider userProvider;
	@Resource
	private ActivitiUtils activitiUtils;

	@ApiOperation("委派任务")
	@PostMapping("/delegateTask")
	public ActionResult<Object> delegateTask(@RequestBody Map<String, String> params) {
		try {
			String taskId = MapUtil.getStr(params, "taskId");
			String delegateUserId = MapUtil.getStr(params, "delegateUserId");
			taskService.delegateTask(taskId, delegateUserId);
			return ActionResult.success("委派成功");
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("委派失败");
		}
	}

	@ApiOperation("/委派人完成任务")
	@PostMapping("/resolveTask")
	public ActionResult<Object> resolveTask(@RequestBody Map<String, String> params) {
		try {
			String taskId = MapUtil.getStr(params, "taskId");
			taskService.resolveTask(taskId);
			return ActionResult.success("办理任务成功");
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("办理任务失败");
		}
	}

	@ApiOperation("取消委派")
	@PostMapping("/cancelResolveTask")
	public ActionResult<Object> cancelResolveTask(@RequestBody Map<String, String> params) {
		try {
			UserInfo userInfo = userProvider.get();
			String userId = userInfo.getUserId();
			String taskId = MapUtil.getStr(params, "taskId");
			managementService.executeCommand(new CancelDelegateTaskCmd(taskId, userId));
			return ActionResult.success("取消委派任务成功");
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("取消委派任务失败");
		}
	}

	@ApiOperation("查看被委派任务列表 1 查看委派且未处理的任务  2查看委派且已处理完成的任务")
	@PostMapping("/getDelegateTaskList")
	public ActionResult<Object> getDelegateTaskList(@RequestBody Map<String, String> params) {
		try {
			Integer currentPage = MapUtil.getInt(params, "currentPage");
			Integer pageSize = MapUtil.getInt(params, "pageSize");
			Integer status = MapUtil.getInt(params, "status");// 1 查看委派且未处理的任务 2查看委派且已处理完成的任务

			String userId = userProvider.getUserId();
			TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(userId);
			if (1 == status) {
				taskQuery.taskDelegationState(DelegationState.PENDING);
			} else {
				taskQuery.taskDelegationState(DelegationState.RESOLVED);
			}

			List<Task> tasks = taskQuery.listPage((currentPage - 1) * pageSize, pageSize);
			List<FlowTaskEntity> result = new ArrayList<>();
			for (Task task : tasks) {
				ProcessDefinitionEntity processDefinitionEntity = activitiUtils
						.getProcessDefinitionEntityById(task.getProcessDefinitionId());
				Process process = getBpmnModelMainProcess(task.getProcessDefinitionId());
				String property = getAttributeValueByTaskDefinitionKey(process, "", "global");
				GlobalExPropertyDTO exPropertyDTO = JSONObject.parseObject(property,
						new TypeReference<GlobalExPropertyDTO>() {
						});

				String taskProperty = getAttributeValueByTaskDefinitionKey(process, task.getTaskDefinitionKey(),
						"userTask");
				UserTaskExPropertyDTO taskExPropertyDTO = JSONObject.parseObject(taskProperty,
						new TypeReference<UserTaskExPropertyDTO>() {
						});
				UserTaskBasicPropDTO userTaskBasicPropDTO = taskExPropertyDTO.getUserTaskBasicPropDTO();
				List<String> activeActivityIds = runtimeService.getActiveActivityIds(task.getProcessInstanceId());
				HashSet<String> setActiveActivityIds = new HashSet<>(activeActivityIds);
				FlowTaskEntity flowTaskEntity = new FlowTaskEntity();
				Map<String, Object> processVariables = task.getProcessVariables();
				flowTaskEntity.setId(task.getId());
				flowTaskEntity.setProcessId(task.getId());
				flowTaskEntity.setEnCode(MapUtil.getStr(processVariables, GlobalProcessVariableConstants.BILL_CODE));
				flowTaskEntity.setFullName(MapUtil.getStr(processVariables, BUSINESS_TITLE));
				flowTaskEntity
						.setFlowUrgent(MapUtil.getInt(processVariables, GlobalProcessVariableConstants.FLOW_URGENT));
				flowTaskEntity
						.setFlowId(MapUtil.getStr(processVariables, GlobalProcessVariableConstants.FLOW_ENGINE_ID));
				flowTaskEntity.setFlowCode(processDefinitionEntity.getKey());
				flowTaskEntity.setFlowName(processDefinitionEntity.getName());
				flowTaskEntity.setFlowType(exPropertyDTO.getFlowType());
				flowTaskEntity.setFlowCategory(
						MapUtil.getStr(processVariables, GlobalProcessVariableConstants.PROCESS_CATEGORY));
				flowTaskEntity.setFlowForm(exPropertyDTO.getFormData());
				FormDataInfo formDataInfo = MapUtil.get(processVariables, FORM_INFO_JSON, FormDataInfo.class);
				flowTaskEntity.setFlowFormContentJson(formDataInfo.getFormDataJson());
				flowTaskEntity.setFlowTemplateJson(exPropertyDTO.getFlowTemplateJson());
				flowTaskEntity.setFlowVersion(exPropertyDTO.getFlowVersion());
				flowTaskEntity.setCreatorTime(task.getCreateTime());
				flowTaskEntity.setStartTime(task.getCreateTime());
				flowTaskEntity.setEndTime(null);
				flowTaskEntity
						.setStatus(MapUtil.getInt(processVariables, GlobalProcessVariableConstants.PROCESS_STATUS));
				flowTaskEntity.setCompletion(userTaskBasicPropDTO.getProgress() == null ? 50
						: Integer.valueOf(userTaskBasicPropDTO.getProgress()));
				flowTaskEntity.setEnabledMark(1);
				flowTaskEntity.setThisStepId(String.join(",", setActiveActivityIds));
//                List<FlowElement> activities = getFlowElementsWithOutSequenceFlow(process);
				List<String> activityNames = new ArrayList<>();
				for (String setActiveActivityId : setActiveActivityIds) {
					FlowElement activity = process.getFlowElement(setActiveActivityId);
					activityNames.add(activity.getName());
				}
				flowTaskEntity.setThisStep(String.join(",", activityNames));
				result.add(flowTaskEntity);
			}
			return ActionResult.success("查询成功", result);
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("查询失败");
		}
	}

	@ApiOperation("普通节点||会签节点的 加签/减签")
	@PostMapping("/counterSign")
	public ActionResult<Object> counterSign(@RequestBody CounterSignModel counterSignModel) {
		try {
			flowTaskService.counterSign(counterSignModel);
			return ActionResult.success("操作成功");
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("操作失败");
		}
	}

	/**
	 * 动态表单信息
	 *
	 * @param id 主键值
	 * @return
	 */
	@ApiOperation("动态表单信息")
	@GetMapping("/{id}")
	public ActionResult<FlowTaskInfoVO> info(@PathVariable("id") String id) throws WorkFlowException {
		try {
			// FlowTaskEntity entity = flowTaskService.getInfo(id);
			HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(id)
					.singleResult();
			String processInstanceId = null;
			if (historicTaskInstance == null) {
//            historicTaskInstance=historyService.createHistoricTaskInstanceQuery().processVariableValueEquals(BUSINESS_KEY,taskId).singleResult();
				HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
						.processInstanceBusinessKey(id).singleResult();
				processInstanceId = historicProcessInstance.getId();
			} else {
				processInstanceId = historicTaskInstance.getProcessInstanceId();
			}
			FlowTaskInfoVO vo = flowDynamicService.info(processInstanceId);
			return ActionResult.success(vo);
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("查询表单失败");
		}
	}

	/**
	 * 保存
	 *
	 * @param flowTaskForm 动态表单
	 * @return
	 */
	@ApiOperation("保存")
	@PostMapping
	public ActionResult<Object> save(@RequestBody FlowTaskForm flowTaskForm) throws WorkFlowException {
		try {
			if (FlowStatusEnum.save.getMessage().equals(flowTaskForm.getStatus())) {
				flowDynamicService.save(null, flowTaskForm.getFlowId(), flowTaskForm.getData());
				return ActionResult.success("保存成功");
			}
			flowDynamicService.submit(null, flowTaskForm.getFlowId(), flowTaskForm.getData(),
					flowTaskForm.getFreeApproverUserId());
			return ActionResult.success("提交成功，请耐心等待");
		} catch (Exception e) {
			e.printStackTrace();
			return ActionResult.fail("提交失败");
		}
	}

	/**
	 * 提交
	 *
	 * @param flowTaskForm 动态表单
	 * @return
	 */
	@ApiOperation("提交")
	@PutMapping("/{id}")
	public ActionResult<Object> submit(@RequestBody FlowTaskForm flowTaskForm, @PathVariable("id") String id)
			throws WorkFlowException {
		if (FlowStatusEnum.save.getMessage().equals(flowTaskForm.getStatus())) {
			flowDynamicService.save(id, flowTaskForm.getFlowId(), flowTaskForm.getData());
			return ActionResult.success("保存成功");
		}
		flowDynamicService.submit(id, flowTaskForm.getFlowId(), flowTaskForm.getData(),
				flowTaskForm.getFreeApproverUserId());
		return ActionResult.success("提交成功，请耐心等待");
	}

	/**
	 * 动态表单详情
	 *
	 * @param flowId 引擎主键值
	 * @param id     主键值
	 * @return
	 */
	@ApiOperation("动态表单信息")
	@GetMapping("/{flowId}/{id}")
	public ActionResult<Object> info(@PathVariable("flowId") String flowId, @PathVariable("id") String id)
			throws WorkFlowException {
		Map<String, Object> data = flowDynamicService.getData(flowId, id);
		return ActionResult.success(data);
	}

}
