package cn.myapps.runtime.common.controller;

import cn.myapps.authtime.common.service.AuthTimeServiceManager;
import cn.myapps.authtime.user.model.UserVO;
import cn.myapps.base.web.WebUser;
import cn.myapps.common.auth.IUser;
import cn.myapps.common.controller.ErrorMessage;
import cn.myapps.common.controller.Resource;
import cn.myapps.common.controller.ResourceNotFoundException;
import cn.myapps.common.data.ParamsTable;
import cn.myapps.common.exception.OBPMValidateException;
import cn.myapps.common.model.activity.Activity;
import cn.myapps.common.util.Security;
import cn.myapps.common.util.StringUtil;
import cn.myapps.constans.Web;
import cn.myapps.designtime.common.service.DesignTimeServiceManager;
import cn.myapps.designtime.form.service.FormDesignTimeService;
import cn.myapps.runtime.common.service.RunTimeServiceManager;
import cn.myapps.runtime.dynaform.document.ejb.Document;
import cn.myapps.runtime.dynaform.document.ejb.DocumentProcess;
import cn.myapps.runtime.dynaform.document.ejb.Item;
import cn.myapps.runtime.dynaform.form.FormDataPacket;
import cn.myapps.runtime.dynaform.form.ejb.Form;
import cn.myapps.runtime.dynaform.form.ejb.FormField;
import cn.myapps.common.util.cache.MemoryCacheUtil;
import com.alibaba.fastjson.JSONObject;
import com.bcxin.saas.core.utils.DocumentIdExtractUtil;
import com.bcxin.saas.core.utils.ExceptionUtils;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.Map.Entry;

import static com.jayway.jsonpath.JsonPath.parse;

/**
 * 定义了基本的运行时Restful接口信息，用于处理共同的错误返回码以及其他信息。<br/>
 * 对应资源的Restful接口需要实现这个抽象类并定义为一个 {@link Component}便可由spring自动扫描装配。
 * 
 * @author ahan
 *
 */
@RestController
public abstract class AbstractRuntimeController {

	@Autowired
	protected HttpServletRequest request;

	@Autowired
	protected HttpSession session;

	private String domain;

	/**
	 * Retrieve WebUser Object.
	 * 
	 * @return The current WebUser Object.
	 * @throws Exception
	 */
	protected WebUser getUser() throws Exception {
		WebUser user = null;
		Object frontUser = AuthTimeServiceManager.getWebUser(request);

		if (frontUser == null)
			user = getAnonymousUser();
		else
			user = (WebUser) frontUser;

		return user;
	}
	
	/**
	 * Retrieve the ParamsTable
	 * 
	 * @return ParamsTable
	 */
	public ParamsTable getParams() {
		ParamsTable pm = ParamsTable.convertHTTP(request);
		// put the domain id to parameters table.
		String domainId = getDomain();
		if (domainId != null) {
			pm.setParameter("domainid", domainId);
		}

		pm.setParameter("tenant-user-id", Security.extractBearerUserId(request));
		// put the page line count id to parameters table.
		if (pm.getParameter("_pagelines") == null)
			pm.setParameter("_pagelines", Web.DEFAULT_LINES_PER_PAGE);
		
		String parentId = pm.getParameterAsString("parentId");
		if(!StringUtil.isBlank(parentId)){
			pm.setParameter("relateid", parentId);
			pm.setParameter("parentid", parentId);
		}
		String parentid = pm.getParameterAsString("parentid");
		if(!StringUtil.isBlank(parentid)){
			pm.setParameter("relateid", parentid);
			pm.setParameter("parentId", parentid);
		}
		
		String isRelate = pm.getParameterAsString("isRelate");
		if(StringUtil.isBlank(isRelate) || !"true".equals(isRelate)){
			pm.removeParameter("parentId");
			pm.removeParameter("parentid");
		}

		return pm;
	}

	/**
	 * Get the domain.
	 * 
	 * @return The domain.
	 */
	public String getDomain() {
		if (StringUtils.isNotBlank(domain))
			return domain;
		try {
			return getUser().getDomainid();
		} catch (Exception e) {
			//todo:
		}
		return "";
	}

	protected Resource success(String errmsg, Object data) {
		if (data instanceof FormDataPacket) {
			FormDataPacket formatedData = (FormDataPacket) data;
			if (!CollectionUtils.isEmpty(formatedData.getActivities())) {
				for (Activity activity : formatedData.getActivities()) {
					activity.setActionScript(null);
					activity.setBeforeActionScript(null);
					activity.setAfterActionScript(null);
				}
			}

			return new Resource(0, errmsg, formatedData, null);
		}
		return new Resource(0, errmsg, data, null);
	}

	protected Resource successWithPagination(String errmsg, Object data, int page, int page_lines, int row_count) {
		JSONObject json = new JSONObject();
		json.put("data", data);
		json.put("page", page);
		json.put("page_lines", page_lines);
		json.put("row_count", row_count);
		resourceValue = new Resource(0, errmsg, json, null);
		return resourceValue;
	}

	protected Resource error(int errcode, String errmsg, Collection<ErrorMessage> errors) {
		return new Resource(errcode, String.format("异常:%s", errmsg), null, errors);
	}

	private Resource resourceValue;

	public Resource getResourceValue() {
		return resourceValue;
	}

	public void setResourceValue(Resource resourceValue) {
		this.resourceValue = resourceValue;
	}

	/**
	 * Get a anonymous user.
	 * 
	 * @return The anonymous user.
	 * @throws Exception
	 */
	private WebUser getAnonymousUser() throws Exception {
		UserVO vo = new UserVO();

		vo.getId();
		vo.setName("GUEST");
		vo.setLoginno("guest");
		vo.setLoginpwd("");
		vo.setRoles(null);
		vo.setEmail("");

		return new WebUser(vo);
	}

	/**
	 * 资源未找到的异常，返回404的状态，且返回错误信息。
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(ResourceNotFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	public Resource resourceNotFound(ResourceNotFoundException e) {
		return error(404, "Not Found", null);
	}

	/**
	 * 运行时异常，服务器错误，返回500状态，返回服务器错误信息。
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(RuntimeException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public Resource error(RuntimeException e) {
		return error(500, ExceptionUtils.getStackMessage(e), null);
	}

	/**
	 * 运行时异常，服务器错误，返回500状态，返回服务器错误信息。
	 *
	 * @param e
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public Resource error(Exception e) {
		return error(500, ExceptionUtils.getStackMessage(e), null);
	}


	/**
	 * 资源未找到的异常，返回404的状态，且返回错误信息。
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(OBPMValidateException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public Resource serverError(OBPMValidateException e) {
		return error(500, ExceptionUtils.getStackMessage(e), null);
	}

	/**
	 * 传参错误。
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(MethodArgumentTypeMismatchException.class)
	@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
	public Resource resourceNotFound(MethodArgumentTypeMismatchException e) {
		return error(40035, "不合法的参数", null);
	}

	/**
	 * 请求包体解析错误。
	 * 
	 * @param e
	 * @return
	 */
	@ExceptionHandler(PathNotFoundException.class)
	@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
	public Resource requestbodyError(PathNotFoundException e) {
		e.printStackTrace();
		return error(406, "请求包体参数出错", null);
	}

	/**
	 * 为Controller方法准备所需要用到的Document
	 *
	 *            ajax提交上来的内容data部分，用JSON表示，不同的接口有不同的格式，但document部分一致
	 * @param applicationId
	 *            应用ID
	 * @param baseJsonPath
	 *            由于不同的JSON格式的document路径不一致，所以需要从接口处传递进来
	 * @return 返回的文档实例
	 * @throws Exception
	 */
	public Document prepareDocument(String jsonContent, String applicationId, String baseJsonPath, ParamsTable params)
			throws Exception {
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(jsonContent, configuration);
		String formid = parse.read(baseJsonPath + ".formId");
		String viewid = parse.read(baseJsonPath + ".viewId");
		String stateid = parse.read(baseJsonPath + ".stateId");
		String parentid = parse.read(baseJsonPath + ".parentId");
		String sign = parse.read(baseJsonPath + ".sign");
		String docId = params.getDocId();
		if(StringUtil.isBlank(docId)) {
			docId = parse.read(baseJsonPath + ".id");
		}

		/**
		 * 模板表单的是以DocId为主来解决通过模板表单提交流程的功能
		 */
		if(!StringUtil.isBlank(docId) && docId.contains("--")) {
			DocumentIdExtractUtil.IdValue idValue = DocumentIdExtractUtil.extract(docId);
			formid = idValue.getFormId();
		}

		String isRelate = parse.read(baseJsonPath + ".isRelate");
		List subDocuments = parse.read(baseJsonPath + ".subDocuments");
		Map<String, Object> items = parse.read(baseJsonPath + ".items");
		Object delete = parse.read(baseJsonPath + ".delete");
		Object edit = parse.read(baseJsonPath + ".edit");
		Object isFromRefresh = params.getParameter("isFromRefresh");
		Object exparams = parse.read(baseJsonPath + ".exparams");

		if (exparams != null) {
			Map<String, Object> exparamsMap = (Map<String, Object>) exparams;
			for (Map.Entry entry : exparamsMap.entrySet()) {
				String key = entry.getKey().toString();
				Object value = entry.getValue();
				params.setParameter(key, value);
			}
		}

		if (isRelate != null && isRelate.equals("true")) {
			params.setParameter("isRelate", "true");
			if (!StringUtil.isBlank(parentid)) {
				params.setParameter("relateid", parentid);
			}
		} else {
			if (!StringUtil.isBlank(parentid)) {
				params.setParameter("relateid", parentid);
			}
			parentid = "";
		}

		if (parentid != null && !parentid.isEmpty())
			params.setParameter("parentid", parentid);
		if (viewid != null && !viewid.isEmpty())
			params.setParameter("viewid", viewid);

		DocumentProcess dProcess = RunTimeServiceManager.documentProcess(applicationId);
		Form form = null;
		IUser user = getUser();
		Document doc = null;
		if (items != null) {
			/**
			 * 审批流程过程中很多操作步骤都是通过这个缓存来修复的.
			 */
			Document temp = (Document)MemoryCacheUtil.getFromPrivateSpace(docId, user);
			if(temp==null) {
				temp = (Document) dProcess.doView(docId);
			}
			if (temp == null|| (!StringUtil.isBlank(formid) && !formid.equalsIgnoreCase(temp.getFormid())) ) {
				form = DesignTimeServiceManager.formDesignTimeService().doView(formid);
				temp = dProcess.doNew(form, user, params);
				temp.setId(docId);
			}

			for (Map.Entry<String, Object> entry : items.entrySet()) {
				String key = entry.getKey();
				Object value = entry.getValue();
				Item item = temp.findItem(key);

				if (item == null) {
					if (!StringUtil.isBlank(key) && key.lastIndexOf("_show") > -1) {
						item = temp.findItem(key.substring(0, key.lastIndexOf("_show")));
					}
				}
				if (item != null) params.setParameter(key, value);
			}

			doc = temp;
		} else {
			return new Document();
		}

		if (!items.isEmpty() && isFromRefresh == null) {
			if (form == null) {
				form = DesignTimeServiceManager.formDesignTimeService().doView(formid);
			}

			doc = form.createDocument(doc, params, user);
		}

		if (!StringUtil.isBlank(parentid)) {
			doc.setParent(parentid);
		}
		if (!StringUtil.isBlank(stateid)) {
			doc.setState(stateid);
		}
		if (!StringUtil.isBlank(sign)) {
			doc.setSign(sign);
		}
		if (StringUtil.isBlank(doc.getFormid()) && !StringUtil.isBlank(formid)) {
			doc.setFormid(formid);
		}

		if (StringUtil.isBlank(doc.getApplicationid()) && !StringUtil.isBlank(applicationId)) {
			doc.setApplicationid(applicationId);
		}

		List<Document> subDocumentList = new ArrayList<Document>();
		if (subDocuments != null) {
			for (Iterator iterator = subDocuments.iterator(); iterator.hasNext(); ) {
				Map map = (Map) iterator.next();
				ParamsTable newParams = params.copyContextParams();
				Iterator<Entry<String, Object>> iter = map.entrySet().iterator();
				while (iter.hasNext()) {
					Entry<String, Object> entry = iter.next();
					String name = entry.getKey();
					Object value = entry.getValue();
					try {
						if (value instanceof String[])
							if (((String[]) value).length > 1)
								newParams.setParameter(name, value);
							else
								newParams.setParameter(name, ((String[]) value)[0]);
						else
							newParams.setParameter(name, value);
					} catch (Exception e) {
						throw e;
					}
				}

				Document subDoc = prepareDocument(net.sf.json.JSONObject.fromObject(map).toString(), applicationId, "$", newParams);
				subDocumentList.add(subDoc);
			}
		}
		doc.setSubDocuments(subDocumentList.isEmpty() ? null : subDocumentList);
		if (delete != null) {
			doc.setDelete(true);
		}
		if (edit != null) {
			doc.setEdit(true);
		}

		//设置回缓存
		/**
		 * 执行中,后的操作将基于该doc
		 */
		MemoryCacheUtil.putToPrivateSpace(doc.getId(), doc, getUser());

		return doc;
	}

	/**
	 * 为Controller方法准备所需要用到的Document
	 *
	 * @param content
	 *            ajax提交上来的内容data部分，用JSON表示，不同的接口有不同的格式，但document部分一致
	 * @param applicationId
	 *            应用ID
	 * @param baseJsonPath
	 *            由于不同的JSON格式的document路径不一致，所以需要从接口处传递进来
	 * @return 返回的文档实例
	 * @throws Exception
	 */
	public List<Document> prepareDocuments(String jsonContent, String applicationId, String baseJsonPath)
			throws Exception {
		Configuration configuration = Configuration.defaultConfiguration();
		configuration = configuration.addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);
		DocumentContext parse = parse(jsonContent, configuration);
		List<Map<String,Object>> maps = parse(jsonContent).read(baseJsonPath) ;

		List<Document> docs = new ArrayList<Document>() ;

		for(Map<String,Object> map : maps){
			ParamsTable params = new ParamsTable() ;
			String formid = (String) map.get("formId");
			String viewid = (String) map.get("viewId");
			String stateid = (String) map.get("stateId");
			String parentid = (String) map.get("parentId");
			String sign = (String) map.get("sign");
			String docId = (String) map.get("id");
			String isRelate = (String) map.get("isRelate");
			Map<String, Object> items = (Map<String, Object>) map.get("items");

			if (isRelate!=null && isRelate.equals("true")){
				params.setParameter("isRelate", "true");
				if(!StringUtil.isBlank(parentid)){
					params.setParameter("relateid", parentid);
				}
			} else {
				if(!StringUtil.isBlank(parentid)){
					params.setParameter("relateid", parentid);
				}
				parentid = "";
			}
			if (parentid!=null && !parentid.isEmpty())
				params.setParameter("parentid", parentid);
			if (parentid!=null && !parentid.isEmpty())
				params.setParameter("viewid", viewid);

			DocumentProcess dProcess = RunTimeServiceManager.documentProcess(
					applicationId);

			FormDesignTimeService formService = DesignTimeServiceManager.formDesignTimeService();
			Form form = formService.doView(formid);

			if (items != null){
				Document temp = (Document) MemoryCacheUtil.getFromPrivateSpace(docId, getUser());
				for (Map.Entry<String, Object> entry : items.entrySet()) {
					String key = entry.getKey();
					Object value = entry.getValue();
					FormField field = form.findFieldByName(key);
					if (field!=null) params.setParameter(key, value);
				}
			} else {
				docs.add(new Document()) ;
				continue ;
			}

			IUser user = getUser();
			Document po = (Document) dProcess.doView(docId);
			Document doc = po != null ? po : null;

			if (StringUtil.isBlank(docId)) {
				Document newDoc = dProcess.doNew(form, user, params);
				docId = newDoc.getId();
			}

			if (!StringUtil.isBlank(docId)) {
	            if (doc == null) {
	                doc = (Document) MemoryCacheUtil.getFromPrivateSpace(docId, user);
	            }

	            if (doc == null) {
	            	//good流程送出脚本校验之后，会清掉存在user中的缓存，doc变null
	            	//先建立doc,获取字段
	                doc = form.createDocument(params, user);                
	    			if(doc!=null){
	    			//遍历包体值，将对应的字段值放进	
	    				for (Map.Entry<String, Object> entry : items.entrySet()) {
	    					String key = entry.getKey();
	    					Object value = entry.getValue();
	    					Item item = doc.findItem(key);
	    					if (item != null) params.setParameter(key, value);
	    				}
	    			}
	    			//将字段值赋予doc，暂时先这样处理，如有好的想法，可改
	    			doc = form.recalculateDocument(doc, params, true, user);
	            } else {
	            	if(!items.isEmpty()){
	            		doc = form.createDocument(doc, params, user);
	            	}
	            }
	        } else if (!StringUtil.isBlank(parentid)) {
	            doc = (Document) MemoryCacheUtil.getFromPrivateSpace(docId, user);
	        }

			if (!StringUtil.isBlank(parentid)) {
				doc.setParent(parentid);
			}
			if (!StringUtil.isBlank(stateid)) {
				doc.setState(stateid);
			}
			if (!StringUtil.isBlank(sign)) {
				doc.setSign(sign);
			}
			if (!StringUtil.isBlank(formid)) {
				doc.setFormid(formid);
			}
			if (!StringUtil.isBlank(applicationId)) {
				doc.setApplicationid(applicationId);
			}

			//设置回缓存
			MemoryCacheUtil.putToPrivateSpace(doc.getId(), doc, getUser());

			docs.add(doc) ;
		}

		return docs ;
	}
}
