package com.persagy.bdtp.adm.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.bdtp.adm.client.RwdClient;
import com.persagy.bdtp.adm.common.AdmConst;
import com.persagy.bdtp.adm.common.AdmRequest;
import com.persagy.bdtp.adm.common.AdmResult;
import com.persagy.bdtp.adm.dao.*;
import com.persagy.bdtp.adm.entity.*;
import com.persagy.bdtp.adm.entity.db.*;
import com.persagy.bdtp.adm.service.IConfigService;
import com.persagy.bdtp.adm.service.ISyncApp;
import com.persagy.bdtp.adm.service.ServiceUtil;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.common.constant.ResponseCode;
import com.persagy.dmp.common.exception.BusinessException;
import com.persagy.dmp.define.entity.GraphDefine;
import com.persagy.dmp.define.entity.RelationDefine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.*;

@CrossOrigin
@RestController
@RequestMapping("/tool")
public class ToolController {

	@Autowired
	private ObjectMapper objectMapper;

	@Autowired
	@Qualifier(ServiceUtil.SYNC_APP_IMPL)
	private ISyncApp syncApp;

	@Autowired
	private RwdClient rwdClient;

	@Autowired
	private IConfigService configService;

	@Autowired
	private AdmM2dEquipMapper m2dEquipMapper;

	@GetMapping("/hello")
	public AdmResult<Integer> hello(){
		return AdmResult.success(configService.queryCommonConfig(null).size());
	}

	@RequestMapping("/dict")
	public AdmResult<Dict> dict(@RequestBody AdmRequest req, @RequestParam(required = false) String module){
		Dict map = syncApp.queryDict(req.getGroupCode(), req.getProjectId(), req.getUserId(), !"infos".equals(module), false);

		List<Map<String, Object>> majorList = map.getMajor();
		List<TypeDef> sysList = map.getSystem();
		List<TypeDef> equipList = map.getEquipment();
		distinct(sysList);
		distinct(equipList);

		clearTime(majorList);
		clearTime(sysList);
		clearTime(equipList);

		//设置正向交付类型
		List<AdmM2dEquip> m2dList = configService.queryM2dEquip(req.getProjectId());
		HashSet<String> m2dSet = new HashSet<>();
		m2dList.forEach(item -> m2dSet.add(item.getClassCode()));
		for(TypeDef eqType : equipList) {
			if(m2dSet.contains(eqType.getCode()))
				eqType.setM2d(true);
		}

		//组织专业-系统树形结构
		for(Map<String, Object> major : majorList){
			ArrayList<TypeDef> childrenSys = new ArrayList<>();
			major.put("children", childrenSys);
			String majorCode = (String) major.get("code");
			for(Iterator<TypeDef> sysIter = sysList.iterator(); sysIter.hasNext();){
				TypeDef sysDef = sysIter.next();
				if(sysDef.getMajorCode() != null && sysDef.getMajorCode().equals(majorCode)){
					childrenSys.add(sysDef);
					sysIter.remove();
				}
			}
		}
		map.setSystem(null);

		if("infos".equals(module)) { //信息点配置

		} else { //关系、管道、无模型设备
			List<RelationDefine> relDefs = (List<RelationDefine>)map.getRelation();
			List<GraphDefine> graph = ServiceUtil.call (() -> rwdClient.queryGraphDef(req.getGroupCode(), req.getProjectId(), AdmConst.APP_ID, req.getUserId(), new QueryCriteria()));
			List<Map<String, Object>> rels = new ArrayList<>();
			for(GraphDefine gd : graph){
				HashMap<String, Object> m = new HashMap<>();
				m.put("code", gd.getCode());
				m.put("name", gd.getName());
				m.put("type", "graph");
				rels.add(m);

				ArrayList<RelationDefine> chidren = new ArrayList<>();
				m.put("children", chidren);
				for(RelationDefine relDef : relDefs) {
					if(relDef.getGraphCode().equals(gd.getCode()))
						chidren.add(relDef);
				}
			}
			map.setRelation(rels);
		}

		return AdmResult.success(map);
	}

	private <T> void distinct(List<T> list){
		List<T> result = new ArrayList<>();
		HashSet<String> codes = new HashSet<>();
		for(T def : list) {
			String code = null;
			if(def instanceof TypeDef)
				code = ((TypeDef) def).getCode();
			else if(def instanceof InfoDef)
				code = ((InfoDef) def).getCode();
			if(!codes.contains(code)) {
				codes.add(code);
				result.add(def);
			}
		}
		list.clear();
		list.addAll(result);
	}

	private void clearTime(List<?> list){
		for(Object o : list) {
			if(o instanceof TypeDef) {
				((TypeDef) o).setCreationTime(null);
				((TypeDef) o).setModifiedTime(null);
			} else if(o instanceof Map) {
				((Map) o).put("creationTime", null);
				((Map) o).put("modifiedTime", null);
				((Map) o).put("name", ((Map) o).get("label")); //handle major
			}
		}
	}

	@RequestMapping("/cfgs")
	public AdmResult<Object> cfgs(@RequestBody AdmRequest req, @RequestParam(required = false) String module){
		String projectId = req.getProjectId();
		HashMap<String, Object> data = new HashMap<>();

		//配置数据查询
		if("infos".equals(module)) { //信息点配置
			List<AdmInfosConfig> infoConfig = configService.queryInfosConfig(projectId);
			data.put("infoConfig", infoConfig);
		} else {
			List<AdmRelsConfig> relConfig = configService.queryRelsConfig(projectId);
			List<AdmPipeConfig> pipeConfig = configService.queryPipeConfig(projectId);
			List<AdmContainerConfig> containerConfig = configService.queryContainerConfig(projectId);

			data.put("relConfig", relConfig);
			data.put("pipeConfig", pipeConfig);
			data.put("containerConfig", containerConfig);
		}

		return AdmResult.success(data);
	}

	@RequestMapping("/typeInfos")
	public AdmResult<Object> typeInfos(@RequestBody AdmRequest req, @RequestParam String typeCode){
		QueryCriteria criteria = new QueryCriteria();
		ObjectNode node = objectMapper.createObjectNode();
		node.put("classCode", typeCode);
		criteria.setCriteria(node);

		List<Object> roots = new ArrayList<>();

		List<InfoDef> funcList = ServiceUtil.call(() -> rwdClient.queryFunc(req.getGroupCode(), req.getProjectId(), AdmConst.APP_ID, req.getUserId(), criteria));
		distinct(funcList);
		for(InfoDef infoDef : funcList) {
			if("交付工具".equals(infoDef.getFirstTag()))
				continue;
			buildInfosTree(roots, infoDef);
		}

		return AdmResult.success(roots);
	}

	private void buildInfosTree(List<Object> roots, InfoDef infoDef){
		Map<String, Object> folder = null;
		if(StrUtil.isNotBlank(infoDef.getFirstTag())) {
			folder = findParent(roots, infoDef.getFirstTag());
			if(StrUtil.isNotBlank(infoDef.getSecondTag())){
				folder = findParent((List<Object>) folder.get("children"), infoDef.getSecondTag());
			}
		}
		if(folder == null)
			roots.add(infoDef);
		else
			((List<Object>) folder.get("children")).add(infoDef);
	}

	private Map<String, Object> findParent(List<Object> list, String tag){
		for(Object o : list){
			if(o instanceof Map){
				if(tag.equals(((Map) o).get("code")))
					return (Map<String, Object>) o;
			}
		}

		Map<String, Object> p = new HashMap<>();
		p.put("code", tag);
		p.put("name", tag);
		p.put("info_tag", true);
		p.put("children", new ArrayList<>());
		list.add(p);
		return p;
	}

	@Autowired
	private AdmRelsConfigMapper relsConfigMapper;
	@Autowired
	private AdmPipeConfigMapper pipeConfigMapper;
	@Autowired
	private AdmContainerConfigMapper containerConfigMapper;

	@PostMapping("/updateCfgItem")
	public AdmResult<Object> updateCfgItem(@RequestBody Map<String, Object> content) {
		String type = (String) content.get("type");
		Map<String, Object> itemMap = (Map<String, Object>) content.get("item");
		String delId = (String) content.get("delId");
		String newId = null;
		if(itemMap != null && !itemMap.containsKey("id")){
			newId = IdUtil.fastSimpleUUID();
			itemMap.put("id", newId);
		}

		boolean create = newId != null;
		if("rel".equals(type)) {
			doUpdateItem(delId, itemMap, create, AdmRelsConfig.class, relsConfigMapper);
		} else if ("pipe".equals(type)) {
			doUpdateItem(delId, itemMap, create, AdmPipeConfig.class, pipeConfigMapper);
		} else if("container".equals(type)) {
			doUpdateItem(delId, itemMap, create, AdmContainerConfig.class, containerConfigMapper);
		}

		return AdmResult.success(newId);
	}
	
	private <T extends BaseAdmEntity> void doUpdateItem(String delId, Map<String, Object> itemMap, boolean create, Class<T> cls, BaseMapper<T> mapper){
		if(StrUtil.isNotBlank(delId))
			mapper.deleteById(delId);
		else {
			T cfg = mapToObj(itemMap, cls);
			if(create)
				mapper.insert(cfg);
			else
				mapper.updateById(cfg);
		}
	}

	private <T extends BaseAdmEntity> T mapToObj(Map<String, Object> map, Class<T> cls){
		try {
			String json = objectMapper.writeValueAsString(map);
			return objectMapper.readValue(json, cls);
		} catch (IOException e) {
			e.printStackTrace();
			throw new BusinessException(ResponseCode.A0427.getCode(), ResponseCode.A0427.getDesc());
		}
	}

	@Autowired
	private AdmInfosConfigMapper infosConfigMapper;

	@PostMapping("/updateInfos")
	public AdmResult<Object> updateInfos(@RequestBody AdmInfosConfig cfg) {
		String newId = null;
		if(StrUtil.isBlank(cfg.getId())){
			newId = IdUtil.fastSimpleUUID();
			cfg.setId(newId);
		}
		if(cfg.getInfos() != null) {
			for(Object o : cfg.getInfos()) {
				if(o instanceof Map){
					((Map<?, ?>) o).remove("name");
					((Map<?, ?>) o).remove("cfgItemType");
				}
			}
		}

		if(newId != null)
			infosConfigMapper.insert(cfg);
		else
			infosConfigMapper.updateById(cfg);

		return AdmResult.success(newId);
	}

	@PostMapping("/updateM2d")
	public AdmResult<Object> updateM2d(@RequestBody Map<String, Object> cfg){
		String code = (String) cfg.get("code");
		boolean m2d = (Boolean)cfg.get("m2d");
		AdmM2dEquip item = m2dEquipMapper.selectOne(new QueryWrapper<AdmM2dEquip>().eq("class_code", code));
		if(m2d){
			if(item == null){
				item = new AdmM2dEquip();
				item.setId(IdUtil.simpleUUID());
				item.setClassCode(code);
				m2dEquipMapper.insert(item);
			}
		} else {
			if(item != null)
				m2dEquipMapper.deleteById(item.getId());
		}
		return AdmResult.success(null);
	}

	@PostMapping("/bdAndFls")
	public AdmResult<List<ObjectNode>> bdAndFls(@RequestBody AdmRequest req){
		Map<String, Object> data = syncApp.downloadFrameData(req.getGroupCode(), req.getProjectId(), req.getUserId());
		List<ObjectNode> bdAndFls = (List<ObjectNode>)data.get("buildingsAndFloors");

		List<ObjectNode> bds = new ArrayList<>();
		Iterator<ObjectNode> iter = bdAndFls.iterator();
		while (iter.hasNext()){
			ObjectNode node = iter.next();
			if(AdmConst.OBJ_TYPE_BUILDING.equals(node.get("objType").asText())) {
				bds.add(node);
				iter.remove();
			}
		}

		for(ObjectNode bd : bds) {
			String id = bd.get("id").asText();
			for(ObjectNode fl : bdAndFls) {
				JsonNode node = fl.get("buildingId");
				if(node != null){
					String bdId = node.asText();
					if(bdId.equals(id)){
						ArrayNode children = (ArrayNode)bd.get("children");
						if(children == null)
							children = bd.putArray("children");
						children.add(fl);
					}
				}
			}
		}

		return AdmResult.success(bds);
	}

}
