package com.hotent.portal.persistence.manager.impl;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hotent.base.exception.WorkFlowException;
import com.hotent.base.feign.BpmRuntimeFeignService;
import com.hotent.base.manager.impl.BaseManagerImpl;
import com.hotent.base.model.CommonResult;
import com.hotent.base.query.FieldRelation;
import com.hotent.base.query.PageBean;
import com.hotent.base.query.PageList;
import com.hotent.base.query.QueryFilter;
import com.hotent.base.query.QueryOP;
import com.hotent.base.util.BeanUtils;
import com.hotent.base.util.JsonUtil;
import com.hotent.base.util.time.TimeUtil;
import com.hotent.portal.model.PortalMeetingApply;
import com.hotent.portal.model.PortalMeetingConfig;
import com.hotent.portal.model.PortalMeetingRoom;
import com.hotent.portal.persistence.dao.PortalMeetingApplyDao;
import com.hotent.portal.persistence.manager.PortalMeetingApplyManager;
import com.hotent.portal.persistence.manager.PortalMeetingConfigManager;
import com.hotent.portal.persistence.manager.PortalMeetingRoomManager;
import com.hotent.uc.api.impl.util.ContextUtil;
import com.hotent.uc.api.model.IGroup;
import com.hotent.uc.api.model.IUser;

import dm.jdbc.util.StringUtil;

/**
 * 会议室申请 服务实现类
 *
 * @company 广州宏天软件股份有限公司
 * @author zhangxw
 * @email zhangxw@jee-soft.cn
 * @since 2021-06-25
 */
@Service
public class PortalMeetingApplyManagerImpl extends BaseManagerImpl<PortalMeetingApplyDao, PortalMeetingApply> implements PortalMeetingApplyManager {

	@Resource
	PortalMeetingConfigManager portalMeetingConfigManager;
	@Resource
	BpmRuntimeFeignService runtimeService;
	@Resource
	PortalMeetingRoomManager roomService;
	
	@Transactional
	@Override
	public CommonResult<String> apply(PortalMeetingApply meetingApply) throws Exception {
		if(StringUtil.isEmpty(meetingApply.getMeetingId())) {
			return new CommonResult<String>(false, "申请失败：会议室不能为空。");
		}
		if(BeanUtils.isEmpty(meetingApply.getStartTime()) || BeanUtils.isEmpty(meetingApply.getEndTime())) {
			return new CommonResult<String>(false, "申请失败：申请会议室开始时间或结束时间不能为空。");
		}
		String taskId = "";
		String msg = "申请成功";
		//先判断申请时间内会议室是否已被申请
		CommonResult<String> verifyResult = verifyApply(meetingApply.getId(),meetingApply.getMeetingId(), meetingApply.getStartTime(), meetingApply.getEndTime());
		if(!verifyResult.getState()) {
			return verifyResult;
		}
		if(StringUtil.isEmpty(meetingApply.getId())) {
			//补充申请人信息
			if(StringUtil.isEmpty(meetingApply.getOwnerOwnerId())) {
				IUser user = ContextUtil.getCurrentUser();
				if(BeanUtils.isNotEmpty(user)) {
					meetingApply.setOwnerOwnerId(user.getUserId());
					meetingApply.setMeetingOwner(user.getFullname());
				}
				IGroup group = ContextUtil.getCurrentGroup();
				if(BeanUtils.isNotEmpty(group)) {
					meetingApply.setMeetingDepId(group.getGroupId());
					meetingApply.setMeetingDep(group.getName());
				}
			}
			PortalMeetingConfig config = portalMeetingConfigManager.getConfig();
			//如果会议室申请绑定了流程，则发起流程
			if(config.getBindFlow() == 1) {
				String instId = this.startApplyFlow(config, meetingApply);
				if(instId.contains(":")) {
					String[] ids = instId.split(":");
					instId = ids[0];
					taskId = ids[1];
				}
				meetingApply.setProInstId(instId);
				meetingApply.setStatus(PortalMeetingApply.STATUS_APPLYING);//状态更新为审批中
			}else {
				meetingApply.setStatus(PortalMeetingApply.STATUS_APPROVED);//不需要审批，状态更新为审批通过
			}
			super.create(meetingApply);
		}else {
			super.updateById(meetingApply);
			msg = "更新成功";
		}
		return new CommonResult<String>(true, msg, taskId);
	}
	
	/**
	 * 校验会议室申请
	 * @param roomId
	 * @param startTime
	 * @param endTime
	 * @return
	 */
	@Override
	public CommonResult<String> verifyApply(String applyId,String roomId,LocalDateTime startTime,LocalDateTime endTime){
		QueryFilter filterStart = QueryFilter.build().withPage(new PageBean(1, 10));
		filterStart.addFilter("meetingId", roomId, QueryOP.EQUAL, FieldRelation.AND ,"start");
		filterStart.addFilter("startTime", TimeUtil.getDateTimeString(endTime), QueryOP.LESS, FieldRelation.AND ,"start");
		filterStart.addFilter("startTime", TimeUtil.getDateTimeString(startTime), QueryOP.GREAT, FieldRelation.AND ,"start");
		filterStart.addFilter("status", PortalMeetingApply.STATUS_REJECTED, QueryOP.NOT_EQUAL, FieldRelation.AND ,"start");
		if(StringUtil.isNotEmpty(applyId)) {
			filterStart.addFilter("id", applyId, QueryOP.NOT_EQUAL, FieldRelation.AND ,"start");
		}
		PageList<PortalMeetingApply> pageStart = super.query(filterStart);
		if(pageStart.getTotal() > 0) {
			return new CommonResult<String>(false, "申请失败：会议室申请时间冲突。");
		}
		QueryFilter filterEnd = QueryFilter.build().withPage(new PageBean(1, 10));
		filterEnd.addFilter("meetingId", roomId, QueryOP.EQUAL, FieldRelation.AND ,"end");
		filterEnd.addFilter("endTime", TimeUtil.getDateTimeString(startTime), QueryOP.GREAT, FieldRelation.AND ,"end");
		filterEnd.addFilter("endTime", TimeUtil.getDateTimeString(endTime), QueryOP.LESS, FieldRelation.AND ,"end");
		filterEnd.addFilter("status", PortalMeetingApply.STATUS_REJECTED, QueryOP.NOT_EQUAL, FieldRelation.AND ,"start");
		if(StringUtil.isNotEmpty(applyId)) {
			filterEnd.addFilter("id", applyId, QueryOP.NOT_EQUAL, FieldRelation.AND ,"start");
		}
		PageList<PortalMeetingApply> pageEnd = super.query(filterEnd);
		if(pageEnd.getTotal() > 0) {
			return new CommonResult<String>(false, "申请失败：会议室申请时间冲突。");
		}
		//处理开始时间和结束时间重合的情况
		QueryFilter filterEqual = QueryFilter.build().withPage(new PageBean(1, 10));
		filterEqual.addFilter("meetingId", roomId, QueryOP.EQUAL, FieldRelation.AND ,"EQ");
		filterEqual.addFilter("startTime", TimeUtil.getDateTimeString(startTime), QueryOP.EQUAL, FieldRelation.AND ,"EQ");
		filterEqual.addFilter("endTime", TimeUtil.getDateTimeString(endTime), QueryOP.EQUAL, FieldRelation.AND ,"EQ");
		filterEqual.addFilter("status", PortalMeetingApply.STATUS_REJECTED, QueryOP.NOT_EQUAL, FieldRelation.AND ,"EQ");
		if(StringUtil.isNotEmpty(applyId)) {
			filterEqual.addFilter("id", applyId, QueryOP.NOT_EQUAL, FieldRelation.AND ,"EQ");
		}
		PageList<PortalMeetingApply> pageEqual = super.query(filterEqual);
		if(pageEqual.getTotal() > 0) {
			return new CommonResult<String>(false, "申请失败：会议室申请时间冲突。");
		}

		return new CommonResult<String>(true, "会议室可申请。");
	}
	
	private String startApplyFlow(PortalMeetingConfig config,PortalMeetingApply meetingApply) throws Exception {
		ObjectNode mappingNode = (ObjectNode) JsonUtil.toJsonNode(config.getFieldMapping());
		ObjectNode dataNode = JsonUtil.getMapper().createObjectNode();
		PortalMeetingRoom meetingRoom = roomService.get(meetingApply.getMeetingId());
		String boDefKey = "";
		//遍历获取bo key
		Iterator<Entry<String, JsonNode>> fields = mappingNode.fields();
		while(fields.hasNext()){
			Entry<String, JsonNode> next = fields.next();
			String key = next.getKey();
			if(BeanUtils.isNotEmpty(next.getValue())) {
				String path = next.getValue().textValue();
				String[] paths = path.split("\\.");
				if(paths.length>1) {
					String field = paths[1];
					Object value = null;
					switch (key) {
					case "subject":
						value = meetingApply.getSubject();
						break;
					case "startTime":
						value = meetingApply.getStartTime();
						break;
					case "endTime":
						value = meetingApply.getEndTime();
						break;
					case "meetingOwner":
						value = meetingApply.getMeetingOwner();
						break;
					case "ownerOwnerId":
						value = meetingApply.getOwnerOwnerId();
						break;
					case "participants":
						value = meetingApply.getParticipants();
						break;
					case "participantsId":
						value = meetingApply.getParticipantsId();
						break;
					case "meetingName":
						value = meetingApply.getMeetingName();
						break;
					case "alias":
						value = meetingRoom.getAlias();
						break;
					case "equipment":
						value = meetingRoom.getEquipment();
						break;
					case "address":
						value = meetingRoom.getAddress();
						break;
					}
					dataNode.set(field, JsonUtil.toJsonNode(value));
					if(StringUtil.isEmpty(boDefKey)) {
						boDefKey = paths[0];
					}
				}
			}
			
		}
		ObjectNode params = JsonUtil.getMapper().createObjectNode();
		params.put("boDefKey", boDefKey);
		params.set("data", dataNode);
		CommonResult<String> result = runtimeService.meetingRoomApply(params, config.getFlowKey());
		if(result.getState()) {
			return result.getValue();
		}else {
			throw new WorkFlowException("流程启动失败："+result.getMessage());
		}
	}

	@Override
	public CommonResult<String> updateByInstId(String instId, String status,String busData) throws Exception {
		PortalMeetingApply apply = baseMapper.getByInstId(instId);
		if(BeanUtils.isNotEmpty(apply)) {
			if(StringUtil.isNotEmpty(busData)) {
				//根据表单数据映射关系，获取流程表单中的数据
				apply = backfillFormData(apply, busData);
				//校验表单中会议室申请数据
				CommonResult<String> verifyResult = verifyApply(apply.getId(),apply.getMeetingId(), apply.getStartTime(), apply.getEndTime());
				if(!verifyResult.getState()) {
					return verifyResult;
				}
			}
			apply.setStatus("agree".equals(status)?PortalMeetingApply.STATUS_APPROVED:PortalMeetingApply.STATUS_REJECTED);
			super.update(apply);
		}else {
			if(StringUtil.isNotEmpty(busData)) {
				return new CommonResult<String>(false, "实例ID为："+instId+" 的会议申请不存在。");
			}else {
				return new CommonResult<String>(true, "该实例id不存在会议室申请记录。");
			}
		}
		return new CommonResult<String>(true, "状态更新成功！");
	}
	
	/**
	 * 
	 * @param apply
	 * @param busData
	 * @return
	 * @throws Exception 
	 */
	private PortalMeetingApply backfillFormData(PortalMeetingApply apply,String busData) throws Exception{
		PortalMeetingConfig config = portalMeetingConfigManager.getConfig();
		PortalMeetingRoom meetingRoom = roomService.get(apply.getMeetingId());
		ObjectNode mappingNode = (ObjectNode) JsonUtil.toJsonNode(config.getFieldMapping());
		JsonNode formData = JsonUtil.toJsonNode(busData);
		Iterator<Entry<String, JsonNode>> fields = mappingNode.fields();
		while(fields.hasNext()){
			Entry<String, JsonNode> next = fields.next();
			String key = next.getKey();
			if(BeanUtils.isNotEmpty(next.getValue())) {
				String path = next.getValue().textValue();
				String[] paths = path.split("\\.");
				if(paths.length>1) {
					String boDefKey = paths[0];
					String field = paths[1];
					if(formData.has(boDefKey) && formData.get(boDefKey).has(field)) {
						String value = formData.get(boDefKey).get(field).asText();
						switch (key) {
						case "subject":
							apply.setSubject(value);
							break;
						case "endTime":
							apply.setEndTime(TimeUtil.convertString(value));
							break;
						case "startTime":
							apply.setStartTime(TimeUtil.convertString(value));
							break;
						case "participants":
							apply.setParticipants(value);
							break;
						case "participantsId":
							apply.setParticipantsId(value);
							break;
						case "alias":
							if(!meetingRoom.getAlias().equals(value)) {
								PortalMeetingRoom nmeetingRoom = roomService.getByAlias(value);
								if(BeanUtils.isNotEmpty(nmeetingRoom)) {
									apply.setMeetingId(nmeetingRoom.getId());
									apply.setMeetingName(nmeetingRoom.getName());
								}else {
									throw new WorkFlowException("会议室别名："+value+" 不存在");
								}
							}
							break;
						}
					}
				}
			}
			
		}
		return apply;
	}

	@Override
	public PortalMeetingApply getByInstId(String instId) {
		return baseMapper.getByInstId(instId);
	}

	@Override
	public Map<String, Integer> getApplyCount(String ownerId, String roomId) throws Exception {
		Map<String,Integer> map = new HashMap<String, Integer>();
		List list = baseMapper.getApplyCount(StringUtil.isEmpty(ownerId)?null:ownerId, StringUtil.isEmpty(roomId)?null:roomId);
		if(BeanUtils.isNotEmpty(list)) {
			for (Object obj : list) {
				JsonNode node = JsonUtil.toJsonNode(obj);
				map.put(node.get("status").asText(), node.get("amount").asInt());
			}
		}
		return map;
	}

}
