package com.persagy.bdtp.adm.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.fasterxml.jackson.databind.node.TextNode;
import com.persagy.bdtp.adm.common.AdmConst;
import com.persagy.bdtp.adm.dao.AdmJobSpaceMapper;
import com.persagy.bdtp.adm.entity.Pagination;
import com.persagy.bdtp.adm.entity.db.AdmJobSpace;
import com.persagy.bdtp.adm.service.ISyncSpace;
import com.persagy.bdtp.adm.service.ServiceUtil;
import com.persagy.bdtp.adm.util.GeomUtil;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.common.constant.ValidEnum;
import com.persagy.dmp.digital.client.DigitalObjectClient;
import org.locationtech.jts.geom.Polygon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
public class SyncSpaceImpl implements ISyncSpace {

	@Autowired
	private ObjectMapper objectMapper;

	@Autowired
	private DigitalObjectClient objectClient;

	@Autowired
	private AdmJobSpaceMapper jobSpaceMapper;

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Map<String, Object> sync(String groupCode, String projectId, String userId, String floorId, String zone) {
		if(StrUtil.isBlank(floorId))
			floorId = null;

		//查询bdtp的空间数据
		QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, AdmConst.OBJ_TYPE_SPACE);
		criteria.getCriteria().put("classCode", zone); //默认工作空间为物业分区
		//按楼层查询
		if (floorId != null) {
			ObjectNode rt = criteria.getCriteria().putObject("relationTo");
			rt.put("graphCode", "ArchSubset");
			rt.put("relCode", "Fl2Sp");
			rt.put("objFrom", floorId);
		}
		List<ObjectNode> spList = ServiceUtil.queryAllPage(() -> objectClient.query(groupCode, projectId, AdmConst.APP_ID, userId, criteria), criteria, new Pagination(500));

		List<AdmJobSpace> toCreate = new ArrayList<>();
		List<AdmJobSpace> toModify = new ArrayList<>();

		if(floorId == null){
			HashMap<String, List<ObjectNode>> floorMap = new HashMap<>();
			for(ObjectNode sp : spList) {
				JsonNode fId = sp.get("floorId");
				if(fId != null) {
					String flId = fId.asText();
					List<ObjectNode> flSps = floorMap.get(flId);
					if(flSps == null)
						floorMap.put(flId, flSps = new ArrayList<>());
					flSps.add(sp);
				}
			}

			for (Map.Entry<String, List<ObjectNode>> entry : floorMap.entrySet()) {
				syncByFloor(projectId, entry.getKey(), entry.getValue(), toCreate, toModify);
			}
		} else {
			syncByFloor(projectId, floorId, spList, toCreate, toModify);
		}

		String time = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
		for(AdmJobSpace job : toCreate) {
			job.setCreationTime(time);
			job.setCreator(AdmConst.CREATOR_SYNC);
			jobSpaceMapper.insert(job);
		}
		for(AdmJobSpace job : toModify) {
			job.setModifiedTime(time);
			job.setModifier(AdmConst.CREATOR_SYNC);
			jobSpaceMapper.updateById(job);
		}

		HashMap<String, Object> rtn = new HashMap<>();
		rtn.put("syncSpaces", toCreate.size() + toModify.size());
		return rtn;
	}

	private void syncByFloor(String projectId, String floorId, List<ObjectNode> spList, List<AdmJobSpace> toCreate, List<AdmJobSpace> toModify){
		//查询任务空间数据
		QueryWrapper<AdmJobSpace> wrapper = new QueryWrapper<>();
		wrapper.eq("project_id", projectId).eq("floor_id", floorId).eq("valid", ValidEnum.TRUE.getType());
		List<AdmJobSpace> jobSpaceList = jobSpaceMapper.selectList(wrapper);

		HashMap<String, AdmJobSpace> jobMap = new HashMap<>(jobSpaceList.size());
		jobSpaceList.forEach(job -> jobMap.put(job.getSpaceId(), job));

		//同步操作
		for(ObjectNode sp : spList){
			String spId = sp.get("id").asText();

			ArrayNode spOutline = null;
			Object o1 = sp.get("outline");
			if(o1 instanceof TextNode) {
				try {
					spOutline = objectMapper.readValue(((TextNode) o1).asText().toLowerCase(), ArrayNode.class); //暂时toLowerCase()处理
				} catch (IOException e) {

				}
			}
			if(!GeomUtil.checkPolygon(spOutline))
				continue;

			AdmJobSpace job = jobMap.remove(spId);
			if(job != null) { //id匹配
				boolean change = false;

				//轮廓变化的空间，重置任务状态
				if(!outlineMatch(job.getOutline(), spOutline)){
					job.setArchState(AdmConst.JOB_STATE_PROCESSING);
					job.setEquipState(AdmConst.JOB_STATE_PROCESSING);
					job.setOutline(spOutline);
					change = true;
				}

				//重置空间id
				if(!Objects.equals(spId, job.getSpaceId())) {
					job.setSpaceId(spId);
					change = true;
				}

				//轮廓和空间id都一致的，不需要做处理
				if(change)
					toModify.add(job);
			} else { //id未匹配
				job = matchByOutline(jobMap, spOutline);
				if(job != null) { //轮廓完全一致的任务空间，重新设置spaceId
					job.setSpaceId(spId);
					toModify.add(job);
				}
			}

			if (job == null) {
				job = new AdmJobSpace();
				job.setId(IdUtil.fastSimpleUUID());
				job.setSpaceId(spId);
				job.setOutline(spOutline);
				job.setArchState(AdmConst.JOB_STATE_PROCESSING);
				job.setEquipState(AdmConst.JOB_STATE_PROCESSING);
				job.setProjectId(projectId);
				job.setBuildingId(sp.get("buildingId").asText());
				job.setFloorId(floorId != null ? floorId : sp.get("floorId").asText());
				job.setValid(ValidEnum.TRUE.getType());

				toCreate.add(job);
			}
		}

		//未找到匹配关系的任务空间，删除
		for(AdmJobSpace job : jobMap.values()){
			job.setValid(ValidEnum.FALSE.getType());
			toModify.add(job);
		}
	}

	@Override
	public Map<String, Object> sync(String groupCode, String projectId, String userId, String zone) {
		return sync(groupCode, projectId, userId, null, zone);
	}

	private AdmJobSpace matchByOutline(HashMap<String, AdmJobSpace> jobMap, ArrayNode spOutline){
		if(jobMap.size() == 0)
			return null;

		if(!isValidOutline(spOutline))
			return null;

		Polygon[] ps = GeomUtil.getPolygon(spOutline);
		Map.Entry<String, AdmJobSpace> target = null;
		for(Map.Entry<String, AdmJobSpace> entry : jobMap.entrySet()){
			ArrayNode o2 = entry.getValue().getOutline();
			if(isValidOutline(o2) && polygonsMatch(ps, GeomUtil.getPolygon(o2))){
				target = entry;
				break;
			}
		}
		if(target != null){
			jobMap.remove(target.getKey());
			return target.getValue();
		}
		return null;
	}

	/**
	 * 判断空间轮廓是否变化
	 * TODO 区分覆盖等情况
	 */
	private boolean outlineMatch(ArrayNode o1, ArrayNode o2){
		return polygonsMatch(GeomUtil.getPolygon(o1), GeomUtil.getPolygon(o2));
	}


	/**
	 * 轮廓匹配判断
	 * TODO 暂不考虑顺序
	 */
	private boolean polygonsMatch(Polygon[] ps1, Polygon[] ps2){
		if (ps1.length == ps2.length) {
			for (int i = 0; i < ps2.length; i++) {
				if (!ps1[i].equalsExact(ps2[i]))
					return false;
			}
			return true;
		}
		return false;
	}

	private boolean isValidOutline(ArrayNode outline) {
		if(outline != null && outline.size() > 0 && outline.get(0).isArray()) {
			ArrayNode o1 = (ArrayNode) outline.get(0);
			if (o1.size() > 0 && o1.get(0).isArray()) {
				ArrayNode o2 = (ArrayNode) o1.get(0);
				return o2.size() >= 3;
			}
		}
		return false;
	}

}
