package com.hotent.base.aop;

import java.lang.reflect.Method;

import javax.annotation.Resource;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hotent.base.annotation.Workflow;
import com.hotent.base.entity.BaseModel;
import com.hotent.base.exception.BaseException;
import com.hotent.base.exception.WorkFlowException;
import com.hotent.base.feign.BpmRuntimeFeignService;
import com.hotent.base.util.BeanUtils;
import com.hotent.base.util.JsonUtil;
import com.hotent.base.util.StringUtil;

/**
 * 工作流切面处理
 *
 * @company 广州宏天软件股份有限公司
 * @author heyifan
 * @email heyf@jee-soft.cn
 * @date 2018年8月17日
 */
@Aspect
@Component
public class WorkflowAspect {
	@Resource
	BpmRuntimeFeignService bpmRuntimeFeignService;
	
	@SuppressWarnings({ "rawtypes" })
	@Around("execution(* *..*ManagerImpl.*(..)) && @annotation(com.hotent.base.annotation.Workflow)")
	public Object workflow(ProceedingJoinPoint joinPoint) throws Throwable{
		Object	returnVal=null;
		Object target = joinPoint.getTarget();
		Class<?> targetClass = target.getClass();
		String methodName = joinPoint.getSignature().getName();
		Object[] params=joinPoint.getArgs();
		// 当前切中的方法
		Method method = getMethodByName(targetClass, methodName);
		// 当前实体类参数
		BaseModel param = null;
		
		for (int i = 0; i < params.length; i++){
			// 获取入参中第一个继承自 BaseModel的参数
			if (params[i] instanceof BaseModel){
				param = (BaseModel)params[i];
				break;
			}
		}
		
		if(BeanUtils.isEmpty(param)) {
			returnVal = joinPoint.proceed();
			return returnVal;
		}

		// 执行注解所在的方法
		returnVal = joinPoint.proceed();

		// 方法不为空
		if (BeanUtils.isNotEmpty(method)) {
			Workflow workflow = method.getAnnotation(Workflow.class);
			// 流程定义
			String flowKey = workflow.flowKey();
			// 未传入流程flowKey时跳过发起流程
			if (StringUtil.isNotEmpty(flowKey)) {
				ObjectNode startFlowResult = startFlow(workflow, param);

				if (BeanUtils.isNotEmpty(startFlowResult) && startFlowResult.get("state").asBoolean()) {
					String callbackMethodName = workflow.callbackMethodName();
					if (StringUtil.isNotEmpty(callbackMethodName)) {
						Method callbackMethod = getMethodByName(targetClass, callbackMethodName);
						Assert.notNull(callbackMethod, String.format("当前类中不存在名称为：%s的回调方法", callbackMethodName));
						// 流程启动成功后的回调
						callbackMethod.invoke(target, startFlowResult, param);
					}

				} else {
					String message = "流程启动失败";
					if (BeanUtils.isNotEmpty(startFlowResult) && BeanUtils.isNotEmpty(startFlowResult.get("message"))) {
						message += ":" + startFlowResult.get("message").asText();
					}
					throw new BaseException(message);
				}
			}
		}
		return returnVal;
	}

	// 通过方法名获取方法
	private Method getMethodByName(Class<?> targetClass, String methodName) {
		Method method = null;
		Method[] methods = targetClass.getMethods();
		for (int i = 0; i < methods.length; i++) {
			if (methods[i].getName().equals(methodName)) {
				method = methods[i];
				break;
			}
		}
		return method;
	}

	@SuppressWarnings({ "rawtypes" })
	private ObjectNode startFlow(Workflow workflow, BaseModel param) throws Throwable {
		// 流程定义
		String flowKey = workflow.flowKey();
		// 业务系统编码
		String sysCode = workflow.sysCode();
		// 变量keys集合
		String[] varKeys = workflow.varKeys();
		// 构建启动流程的参数
		ObjectNode startFlowParam = JsonUtil.getMapper().createObjectNode();
		startFlowParam.put("flowKey", flowKey);
		String businessKey =  param.getPkVal();
		if(StringUtil.isEmpty(businessKey)) {
			// 没有主键时抛出错误
			throw new WorkFlowException("启动流程时，实体对象中的id不能为空");
		}
		startFlowParam.put("businessKey", businessKey);
		startFlowParam.put("formType", "frame");
		if(StringUtil.isNotEmpty(sysCode)) {
			startFlowParam.put("sysCode", sysCode);
		}
		// 构建vars流程变量
		if(BeanUtils.isNotEmpty(varKeys) && varKeys.length > 0) {
			ObjectNode varsObject = JsonUtil.getMapper().createObjectNode();
			JsonNode paramJsonNode = JsonUtil.toJsonNode(param);
			for(String key : varKeys) {
				JsonNode jsonNode = paramJsonNode.get(key);
				varsObject.set(key, jsonNode);
			}
			startFlowParam.set("vars", varsObject);
		}
		
		// 调用接口启动流程
		ObjectNode startFlowResult = bpmRuntimeFeignService.start(startFlowParam);
		return startFlowResult;
	}
}
