package com.sagacloud.endpoints;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sagacloud.broadcast.MessageHandler;
import com.sagacloud.dict.CsStaticInfo;
import com.sagacloud.endpoints.tagset.ObjectTag;
import com.sagacloud.utils.CommonMethods;
import com.sagacloud.utils.CommonUtil;
import com.sagacloud.utils.Const;
import com.sagacloud.utils.DpUtils;
import com.sagacloud.utils.InfoCodeUtil;
import com.sagacloud.utils.JSONCheckUtil;
import com.sagacloud.utils.ObjectUtil;
import com.sagacloud.utils.OssUtils;
import com.sagacloud.utils.PropertyUtil;
import com.sagacloud.utils.ZillionAgentProvider;
import com.sagacloud.utils.jsonHelper.infoPoint.InfoPoint;
import com.sagacloud.utils.objectLog.ActivityLogObject;
import com.sagacloud.utils.objectLog.ActivityLogRunnable;
import com.sagacloud.utils.objectLog.ActivityLogUtil;
import com.sagacloud.utils.objects.CommonQueryCriteria;
import com.sagacloud.utils.objects.ObjectIdName;
import com.sagacloud.utils.objects.ObjectType;
import com.sagacloud.utils.qrCode.QRCodeRunnable;
import com.sagacloud.utils.tables.HBaseTable;

/**
 * Created by wo on 10/10/19.
 */
@Path("consumable")
public class Consumable {
	public static final String defaultId = "comsumable-category-default";
	private static Set<String> categoryKeySet = new HashSet<>(Arrays.asList("code", "parent", "name"));
	
	// 批量增加或更新耗材类型
	@Path("/category/saveUpdate")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
	public String categorySaveUpdate(String jsonStr) {
		String errorMsg = null;
		JSONObject param = JSONObject.parseObject(jsonStr);
		int optType = param.getIntValue("optType");
		
		try {
			if(optType > 1 || optType < 0) {
				errorMsg = "optType值不合法！";
			}else {
				JSONArray oldData = ObjectTag.comsumableCategoryQuery();
				Set<String> codes = new HashSet<>();
				Set<String> allCodes = new HashSet<>();
				for(int i=0; i<oldData.size(); i++) {
					codes.add(oldData.getJSONObject(i).getString("code"));
				}
				allCodes.addAll(codes);
				
				Map<String, JSONObject> dataParamMap = new HashMap<>();
				JSONArray dataParamList = param.getJSONArray("dataList");
				
				for(int i=0; i<dataParamList.size(); i++) {
					JSONObject item = dataParamList.getJSONObject(i);
					
					if(!checkCategoryPram(item)) {
						errorMsg = "参数错误！有不合法的key值。";
						break;
					}
					
					String code = item.getString("code");
					if(dataParamMap.containsKey(item.getString("code"))) {
						errorMsg = "重复的类型编码：code=" + code;
						break;
					}else {
						dataParamMap.put(code, item);
					}
					
					if(1 == optType) {
						if(codes.contains(code)) {
							errorMsg = "类型编码code=" + code + "已存在！";
							break;
						}
					}else {
						if(!codes.contains(code)) {
							errorMsg = "类型编码code=" + code + "不存在！";
							break;
						}
					}
					
					allCodes.add(code);
				}
				
				if(errorMsg == null) {
					for(String key : dataParamMap.keySet()) {
						JSONObject item = dataParamMap.get(key);
						String parent = item.getString("parent");
						if(null != parent && !allCodes.contains(parent)) {
							errorMsg = "parent=" + parent + "无法识别，不存在编码为" + parent + "的类型！";
							break;
						}
					}
				}
				
				if(errorMsg == null) {
					JSONArray updateList = new JSONArray();
					if(optType == 1) {
						updateList.addAll(oldData);
						updateList.addAll(dataParamList);
					}else {
						for(int i=0; i<oldData.size(); i++) {
							JSONObject item = oldData.getJSONObject(i);
							if(dataParamMap.containsKey(item.getString("code"))) {
								updateList.add(dataParamMap.get(item.getString("code")));
							}else {
								updateList.add(item);
							}
						}
					}
					
					ObjectTag.comsumableCategoryUpdate(updateList);
				}
			}
		} catch (Exception e) {
			errorMsg = e.getMessage();
			e.printStackTrace();
		}
		
		if(null == errorMsg) {
			synchronized (CsStaticInfo.class) {
				try {
					CsStaticInfo.categoryTreeArray = ObjectTag.comsumableCategoryTreeQuery();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return DpUtils.successJsonMsg("");
		}else {
			return DpUtils.errorJsonMsg(errorMsg);
		}
	}
	
	/**
	 * 校验耗材类型参数，只含有合法的key，code值不为空
	 * @param item
	 * @return
	 */
	private boolean checkCategoryPram(JSONObject item) {
		if(null != item && item.getString("code") != null && item.getString("name") != null) {
			return categoryKeySet.containsAll(item.keySet());
		}
		return false;
	}

	// 查询所有耗材类型
	@Path("/category/queryAll")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
	public String categoryQueryAll(String jsonStr) {
		try {
			Boolean treeFormat = false;
			if(null != jsonStr) {
				JSONObject param = JSONObject.parseObject(jsonStr);
				treeFormat = param.getBoolean("treeFormat");
				treeFormat = treeFormat == null ? false : treeFormat;
			}
			JSONArray oldData = null;
			if(treeFormat) {
				oldData = ObjectTag.comsumableCategoryTreeQuery();
			}else {
				oldData = ObjectTag.comsumableCategoryQuery();
			}
			JSONObject result = new JSONObject();
			result.put("Result", "success");
			result.put("Content", oldData);
			return result.toString();
		} catch (Exception e) {
			e.printStackTrace();
			return DpUtils.errorJsonMsg("failure！" + e.getMessage());
		}
	}
	
	// 查询所有耗材类型
	@Path("/category/deleteBatch")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
	public String deleteBatch(String jsonStr) {
		try {
			JSONObject paramObj = JSONObject.parseObject(jsonStr);
			JSONArray paramCodeList = paramObj.getJSONArray("codeList");
			if(!paramCodeList.isEmpty()) {
				Set<String> delCodeParam = new HashSet<>();
				paramCodeList.forEach((o)->delCodeParam.add(o.toString()));
				
				JSONArray oldData = ObjectTag.comsumableCategoryQuery();
				Map<String, Set<String>> parent_son = new HashMap<>();
				Map<String, String> son_parent = new HashMap<>();
				Map<String, JSONObject> code_data = new HashMap<>();
				
				JSONObject item = new JSONObject();
				String son = null, parent = null;
				for(int i=0; i<oldData.size(); i++) {
					item = oldData.getJSONObject(i);
					son = item.getString("code");
					parent = item.getString("parent");
					son_parent.put(son, parent);
					code_data.put(son, item);
					
					if(null != parent) {
						if(parent_son.containsKey(parent)) {
							parent_son.get(parent).add(son);
						}else {
							parent_son.put(parent, new HashSet<>(Arrays.asList(son)));
						}
					}
				}
				
				Set<String> codeDataInDB = son_parent.keySet();
				for(String code : delCodeParam) {
					if(!codeDataInDB.contains(code)) {
						return DpUtils.errorJsonMsg("删除失败！参数值" + code + "无法识别！");
					}
				}
				
				Set<String> allDelCode = new HashSet<>(delCodeParam);
				for(String code : delCodeParam) {
					Set<String> sonCodes = new HashSet<>();
					getAllSubCode(code, sonCodes, parent_son);
					allDelCode.addAll(sonCodes);
				}
				
				Set<String> hasDataCode = queryAllUseCategory();
				
				if(!hasDataCode.isEmpty()) {
					String errorCode = null;
					for(String code : allDelCode) {
						if(hasDataCode.contains(code)) {
							errorCode = code;
							break;
						}
					}
					
					if(null != errorCode) {
						if(delCodeParam.contains(errorCode)) {
							return DpUtils.errorJsonMsg("删除失败！类型code=" + errorCode + "已被数据引用。");
						}else {
							String codeInParam = findParamCode(errorCode, delCodeParam, son_parent);
							return DpUtils.errorJsonMsg("删除失败！类型code=" + codeInParam + "的子类型code=" + errorCode + "已被数据引用。");
						}
					}
				}
				
				JSONArray newData = new JSONArray();
				for(String code : code_data.keySet()) {
					if(!allDelCode.contains(code)) {
						newData.add(code_data.get(code));
					}
				}
				
				ObjectTag.comsumableCategoryUpdate(newData);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return DpUtils.errorJsonMsg("failure！" + e.getMessage());
		}
		synchronized (CsStaticInfo.class) {
			try {
				CsStaticInfo.categoryTreeArray = ObjectTag.comsumableCategoryTreeQuery();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return DpUtils.successJsonMsg("");
	}
	
	private String findParamCode(String errorCode, Set<String> delCodeParam, Map<String, String> son_parent) {
		if(delCodeParam.contains(errorCode)) {
			return errorCode;
		}else {
			String parent = son_parent.get(errorCode);
			if(null == parent) {
				return null;
			}
			return findParamCode(parent, delCodeParam, son_parent);
		}
	}

	private Set<String> queryAllUseCategory() throws Exception {
		Set<String> resultSet = new HashSet<>();
		JSONObject json = new JSONObject();
		json.put("QueryType", "select");
		json.put("Database", OssUtils.getProperty("database.name"));
        json.put("Datatable", HBaseTable.objects.name());
        
		JSONObject criteria = new JSONObject();
		criteria.put("obj_type", "Cs");
		json.put("Criteria", criteria);
		
		JSONArray returnColumns = new JSONArray();
		returnColumns.add("subtype");
		json.put("ReturnColumns", returnColumns);
		
		JSONObject result = ZillionAgentProvider.GetAgent().Query(json);
		if(null != result && result.toString().contains("success")) {
			JSONArray content = result.getJSONArray("Content");
			if(null != content) {
				JSONObject obj = null;
				for(int i=0; i<content.size(); i++) {
					obj = content.getJSONObject(i);
					resultSet.add(obj.getString("subtype"));
				}
			}
		}
		
		resultSet.remove(null);
		
		return resultSet;
	}

    private void getAllSubCode(String code, Set<String> sonCodes, Map<String, Set<String>> parent_son) {
		if(parent_son.containsKey(code)) {
			Set<String> subCodes = parent_son.get(code);
			if(subCodes.isEmpty()) {
				return;
			}
			sonCodes.addAll(subCodes);
			for(String subCode : subCodes) {
				getAllSubCode(subCode, sonCodes, parent_son);
			}
		}
	}

	@Path("/batch/create")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String batchCreate(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        JSONObject postBody = JSONObject.parseObject(jsonStr);
        return batchCreate(projectId, postBody);
    }

    public String batchCreate(@QueryParam(Const.PROJECT_ID) String projectId, JSONObject postBody){
        try{
            String imageUrl = postBody.getString("innerImageUrl");
            Boolean genPersagyQr = postBody.getBoolean("genPersagyQr");
            if(genPersagyQr == null){
                genPersagyQr = false;
            }
            JSONArray criterias = postBody.getJSONArray("criterias");
            if(criterias == null || criterias.size() == 0){
                return DpUtils.returnJSONArray(new JSONArray(), "");
            }
            JSONArray errors = new JSONArray();
            String pjId = projectId.substring(2);
//            一. 准备数据
//            1. 为每个对象生成id, 或者检验指定的id符不符合规则, 检验是否有重复指定的id.
//            2. 获取每个类型下的信息点列表. (剔除错误类型)
            Set<String> typeSet = this.checkGenerateId(pjId, criterias, errors);
            //检查不同业务空间的信息点
            HashMap<String, HashMap<String, InfoPoint>> infoCodeMap = InfoCodeUtil.getWholeInfosWithCustomMap(pjId, typeSet.toArray(new String[typeSet.size()]));
//            3. 通过2获取到的信息点列表, 验证信息点的key是否正确(剔除错误)
//            4. 验证信息点节点的格式是否正确.
            this.verifyInfos(criterias, infoCodeMap, errors);
//            5. DefaultQRCode, 有的话不再插入二维码, 没有的话, 添加二维码信息点, 同时记录id(用于接口返回后添加二维码图片).
            Map<String, String> idFileKeyMap = new HashMap<>();
            JSONCheckUtil.addQRCodeInfo(projectId, criterias, "DefaultQRCode", idFileKeyMap);

//            二. 插入数据
//            1. 插入对象表 (将失败的剔除)
            String time = CommonUtil.getTime();
            DpUtils.submitToObjectTable(pjId, criterias, time, errors);
            // 补充信息点数据
            ArrayList<ActivityLogObject> logList = new ArrayList<>();
            for(int i = 0; i < criterias.size(); ++i) {
                JSONObject single = criterias.getJSONObject(i);
                String id = single.getString("id");
                ObjectType ot = ObjectType.consumable;
                String detailedType = single.getString("category");
                com.sagacloud.dict.InfosUtil.addIdName(pjId, ot, detailedType, single.getJSONObject("infos"), id, new ObjectIdName());
                // 添加要发送到activeMQ的消息
                ActivityLogUtil.addMessageToQueue(pjId, time, logList, id, ot);
            }
            JSONArray success = new JSONArray();
            PropertyUtil.putIDToInfos(criterias, success);
//            2. 插入信息点
            ObjectUtil.batchUpdate(pjId, criterias, false, true, false, new CommonQueryCriteria());
//            三. 接口返回后
//            1. 发送activeMQ消息, 记录日志
            ActivityLogRunnable activityLog = new ActivityLogRunnable(logList);
            ActivityLogUtil.pool.submit(activityLog);
//            2. 生成二维码文件, 上传到文件服务器
            QRCodeRunnable qrCodeRunnable = new QRCodeRunnable(idFileKeyMap, imageUrl, projectId, genPersagyQr);
            ActivityLogUtil.pool.submit(qrCodeRunnable);

            synchronized (CsStaticInfo.class) {
				try {
					CsStaticInfo.categoryTreeArray = ObjectTag.comsumableCategoryTreeQuery();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
            return PropertyUtil.returnOnCreation(success, errors, "");
        }catch (Exception ex){
            ex.printStackTrace();
            return DpUtils.errorJsonMsg(ex.getMessage() == null? "添加失败!": ex.getMessage());
        }
    }

    /**
     * 检查耗材的id, 并将所有要检查的对象类型(大类型和详细类型)返回
     * @param pjId 项目id
     * @param criterias
     * @param errors
     * @return 添加对象的详细业务空间类型
     */
    private Set<String> checkGenerateId(String pjId, JSONArray criterias, JSONArray errors) {
        Set<String> typeSet = new HashSet<>();
        Set<String> idSet = new HashSet<>();
        for(int i = criterias.size() - 1; i > -1; --i){
            JSONObject obj = criterias.getJSONObject(i);
            if(obj == null){
                criterias.remove(i);
                continue;
            }
            String cate = obj.getString(Const.CATEGORY);
            if(cate == null){
                PropertyUtil.moveToError(criterias, i, errors, "缺少类型编码");
                continue;
            }
            if(cate != null && !CsStaticInfo.getCode2name().keySet().contains(cate)){
                PropertyUtil.moveToError(criterias, i, errors, "类型编码错误");
                continue;
            }

            String prefix = String.join("", Const.OBJ_TYPE_CONSUMABLE, pjId);
            String id = obj.getString("id");
            if(id != null && id.length() > 0){
                // 已有id的对象, 验证id
                if(!DpUtils.checkId(pjId, id) || !prefix.startsWith(id.substring(0, 2))){
                    PropertyUtil.moveToError(criterias, i, errors, "指定id错误");
                    continue;
                }
            }else{
                // 没有id的对象, 生成id添加进对象
                id = DpUtils.generateUUID(prefix, true);
                obj.put("id", id);
            }
            if(idSet.contains(id)){
                PropertyUtil.moveToError(criterias, i, errors, "id重复");
                continue;
            }
            typeSet.add(cate);
            idSet.add(id);

        }
        return  typeSet;
    }

    private void verifyInfos(JSONArray criterias, HashMap<String, HashMap<String, InfoPoint>> infoCodeMap, JSONArray errors) {
        for(int i = criterias.size() - 1; i > -1; --i){
            JSONObject obj = criterias.getJSONObject(i);
            String cate = obj.getString("category");
            JSONObject infos = obj.getJSONObject("infos");
            JSONCheckUtil.validInfoCode(criterias, errors, i, ObjectType.consumable.name(), infoCodeMap.get(cate), infos);
        }
    }

    @Path("/batch/delete")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String batchDelete(@QueryParam(Const.PROJECT_ID) String projectId, String jsonStr){
        try{
            String validateInfo = DpUtils.validateJsonSchema(jsonStr, "jsonSchema/propertyDeleteSchema.json");
            if(validateInfo != null && validateInfo.length() != 0)
                return  DpUtils.errorJsonMsg(validateInfo);
        }catch (Exception ex)
        {
            return DpUtils.errorJsonMsg("输入数据格式错误!");
        }
        JSONObject obj = JSONObject.parseObject(jsonStr);
        JSONArray objects = (JSONArray) obj.get("criterias");
        JSONArray errors = new JSONArray();
        if(objects.size() == 0)
            return PropertyUtil.returnJSON(false, null, errors, "");
        // 删除
        try {
            String pjId = projectId.substring(2);
            ArrayList<String> disableObjs = new ArrayList<>();
            ArrayList<String> deleteObjs = new ArrayList<>();
            PropertyUtil.deleteObjects(pjId, objects, errors, disableObjs, deleteObjs);
            if(objects.size() > 0)
                MessageHandler.publish(String.format("{\"type\":8,\"projectId\":\"%s\",\"name\":\"relationChange\",\"description\":\"\"}", CommonMethods.getProject(projectId,true)));

            // 删除、作废设备、系统的所有关系
            ObjectUtil.deleteAllRelation(pjId, deleteObjs);
        }catch (Exception ex)
        {
            return DpUtils.errorJsonMsg("删除失败");
        }
        if(errors.size() > 0)
            return PropertyUtil.returnJSON(objects.size() != 0, null, errors, "");
        
        synchronized (CsStaticInfo.class) {
			try {
				CsStaticInfo.categoryTreeArray = ObjectTag.comsumableCategoryTreeQuery();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
        return DpUtils.successJsonMsg("删除成功");
    }
}
