package com.artfess.bpm.util;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.artfess.base.query.Direction;
import com.artfess.base.query.FieldSort;
import com.artfess.base.query.PageList;
import com.artfess.base.query.QueryFilter;
import com.artfess.base.query.QueryOP;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.JsonUtil;
import com.artfess.base.util.StringUtil;
import com.artfess.base.util.UniqueIdUtil;
import com.artfess.base.util.time.TimeUtil;
import com.artfess.bpm.api.constant.BpmConstants;
import com.artfess.bpm.api.constant.OpinionStatus;
import com.artfess.bpm.api.model.delegate.BpmDelegateExecution;
import com.artfess.bpm.api.model.delegate.BpmDelegateTask;
import com.artfess.bpm.api.model.identity.BpmIdentity;
import com.artfess.bpm.model.identity.DefaultBpmIdentity;
import com.artfess.bpm.natapi.inst.NatProInstanceService;
import com.artfess.bpm.persistence.manager.BpmCheckOpinionManager;
import com.artfess.bpm.persistence.model.ActTask;
import com.artfess.bpm.persistence.model.DefaultBpmCheckOpinion;
import com.artfess.bpm.persistence.model.DefaultBpmTask;
import com.artfess.uc.api.impl.util.ContextUtil;
import com.artfess.uc.api.model.IUser;
import com.artfess.uc.api.model.IdentityType;

public class BpmCheckOpinionUtil {
	/**
	 * 所有影响节点流转的审批动作
	 */
	public static final List<String> effectNodeTransActions = new ArrayList<String>() {
		private static final long serialVersionUID = 1L;
		{
			add(OpinionStatus.AGREE.getKey());
			add(OpinionStatus.OPPOSE.getKey());
			add(OpinionStatus.SKIP.getKey());
			add(OpinionStatus.REJECT.getKey());
			add(OpinionStatus.DELIVERTO_AGREE.getKey());
			add(OpinionStatus.DELIVERTO_OPPOSE.getKey());
			add(OpinionStatus.BACK_TO_START.getKey());
			add(OpinionStatus.TRANS_FORMING.getKey());
			add(OpinionStatus.SIGN_PASSED.getKey());
		}
	};

	public static DefaultBpmCheckOpinion buildBpmCheckOpinion(BpmDelegateTask delegateTask, String procInstId) {
		DefaultBpmCheckOpinion bpmCheckOpinion = new DefaultBpmCheckOpinion();
		bpmCheckOpinion.setProcDefId(delegateTask.getBpmnDefId());
		String superInstId = (String) delegateTask.getSupperVariable(BpmConstants.PROCESS_INST_ID);
		if (StringUtil.isEmpty(superInstId)) {
			superInstId = "0";
		}
		bpmCheckOpinion.setSupInstId(superInstId);
		bpmCheckOpinion.setProcInstId(procInstId);
		bpmCheckOpinion.setTaskId(delegateTask.getId());
		bpmCheckOpinion.setTaskKey(delegateTask.getTaskDefinitionKey());
		bpmCheckOpinion.setTaskName(delegateTask.getName());
		bpmCheckOpinion.setStatus(OpinionStatus.AWAITING_CHECK.getKey());
		bpmCheckOpinion.setCreateTime(delegateTask.getCreateTime());

		return bpmCheckOpinion;
	}

	/**
	 * 获取有资格审批人Json数据。
	 * 
	 * <pre>
	 * 	数据格式如下:
	 *  [{id:"",type:""},{id:"",type:""}]
	 * </pre>
	 * 
	 * @param identityList
	 * @return String
	 */
	public static String getIdentityIds(List<BpmIdentity> identityList) {
		if (BeanUtils.isEmpty(identityList))
			return "";
		ArrayNode identityArray = JsonUtil.getMapper().createArrayNode();
		for (int i = 0; i < identityList.size(); i++) {
			BpmIdentity identity = identityList.get(i);
			ObjectNode objectNode = JsonUtil.getMapper().createObjectNode();
			objectNode.put("id", identity.getId());
			objectNode.put("type", identity.getType());
			objectNode.put("groupType", identity.getGroupType());
			objectNode.put("name", identity.getName());
			identityArray.add(objectNode);
		}
		try {
			return JsonUtil.toJson(identityArray);
		} catch (IOException e) {
			return "";
		}
	}

	/**
	 * 获取有资格审批人的名字,以逗号隔开。
	 * 
	 * @param identityList
	 * @return String
	 */
	public static String getIdentityNames(List<BpmIdentity> identityList) {
		if (BeanUtils.isEmpty(identityList))
			return "";

		StringBuffer names = new StringBuffer();
		for (BpmIdentity bpmIdentity : identityList) {
			names.append(bpmIdentity.getName());
			names.append(",");
		}
		String result = names.toString();
		return result.substring(0, result.length() - 1);

	}

	public static DefaultBpmCheckOpinion buildBpmCheckOpinion(ActTask actTask, String superInstId, String procInstId) {
		IUser user = ContextUtil.getCurrentUser();
		String id = UniqueIdUtil.getSuid();

		List<BpmIdentity> identityList = new ArrayList<BpmIdentity>();
		BpmIdentity bpmIdentity = new DefaultBpmIdentity();
		bpmIdentity.setType(IdentityType.USER);
		bpmIdentity.setId(user.getUserId());
		bpmIdentity.setName(user.getFullname());
		identityList.add(bpmIdentity);

		DefaultBpmCheckOpinion bpmCheckOpinion = new DefaultBpmCheckOpinion();
		bpmCheckOpinion.setId(id);
		bpmCheckOpinion.setProcDefId(actTask.getProcDefId());
		bpmCheckOpinion.setSupInstId(superInstId);
		bpmCheckOpinion.setProcInstId(procInstId);
		bpmCheckOpinion.setTaskId(actTask.getId());
		bpmCheckOpinion.setTaskKey(actTask.getTaskDefKey());
		bpmCheckOpinion.setTaskName(actTask.getName());
		bpmCheckOpinion.setStatus(OpinionStatus.AWAITING_CHECK.getKey());
		bpmCheckOpinion.setCreateTime(actTask.getCreateTime());
		bpmCheckOpinion.setQualfieds(getIdentityIds(identityList));
		bpmCheckOpinion.setQualfiedNames(user.getFullname());

		return bpmCheckOpinion;
	}

	/**
	 * 获取发起节点或结束结点的审核意见
	 * 
	 * @param delegateExecution
	 * @param procInstId
	 * @return
	 */
	public static DefaultBpmCheckOpinion buildBpmCheckOpinion(BpmDelegateExecution delegateExecution, String procInstId,
			Boolean isEndNote) {
		IUser user = ContextUtil.getCurrentUser();
		List<BpmIdentity> identityList = new ArrayList<BpmIdentity>();
		BpmIdentity bpmIdentity = new DefaultBpmIdentity();
		bpmIdentity.setType(IdentityType.USER);
		bpmIdentity.setId(user.getUserId());
		bpmIdentity.setName(user.getFullname());
		identityList.add(bpmIdentity);

		DefaultBpmCheckOpinion bpmCheckOpinion = new DefaultBpmCheckOpinion();
		bpmCheckOpinion.setProcDefId(delegateExecution.getBpmnDefId());
		String superInstId = (String) delegateExecution.getSupperVariable(BpmConstants.PROCESS_INST_ID);
		bpmCheckOpinion.setSupInstId(superInstId);
		bpmCheckOpinion.setProcInstId(procInstId);
		bpmCheckOpinion.setTaskId(null);
		bpmCheckOpinion.setTaskKey(delegateExecution.getNodeId());
		bpmCheckOpinion.setTaskName("发起节点");
		bpmCheckOpinion.setIsRead(1);
		if (isEndNote) {
			// 对于从一个开始节点出发，可能会走向不同的结束节点，故需要记录具体的结束节点
			bpmCheckOpinion.setTaskKey(delegateExecution.getNodeId());
			bpmCheckOpinion.setTaskName(delegateExecution.getNodeName());
			bpmCheckOpinion.setStatus("end");
			bpmCheckOpinion.setOpinion("结束流程");
		}
		bpmCheckOpinion.setCreateTime(LocalDateTime.now());
		bpmCheckOpinion.setCompleteTime(bpmCheckOpinion.getCreateTime());

		bpmCheckOpinion.setQualfieds(getIdentityIds(identityList));
		bpmCheckOpinion.setQualfiedNames(user.getFullname());
		bpmCheckOpinion.setAuditor(user.getUserId());
		bpmCheckOpinion.setAuditorName(user.getFullname());
		BpmUtil.setOpinionOrgInfo(user.getUserId(), bpmCheckOpinion);
		bpmCheckOpinion.setDurMs(0L);
		return bpmCheckOpinion;
	}

	/**
	 * 获取发起节点或结束结点的审核意见
	 * 
	 * @param delegateExecution
	 * @param procInstId
	 * @return
	 */
	public static void updateExtraPropCheckOpinion(DefaultBpmCheckOpinion bpmCheckOpinion, DefaultBpmTask bpmTask) {
		if (BeanUtils.isEmpty(bpmCheckOpinion) || BeanUtils.isEmpty(bpmTask)) {
			return;
		}
		boolean isUpdate = false;
		if (StringUtil.isNotEmpty(bpmTask.getProp1())) {
			isUpdate = true;
			bpmCheckOpinion.setProp1(bpmTask.getProp1());
		}
		if (StringUtil.isNotEmpty(bpmTask.getProp2())) {
			isUpdate = true;
			bpmCheckOpinion.setProp2(bpmTask.getProp2());
		}
		if (StringUtil.isNotEmpty(bpmTask.getProp3())) {
			isUpdate = true;
			bpmCheckOpinion.setProp3(bpmTask.getProp3());
		}
		if (StringUtil.isNotEmpty(bpmTask.getProp4())) {
			isUpdate = true;
			bpmCheckOpinion.setProp4(bpmTask.getProp4());
		}
		if (StringUtil.isNotEmpty(bpmTask.getProp5())) {
			isUpdate = true;
			bpmCheckOpinion.setProp5(bpmTask.getProp5());
		}
		if (StringUtil.isNotEmpty(bpmTask.getProp6())) {
			isUpdate = true;
			bpmCheckOpinion.setProp6(bpmTask.getProp6());
		}
		if (isUpdate) {
			BpmCheckOpinionManager bpmCheckOpinionManager = AppUtil.getBean(BpmCheckOpinionManager.class);
			bpmCheckOpinionManager.updateExtraProps(bpmCheckOpinion);
		}
	}

	public static void addCheckOpinion(DefaultBpmTask bpmTask, OpinionStatus opinionStatus, String userId,
			String opinion, boolean isCompleted) {
		String bpmnInstId = bpmTask.getBpmnInstId();
		NatProInstanceService natProInstanceService = AppUtil.getBean(NatProInstanceService.class);
		String superInstId = (String) natProInstanceService.getSuperVariable(bpmnInstId, BpmConstants.PROCESS_INST_ID);

		IUser user = BpmUtil.getUser(StringUtil.isEmpty(userId) ? bpmTask.getAssigneeId() : userId);
		List<BpmIdentity> identityList = new ArrayList<BpmIdentity>();
		BpmIdentity bpmIdentity = new DefaultBpmIdentity();
		bpmIdentity.setType(IdentityType.USER);
		bpmIdentity.setId(user.getUserId());
		bpmIdentity.setName(user.getFullname());
		identityList.add(bpmIdentity);

		DefaultBpmCheckOpinion checkOpinion = new DefaultBpmCheckOpinion();
		checkOpinion.setId(UniqueIdUtil.getSuid());
		checkOpinion.setProcDefId(bpmTask.getBpmnDefId());
		checkOpinion.setSupInstId(superInstId);
		checkOpinion.setProcInstId(bpmTask.getProcInstId());
		checkOpinion.setTaskId(bpmTask.getTaskId());
		checkOpinion.setTaskKey(bpmTask.getNodeId());
		checkOpinion.setTaskName(bpmTask.getName());
		checkOpinion.setStatus(opinionStatus.getKey());

		checkOpinion.setCreateTime(LocalDateTime.now());

		checkOpinion.setQualfieds(BpmCheckOpinionUtil.getIdentityIds(identityList));
		checkOpinion.setQualfiedNames(user.getFullname());

		if (isCompleted) {
			checkOpinion.setAuditor(user.getUserId());
			checkOpinion.setAuditorName(user.getFullname());
			checkOpinion.setCompleteTime(LocalDateTime.now());
			checkOpinion.setDurMs(TimeUtil.getCurrentTimeMillis() - TimeUtil.getTimeMillis(bpmTask.getCreateTime()));
			checkOpinion.setOpinion(opinion);
		}
		BpmCheckOpinionManager bpmCheckOpinionManager = AppUtil.getBean(BpmCheckOpinionManager.class);
		bpmCheckOpinionManager.create(checkOpinion);
	}

	public static void updateCheckRevoker(String completeTaskId) {
		updateCheckOpinionStatus(completeTaskId,OpinionStatus.REVOKER);
	}
	
	/**
	 * 根据任务id获取审批记录。按照AGREE，OPPOSE，SKIP，REJECT，DELIVERTO_AGREE，BACK_TO_START 的优先级
	 * @param completeTaskId
	 * @return
	 */
	public static DefaultBpmCheckOpinion getCheckOpinionByTaskId(String completeTaskId) {
		BpmCheckOpinionManager bpmCheckOpinionManager = AppUtil.getBean(BpmCheckOpinionManager.class);
		QueryFilter<DefaultBpmCheckOpinion> queryFilter = QueryFilter.build();
		queryFilter.addFilter("task_id_", completeTaskId, QueryOP.EQUAL);
		List<FieldSort> sorter1 = new ArrayList<FieldSort>();
		FieldSort sort1 = new FieldSort("COMPLETE_TIME_", Direction.DESC);
		sorter1.add(sort1);
		queryFilter.setSorter(sorter1);
		PageList<DefaultBpmCheckOpinion> query = bpmCheckOpinionManager.query(queryFilter);
		if (BeanUtils.isEmpty(query.getRows())) {
			return null;
		}
		DefaultBpmCheckOpinion bpmCheckOpinion = null;
		Map<String, DefaultBpmCheckOpinion> opnionMap = new HashMap<>();
		for (DefaultBpmCheckOpinion opinion : query.getRows()) {
			opnionMap.put(opinion.getStatus(), opinion);
		}

		for (String status : effectNodeTransActions) {
			if (opnionMap.containsKey(status)) {
				bpmCheckOpinion = opnionMap.get(status);
				break;
			}
		}
		return bpmCheckOpinion;
	}

	/**
	 * 修改审批历史的状态。状态为待处理时，会清空审批历史的完成时间和完成人相关信息
	 * @param completeTaskId 已审批的任务id
	 * @param updStatus 要更新的状态，传null时默认为撤回
	 */
	public static void updateCheckOpinionStatus(String completeTaskId,OpinionStatus updStatus) {
		if (BeanUtils.isEmpty(updStatus)) {
			updStatus = OpinionStatus.REVOKER;
		}
		BpmCheckOpinionManager bpmCheckOpinionManager = AppUtil.getBean(BpmCheckOpinionManager.class);

		DefaultBpmCheckOpinion bpmCheckOpinion = getCheckOpinionByTaskId(completeTaskId);

		if (BeanUtils.isNotEmpty(bpmCheckOpinion)) {
			bpmCheckOpinion.setStatus(updStatus.getKey());
			if (OpinionStatus.AWAITING_CHECK.equals(updStatus)) {
				bpmCheckOpinion.setCompleteTime(null);
				bpmCheckOpinion.setOpinion(null);
				bpmCheckOpinion.setFiles(null);
				bpmCheckOpinion.setAuditor(null);
				bpmCheckOpinion.setAuditorName(null);
				bpmCheckOpinion.setAgentLeaderId(null);
			}
			bpmCheckOpinionManager.update(bpmCheckOpinion);
		}
	}
}
