package com.ppdai.infrastructure.ui.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import javax.annotation.PreDestroy;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.ppdai.infrastructure.mq.biz.common.SoaConfig;
import com.ppdai.infrastructure.mq.biz.common.inf.TimerService;
import com.ppdai.infrastructure.mq.biz.common.thread.SoaThreadFactory;
import com.ppdai.infrastructure.mq.biz.common.trace.Tracer;
import com.ppdai.infrastructure.mq.biz.common.trace.spi.Transaction;
import com.ppdai.infrastructure.mq.biz.common.util.JsonUtil;
import com.ppdai.infrastructure.mq.biz.common.util.Util;
import com.ppdai.infrastructure.mq.biz.dto.Constants;
import com.ppdai.infrastructure.mq.biz.dto.ReadWriteEnum;
import com.ppdai.infrastructure.mq.biz.dto.request.TopicCreateRequest;
import com.ppdai.infrastructure.mq.biz.entity.ConsumerGroupEntity;
import com.ppdai.infrastructure.mq.biz.entity.ConsumerGroupTopicEntity;
import com.ppdai.infrastructure.mq.biz.entity.DbNodeEntity;
import com.ppdai.infrastructure.mq.biz.entity.QueueEntity;
import com.ppdai.infrastructure.mq.biz.entity.QueueOffsetEntity;
import com.ppdai.infrastructure.mq.biz.entity.TopicEntity;
import com.ppdai.infrastructure.mq.biz.service.AuditLogService;
import com.ppdai.infrastructure.mq.biz.service.ConsumerGroupService;
import com.ppdai.infrastructure.mq.biz.service.ConsumerGroupTopicService;
import com.ppdai.infrastructure.mq.biz.service.DbNodeService;
import com.ppdai.infrastructure.mq.biz.service.Message01Service;
import com.ppdai.infrastructure.mq.biz.service.QueueService;
import com.ppdai.infrastructure.mq.biz.service.RoleService;
import com.ppdai.infrastructure.mq.biz.service.TopicService;
import com.ppdai.infrastructure.mq.biz.service.UserInfoHolder;
import com.ppdai.infrastructure.mq.biz.service.common.AuditUtil;
import com.ppdai.infrastructure.mq.biz.service.common.CacheUpdateHelper;
import com.ppdai.infrastructure.mq.biz.ui.dto.request.TopicGetListRequest;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicClearTokenResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicCreateResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicDeleteResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicExpandResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicGenerateTokenResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicGetByIdResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicGetListResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicGetTopicNamesResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicManualExpandResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicQueueRemoveListResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicQueueRemoveResponse;
import com.ppdai.infrastructure.mq.biz.ui.dto.response.TopicReportResponse;
import com.ppdai.infrastructure.mq.biz.ui.enums.NodeTypeEnum;
import com.ppdai.infrastructure.mq.biz.ui.exceptions.AuthFailException;
import com.ppdai.infrastructure.mq.biz.ui.exceptions.CheckFailException;
import com.ppdai.infrastructure.mq.biz.ui.vo.QueueRemoveInfoVo;
import com.ppdai.infrastructure.mq.biz.ui.vo.QueueVo;
import com.ppdai.infrastructure.mq.biz.ui.vo.TopicVo;
import com.ppdai.infrastructure.ui.util.DesUtil;


@Service
public class UiTopicService implements TimerService {
	Logger log = LoggerFactory.getLogger(this.getClass().getName());
	@Autowired
	private TopicService topicService;
	@Autowired
	private UiQueueService uiQueueService;
	@Autowired
	private SoaConfig soaConfig;
	@Autowired
	private AuditLogService uiAuditLogService;
	@Autowired
	private DbNodeService dbNodeService;
	@Autowired
	private UiQueueOffsetService uiQueueOffsetService;
	@Autowired
	private QueueService queueService;
	@Autowired
	private ConsumerGroupService consumerGroupService;
	@Autowired
	private UiConsumerGroupTopicService uiConsumerGroupTopicService;
	@Autowired
	private ConsumerGroupTopicService consumerGroupTopicService;
	@Autowired
	private RoleService roleService;
	@Autowired
	private Message01Service message01Service;
	@Autowired
	private UserInfoHolder userInfoHolder;
	private ThreadPoolExecutor executor = null;
	private volatile boolean isRunning = true;
	private AtomicBoolean startFlag = new AtomicBoolean(false);
	private AtomicReference<List<TopicVo>> topicVoListRf = new AtomicReference<>(new ArrayList<>());
	private volatile long lastUpdateTime = 0;
	private final String shouldShrink = "应该缩容";
	private final String shouldExpand = "应该扩容";

	@Override
	public void start() {
		if (startFlag.compareAndSet(false, true)) {
			lastUpdateTime = System.currentTimeMillis() - soaConfig.getMqReportInterval() * 2;
			executor = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(50),
					SoaThreadFactory.create("UiTopicService", true), new ThreadPoolExecutor.DiscardOldestPolicy());
			executor.execute(() -> {
				while (isRunning) {
					try {
						if (System.currentTimeMillis() - lastAccessTime < soaConfig.getMqReportInterval()
								|| System.currentTimeMillis() - lastAccessTime > 1000 * 60 * 60 * 60) {
							if (System.currentTimeMillis() - lastUpdateTime > soaConfig.getMqReportInterval()) {
								initCache();
								if (uiQueueService.getQueueListCount().size() > 0) {
									lastUpdateTime = System.currentTimeMillis();
									lastAccessTime = System.currentTimeMillis() - soaConfig.getMqReportInterval() * 2;
								}
							}
						}
					} catch (Exception e) {
						log.error("UiQueueServiceImpl_initCache_error", e);
					}
					if (uiQueueService.getQueueListCount().size() == 0) {
						Util.sleep(10 * 1000);
					} else {
						Util.sleep(2 * 1000);
					}
				}
			});
		}
	}

	private boolean initCache() {
		Transaction transaction = Tracer.newTransaction("UiQueueService", "initCache");
		try {
			Map<String, TopicEntity> topicMap = topicService.getCache();
			Map<String, List<QueueEntity>> topicQueueMap = queueService.getAllLocatedTopicQueue();
			Map<String, List<QueueVo>> queueVoMap = uiQueueService.getQueueListCount();
			List<TopicVo> topicVoList = new ArrayList<>();
			for (String topicName : topicMap.keySet()) {
				try {
					int queueCount = 0;
					TopicVo topicVo = new TopicVo(topicMap.get(topicName));
					long msgCount = 0;
					if (queueVoMap != null) {
						List<QueueVo> queueList = queueVoMap.get(topicName);
						if (queueList != null) {
							for (QueueVo queueVo : queueList) {
								msgCount += queueVo.getMsgCount();
							}
						}

					}
					topicVo.setMsgCount(msgCount);
					if (topicQueueMap.containsKey(topicName)) {
						queueCount = topicQueueMap.get(topicName).size();
					}

					topicVo.setQueueCount(queueCount);
					if (topicVo.getSaveDayNum() > 0) {
						topicVo.setAvgCount(topicVo.getMsgCount() / topicVo.getSaveDayNum());
					}
					if (topicVo.getQueueCount() > 0) {
						topicVo.setAvgCountOfQueue(topicVo.getAvgCount() / topicVo.getQueueCount());
					}
					if (topicVo.getAvgCountOfQueue() < 500000 && topicVo.getQueueCount() > 1) {
						topicVo.setIsReasonable(shouldShrink);
					} else if (topicVo.getAvgCountOfQueue() > 1000000) {
						topicVo.setIsReasonable(shouldExpand);
					}
					// 根据topic每天的平均消息量,计算该topic合理的队列数量(向上取整算法)
					long reasonableQueueCount = ((topicVo.getAvgCount() + 999999)
							- (topicVo.getAvgCount() + 999999) % 1000000) / 1000000;
					topicVo.setManageQueueCount(reasonableQueueCount - topicVo.getQueueCount());
					topicVoList.add(topicVo);
				} catch (Exception e) {
					throw new RuntimeException(topicName, e);
				}
			}
			topicSort(topicVoList);
			topicVoListRf.set(topicVoList);
			transaction.setStatus(Transaction.SUCCESS);
		} catch (Exception e) {
			log.error("UiQueueService_initCache_error", e);
			transaction.setStatus(e);
		} finally {
			transaction.complete();
		}
		return true;
	}

	private void topicSort(List<TopicVo> topicVoList) {
		// 按照消息总量
		Collections.sort(topicVoList, new Comparator<TopicVo>() {
			@Override
			public int compare(TopicVo q1, TopicVo q2) {
				long i = q1.getMsgCount() - q2.getMsgCount();
				if (i == 0) {
					return 0;
				} else if (i > 0) {
					return -1;
				} else {
					return 1;
				}
			}
		});
	}

	public TopicGetListResponse queryByPage(TopicGetListRequest topicGetListRequest) {
		Map<String, List<QueueEntity>> topicQueueMap = queueService.getAllLocatedTopicQueue();
		Map<String, Object> conditionMap = new HashMap<>();
		if (StringUtils.isNotBlank(topicGetListRequest.getName())) {
			conditionMap.put("name", topicGetListRequest.getName());
		}
		if (StringUtils.isNotBlank(topicGetListRequest.getId())) {
			conditionMap.put("id", Long.valueOf(topicGetListRequest.getId()));
		}
		if (StringUtils.isNotBlank(topicGetListRequest.getOwnerName())) {
			conditionMap.put(TopicEntity.FdOwnerNames, topicGetListRequest.getOwnerName());
		}
		if (StringUtils.isNotBlank(topicGetListRequest.getTopicType())) {
			conditionMap.put(TopicEntity.FdTopicType, topicGetListRequest.getTopicType());
		}
		long count = topicService.countWithUserName(conditionMap);
		if (count == 0) {
			return new TopicGetListResponse(count, null);
		}
		List<TopicEntity> topicEntityList = topicService.getListWithUserName(conditionMap,
				Long.valueOf(topicGetListRequest.getPage()), Long.valueOf(topicGetListRequest.getLimit()));
		String currentUserId = userInfoHolder.getUserId();
		List<TopicVo> topicVoList = topicEntityList.stream().map(topicEntity -> {
			TopicVo topicVo = new TopicVo(topicEntity);
			topicVo.setRole(roleService.getRole(currentUserId, topicEntity.getOwnerIds()));
			int queueCount = 0;
			if (topicQueueMap.containsKey(topicEntity.getName())) {
				queueCount = topicQueueMap.get(topicEntity.getName()).size();
			} else {
				queueCount = topicEntity.getExpectDayCount() / Constants.NUMS_OF_MESSAGE_PER_QUEUE_ONEDAY;
			}

			topicVo.setQueueCount(queueCount);
			return topicVo;
		}).collect(Collectors.toList());

		return new TopicGetListResponse(count, topicVoList);
	}

	private boolean hasAuth(String userId, TopicEntity topicEntity) {
		return Arrays.asList(topicEntity.getOwnerIds().split(",")).contains(userId) || roleService.isAdmin(userId);
	}

	private boolean isAdmin(String userId) {
		return roleService.isAdmin(userId);
	}

	public TopicCreateResponse createOrUpdateTopic(TopicCreateRequest topicCreateRequest) {
		CacheUpdateHelper.updateCache();
		String name = topicCreateRequest.getName();
		if (name.length() > 4 && "fail".equals(name.substring(name.length() - 4).toLowerCase())) {
			throw new CheckFailException("topic名称:" + name + "不能以fail结尾");
		}
		TopicEntity topicEntity = new TopicEntity();
		topicCreateRequest.setName(StringUtils.trim(topicCreateRequest.getName()));
		topicEntity.setName(topicCreateRequest.getName());
		topicEntity.setOwnerIds(topicCreateRequest.getOwnerIds());
		topicEntity.setOwnerNames(topicCreateRequest.getOwnerNames());
		topicEntity.setExpectDayCount(topicCreateRequest.getExpectDayCount());
		topicEntity.setEmails(StringUtils.trim(topicCreateRequest.getEmails()));
		topicEntity.setBusinessType(topicCreateRequest.getBusinessType());
		topicEntity.setMaxLag(topicCreateRequest.getMaxLag());
		topicEntity.setRemark(topicCreateRequest.getRemark());
		topicEntity.setDptName(topicCreateRequest.getDptName());
		topicEntity.setOriginName(topicCreateRequest.getName());
		topicEntity.setNormalFlag(topicCreateRequest.getNormalFlag());
		topicEntity.setSaveDayNum(topicCreateRequest.getSaveDayNum());
		topicEntity.setTels(topicCreateRequest.getTels());
		topicEntity.setIsActive(1);
		topicEntity.setTopicType(topicCreateRequest.getTopicType());
		topicEntity.setConsumerFlag(topicCreateRequest.getConsumerFlag());
		topicEntity.setConsumerGroupNames(topicCreateRequest.getConsumerGroupList());
		topicEntity.setAppId(topicCreateRequest.getAppId());
		String userId = userInfoHolder.getUserId();
		if (StringUtils.isNotEmpty(topicCreateRequest.getId())) {
			topicEntity.setId(Long.valueOf(topicCreateRequest.getId()));
			topicEntity.setUpdateBy(userId);
			updateTopic(topicEntity);
		} else {
			topicEntity.setInsertBy(userId);
			createSuccessTopic(topicEntity);
		}
		// 创建或者更新topic时,同步到mq2
		// synService32.synTopic32(topicCreateRequest, topicEntity);
		return new TopicCreateResponse();
	}

	private void updateTopic(TopicEntity topicEntity) {
		String currentUserId = userInfoHolder.getUserId();
		TopicEntity oldTopicEntity = baseCheckRequest(topicEntity.getId(), currentUserId);
		// 鉴于安全原因,token 不能传给前端,只能在服务端传递
		topicEntity.setToken(oldTopicEntity.getToken());
		topicService.update(topicEntity);
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicEntity.getId(),
				"更新topic," + AuditUtil.diff(oldTopicEntity, topicEntity));
	}

	private void createSuccessTopic(TopicEntity topicEntity) {
		TopicEntity entity= topicService.getTopicByName(topicEntity.getName());
		if(entity!=null){
			throw new CheckFailException("topic:"+topicEntity.getName()+"重复,检查是否有重名topic已经存在。");
		}
		try {
			topicService.insert(topicEntity);
		} catch (DuplicateKeyException e) {
			throw new CheckFailException("topic:" + topicEntity.getName() + "重复,检查是否有重名topic已经存在。");
		}
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicEntity.getId(),
				"新建topic," + JsonUtil.toJson(topicEntity));
		// 计算分配的队列数
		int expectDayCount = topicEntity.getExpectDayCount() * 10000;
		int successQueueNum = expectDayCount / Constants.MSG_NUMS_OF_ONE_QUEUE;
		// 分配队列
		topicService.distributeQueueWithLock(topicEntity, successQueueNum,
				NodeTypeEnum.SUCCESS_NODE_TYPE.getTypeCode());
	}

	public TopicDeleteResponse deleteTopic(Long topicId) {
		CacheUpdateHelper.updateCache();
		String currentUserId = userInfoHolder.getUserId();
		Map<String, List<QueueVo>> queueVoMap = uiQueueService.getQueueListCount();
		TopicEntity topicEntity = baseCheckRequest(topicId, currentUserId);

		if (uiConsumerGroupTopicService.findByTopicId(topicId).size() > 0) {
			throw new CheckFailException("目前有消费者订阅,不能删除,请通知取消订阅后再删除");
		}

		// 如果topic下存在消息量大于阈值的queue,则不允许删除
		if (queueVoMap != null && soaConfig.isPro()) {
			List<QueueVo> queueList = queueVoMap.get(topicEntity.getName());
			if (queueList != null) {
				for (QueueVo queueVo : queueList) {
					if (queueVo.getMsgCount() > soaConfig.getTopicDeleteLimitCount() * 10000) {
						throw new CheckFailException("topic:" + topicEntity.getName() + "存在消息量大于"
								+ soaConfig.getTopicDeleteLimitCount() + "万的queue,不能直接删除");
					}
				}
			}
		}

		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicEntity.getId(),
				"删除topic之前" + JsonUtil.toJson(topicEntity));
		doDeleteTopic(topicEntity);
		// 删除topic后,同步到mq2
		// synService32.synTopicDelete(topicEntity);
		return new TopicDeleteResponse();
	}

	private void doDeleteTopic(TopicEntity topicEntity) {
		Long topicId = topicEntity.getId();
		List<QueueEntity> queueEntities = queueService.getQueuesByTopicId(topicId);
		if (soaConfig.isPro() && !isAdmin(userInfoHolder.getUserId())) {
			queueEntities.forEach(queueEntity -> uiQueueService.remove(queueEntity.getId()));
		} else {
			queueEntities.forEach(queueEntity -> uiQueueService.forceRemove(queueEntity.getId()));
		}
		topicService.delete(topicId);
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicEntity.getId(),
				"删除topic," + JsonUtil.toJson(topicEntity));
	}

	public List<TopicEntity> getFailTopic(String topicName) {
		Map<String, Object> conditionMap = new HashMap<>();
		conditionMap.put(TopicEntity.FdOriginName, topicName);
		conditionMap.put(TopicEntity.FdTopicType, NodeTypeEnum.FAIL_NODE_TYPE.getTypeCode());
		return topicService.getList(conditionMap);
	}

	public TopicExpandResponse expandTopic(Long topicId) {
		String currentUserId = userInfoHolder.getUserId();
		TopicEntity topicEntity = baseCheckRequest(topicId, currentUserId);
		if (roleService.getRole(userInfoHolder.getUserId()) > 0) {
			List<QueueEntity> queueEntities = queueService.getQueuesByTopicId(topicId);
			checkQueueMessageCount(queueEntities);
			checkQueueMax(queueEntities);
		}
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId, "开始扩容, 扩容队列 1 条");
		List<QueueEntity> normalQueueList = queueService.getTopUndistributed(1, topicEntity.getTopicType(), topicId);
		if (CollectionUtils.isEmpty(normalQueueList)) {
			uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId, "数据节点不够分配");
			throw new CheckFailException("数据节点不够分配,请联系管理员");
		}
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId,
				String.format("对备选队列进行分配: %s", normalQueueList.get(0).getId()));
		topicService.distributeQueue(topicEntity, normalQueueList.get(0));
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId, "扩容结束");
		uiQueueOffsetService.createQueueOffsetForExpand(normalQueueList.get(0), topicId, topicEntity);
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId,
				"分配queue," + JsonUtil.toJson(normalQueueList.get(0).getId()));
		consumerGroupService.notifyRb(uiConsumerGroupTopicService.findByTopicId(topicId).stream()
				.map(ConsumerGroupTopicEntity::getConsumerGroupId).collect(Collectors.toList()));

		return new TopicExpandResponse();
	}

	private void checkQueueMessageCount(List<QueueEntity> queueEntities) {
		if (!soaConfig.getMaxTableMessageSwitch()) {
			return;
		}
		if (CollectionUtils.isEmpty(queueEntities)) {
			return;
		}
		Long allMessageCount = 0L;

		for (QueueEntity queueEntity : queueEntities) {
			allMessageCount += getQueueMessage(queueEntity);
		}

		if (allMessageCount / queueEntities.size() > soaConfig.getMaxTableMessage()) {
			throw new CheckFailException("每队列消息量未达到最大值,不允许扩容,可联系管理员强制扩容");
		}
	}

	private void checkQueueMax(List<QueueEntity> queueEntities) {
		int maxQueue = soaConfig.getMaxQueuePerTopic();
		if (CollectionUtils.isEmpty(queueEntities)) {
			return;
		}
		if (queueEntities.size() >= maxQueue) {
			throw new CheckFailException("topic内队列数量达到上限,不允许扩容,可联系管理员强制扩容");
		}
	}

	private Long getQueueMessage(QueueEntity queueEntity) {
		message01Service.setDbId(queueEntity.getDbNodeId());
		Long maxId = message01Service.getMaxId(queueEntity.getTbName());
		Long minId = queueEntity.getMinId();
		return maxId - minId - 1;
	}

	public TopicGenerateTokenResponse generateToken(Long topicId) {
		String currentUserId = userInfoHolder.getUserId();
		TopicEntity topicEntity = baseCheckRequest(topicId, currentUserId);

		String token = DesUtil.getUuidToken();
		topicEntity.setToken(token);
		topicEntity.setUpdateBy(currentUserId);
		topicService.update(topicEntity);
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId, "生成token");
		return new TopicGenerateTokenResponse();
	}

	public TopicClearTokenResponse clearToken(Long topicId) {
		String currentUserId = userInfoHolder.getUserId();
		TopicEntity topicEntity = baseCheckRequest(topicId, currentUserId);
		String token = topicEntity.getToken();
		topicEntity.setToken("");
		topicEntity.setUpdateBy(currentUserId);
		topicService.update(topicEntity);
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId, "清除token:" + token);
		return new TopicClearTokenResponse();
	}

	private TopicEntity baseCheckRequest(Long topicId, String currentUserId) {
		TopicEntity topicEntity = topicService.get(topicId);
		if (topicEntity == null) {
			throw new CheckFailException("topic已经被删除,请刷新重试。 ");
		}
		if (!hasAuth(currentUserId, topicEntity)) {
			throw new AuthFailException("没有操作权限,请进行权限检查。");
		}
		return topicEntity;
	}

	public List<TopicEntity> getSelectSearch(String keyword, int offset, int limit, String consumerGroupName) {
		Map<String, ConsumerGroupEntity> consumerGroupEntityMap = consumerGroupService.getCache();
		Map<String, TopicEntity> topicMap = topicService.getCache();
		List<TopicEntity> topicInfos = new ArrayList<>();
		List<String> subscribedTopics = new ArrayList<>();
		// 该消费者组已经订阅过的topic
		ConsumerGroupEntity consumerGroupEntity = consumerGroupEntityMap.get(consumerGroupName);
		String topicNames = consumerGroupEntity.getTopicNames();
		if (StringUtils.isNotEmpty(topicNames)) {
			subscribedTopics = Arrays.asList(topicNames.split(","));
		}
		for (String key : topicMap.keySet()) {
			if (key.toLowerCase().startsWith(keyword.toLowerCase()) && topicMap.get(key).getTopicType() == 1) {
				// 排除已经订阅过的topic
				if (!subscribedTopics.contains(key)) {
					// 如果该topic允许所有人订阅,或者该消费者组拥有订阅该topic的权限
					if (topicMap.get(key).getConsumerFlag() == 1 || Arrays
							.asList(topicMap.get(key).getConsumerGroupNames().split(",")).contains(consumerGroupName)) {
						topicInfos.add(topicMap.get(key));
					}

				}
			}
			if (topicInfos.size() >= limit) {
				break;
			}
		}
		return topicInfos;
	}

	public TopicQueueRemoveListResponse queueRemoveList(Long topicId) {
		CacheUpdateHelper.updateCache();
		Transaction catTransaction = Tracer.newTransaction("UiTopicService", "all-process-queueRemoveList");
		try {
			Transaction catTransaction1 = Tracer.newTransaction("UiTopicService", "getQueuesByTopicId");
			List<QueueEntity> queueEntityList = queueService.getQueuesByTopicId(topicId);
			if (CollectionUtils.isEmpty(queueEntityList)) {
				return new TopicQueueRemoveListResponse(0L, null);
			}
			catTransaction1.setStatus(Transaction.SUCCESS);
			catTransaction1.complete();
			Long bestQueueId = 0L;
			if (queueEntityList.size() > 2) {
				Transaction catTransaction2 = Tracer.newTransaction("UiTopicService", "getBestRemoveQueue");
				List<QueueEntity> bestQueueList = uiQueueService.getBestRemoveQueue(topicId);
				catTransaction2.setStatus(Transaction.SUCCESS);
				catTransaction2.complete();
				Transaction catTransaction3 = Tracer.newTransaction("UiTopicService", "findMinLeftMessage");
				if (!CollectionUtils.isEmpty(bestQueueList)) {
					bestQueueId = findMinLeftMessage(bestQueueList).getId();
				} else {
					bestQueueId = findMinLeftMessage(queueEntityList).getId();
				}
				catTransaction3.setStatus(Transaction.SUCCESS);
				catTransaction3.complete();
			}
			Transaction catTransaction4 = Tracer.newTransaction("UiTopicService", "buildQueueRemoveInfoVoList");
			List<QueueRemoveInfoVo> queueRemoveInfoVoList = new ArrayList<>();
			for (QueueEntity queueEntity : queueEntityList) {
				QueueRemoveInfoVo queueRemoveInfoVo = new QueueRemoveInfoVo();
				queueRemoveInfoVo.setId(queueEntity.getId());
				queueRemoveInfoVo.setTopicId(topicId);
				queueRemoveInfoVo.setQueueReadOnly(queueEntity.getReadOnly());
				DbNodeEntity dbNodeEntity = dbNodeService.get(queueEntity.getDbNodeId());
				queueRemoveInfoVo.setDbReadOnly(dbNodeEntity.getReadOnly());
				if (dbNodeEntity.getReadOnly() != 1) {
					queueRemoveInfoVo.setReadStatus(dbNodeEntity.getReadOnly());
				} else {
					queueRemoveInfoVo.setReadStatus(queueEntity.getReadOnly());
				}
				queueRemoveInfoVo.setConsumerGroups(String.join(",", getConsumerGroups(queueEntity.getId())));
				queueRemoveInfoVo.setLeftMessage(getLeftMessage(queueEntity.getId()));
				queueRemoveInfoVo.setDbNodeId(queueEntity.getDbNodeId());
				if (queueEntity.getId() == bestQueueId) {
					queueRemoveInfoVo.setIsBestRemove(1);
				}
				queueRemoveInfoVoList.add(queueRemoveInfoVo);
			}
			catTransaction4.setStatus(Transaction.SUCCESS);
			catTransaction4.complete();
			catTransaction.setStatus(Transaction.SUCCESS);
			return new TopicQueueRemoveListResponse((long) queueRemoveInfoVoList.size(), queueRemoveInfoVoList);

		} catch (Exception e) {
			catTransaction.setStatus(e);
			throw e;
		} finally {
			catTransaction.complete();
		}

	}

	private QueueEntity findMinLeftMessage(List<QueueEntity> queueEntities) {
		QueueEntity queueEntity = null;
		Long minLeftMessage = 999999999999999999L;
		for (QueueEntity queueEntity1 : queueEntities) {
			Long currentLeftMessage = getLeftMessage(queueEntity1.getId());
			if (currentLeftMessage <= minLeftMessage) {
				minLeftMessage = currentLeftMessage;
				queueEntity = queueEntity1;
			}
		}
		return queueEntity;

	}

	private Long getLeftMessage(Long queueId) {
		List<QueueOffsetEntity> queueOffsetEntityList = uiQueueOffsetService.findByQueueId(queueId);
		if (CollectionUtils.isEmpty(queueOffsetEntityList)) {
			return 0L;
		}
		Long maxId = queueService.getMax().get(queueId);
		Long leftMessage = 0L;
		for (QueueOffsetEntity queueOffsetEntity : queueOffsetEntityList) {
			Long left = maxId - 1 - queueOffsetEntity.getOffset();
			leftMessage += left > 0 ? left : 0;
		}
		return leftMessage;
	}

	private List<String> getConsumerGroups(Long queueId) {
		List<QueueOffsetEntity> queueOffsetEntityList = uiQueueOffsetService.findByQueueId(queueId);
		List<String> consumerGroups = new ArrayList<>();
		queueOffsetEntityList
				.forEach(queueOffsetEntity -> consumerGroups.add(queueOffsetEntity.getConsumerGroupName()));
		return consumerGroups;
	}

	public TopicGetByIdResponse getById(Long topicId) {
		return new TopicGetByIdResponse(topicService.get(topicId));
	}

	public void updateSaveDayNum(Long topicId, int saveDayNum) {
		CacheUpdateHelper.updateCache();
		TopicEntity topicEntity = topicService.get(topicId);
		if (!hasAuth(userInfoHolder.getUserId(), topicEntity)) {
			throw new AuthFailException("没有操作权限,请进行权限检查。");
		}
		int oldSaveDayNum = topicEntity.getSaveDayNum();
		topicEntity.setSaveDayNum(saveDayNum);
		topicService.update(topicEntity);
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId,
				"更新[保留时间]: {" + oldSaveDayNum + "->" + saveDayNum + "}");
		// synService32.synTopicSaveDayNum32(topicEntity);
	}

	@Transactional(rollbackFor = Exception.class)
	public TopicQueueRemoveResponse queueRemove(Long queueId, Long topicId) {
		CacheUpdateHelper.updateCache();
		TopicEntity topicEntity = topicService.get(topicId);
		if (roleService.getRole(userInfoHolder.getUserId(), topicEntity.getOwnerIds()) > 0) {
			throw new AuthFailException("没有操作权限,请进行权限检查。");
		}

		Map<String, List<QueueVo>> queueVoMap = uiQueueService.getQueueListCount();
//		 如果queue下的消息量大于阈值,则不允许缩容
		if (queueVoMap != null) {
			List<QueueVo> queueList = queueVoMap.get(topicEntity.getName());
			if (queueList != null) {
				for (QueueVo queueVo : queueList) {
					if(queueId==queueVo.getId()&&queueVo.getMsgCount()>soaConfig.getTopicDeleteLimitCount() * 10000){
						throw new CheckFailException("topic:"+topicEntity.getName()+"存在消息量大于"+soaConfig.getTopicDeleteLimitCount() * 10000+"的queue,不能直接缩容");
					}
				}
			}

		}

		// List<QueueEntity> queueEntities = queueService.getQueuesByTopicId(topicId);
		uiQueueService.remove(queueId);
		consumerGroupService.notifyRb(uiConsumerGroupTopicService.findByTopicId(topicId).stream()
				.map(ConsumerGroupTopicEntity::getConsumerGroupId).collect(Collectors.toList()));
		// topic缩容时,同步到mq2.0
		// synService32.synQueueRemove(queueService.get(queueId), topicEntity);
		return new TopicQueueRemoveResponse();
	}

	public TopicManualExpandResponse manualExpand(Long topicId, Long queueId) {
		CacheUpdateHelper.updateCache();
		String currentUserId = userInfoHolder.getUserId();
		TopicEntity topicEntity = baseCheckRequest(topicId, currentUserId);

		QueueEntity queueEntity = queueService.get(queueId);
		if (topicEntity.getTopicType() != queueEntity.getNodeType()) {
			throw new CheckFailException("所选择的队列类型与主题不一致!请重新选择!");
		}
		if (queueEntity.getTopicId() != 0) {
			throw new CheckFailException("队列已经被分配!请重新选择!");
		}
		if (ReadWriteEnum.READ_WRITE.getCode() != dbNodeService.get(queueEntity.getDbNodeId()).getReadOnly()) {
			throw new CheckFailException("队列所在节点不可写,请重新选择!");
		}
		topicService.distributeQueue(topicEntity, queueEntity);
		uiQueueOffsetService.createQueueOffsetForExpand(queueEntity, topicId, topicEntity);
		uiQueueService.queueChangeRb(topicId);
		uiAuditLogService.recordAudit(TopicEntity.TABLE_NAME, topicId,
				"手工分配queue," + JsonUtil.toJson(queueEntity.getId()));

		return new TopicManualExpandResponse();
	}

	public TopicGetTopicNamesResponse getTopicNames(String keyword, int offset, int limit) {
		Map<String, TopicEntity> topicMap = topicService.getCache();
		List<String> topicList = new LinkedList<>();
		for (String topicName : topicMap.keySet()) {
			if (topicName.toLowerCase().startsWith(keyword.toLowerCase())) {
				topicList.add(topicName);
			}
		}

		if (offset + limit > topicList.size()) {
			limit = topicList.size() - offset;
		}
		return new TopicGetTopicNamesResponse(new Long(topicList.subList(offset, limit).size()),
				topicList.subList(offset, limit));

	}

	public TopicGetTopicNamesResponse getTopicNamesForMessageTool(String keyword, int offset, int limit) {
		Map<String, TopicEntity> topicMap = topicService.getCache();
		List<String> topicList = new LinkedList<>();
		for (String topicName : topicMap.keySet()) {
			// 如果是超级管理员或者apollo配置了允许所有人发送,则展示所有topic
			if (roleService.getRole(userInfoHolder.getUserId(), null) == 0
					|| ("0".equals(soaConfig.getToolTopicFilterFlag()))) {
				if (topicName.toLowerCase().startsWith(keyword.toLowerCase())) {
					topicList.add(topicName);
				}
			} else {
				// 否则只展示负责人名下的topic,即只允许负责人发送
				if (topicName.toLowerCase().startsWith(keyword.toLowerCase())
						&& topicMap.get(topicName).getOwnerIds().contains(userInfoHolder.getUserId())) {
					topicList.add(topicName);
				}
			}

		}

		if (offset + limit > topicList.size()) {
			limit = topicList.size() - offset;
		}
		return new TopicGetTopicNamesResponse(new Long(topicList.subList(offset, limit).size()),
				topicList.subList(offset, limit));

	}

	private volatile long lastAccessTime = System.currentTimeMillis() * 2;

	public TopicReportResponse getTopicReport(TopicGetListRequest topicGetListRequest) {
		lastAccessTime = System.currentTimeMillis();
		int page = Integer.parseInt(topicGetListRequest.getPage());
		int pageSize = Integer.parseInt(topicGetListRequest.getLimit());
		List<TopicVo> topicVoList = new ArrayList<>();

		Map<String, List<ConsumerGroupTopicEntity>> topicSubscribeMap = consumerGroupTopicService
				.getTopicSubscribeMap();
		for (TopicVo topicVo : topicVoListRf.get()) {
			if (StringUtils.isNotEmpty(topicGetListRequest.getName())) {
				if (!topicGetListRequest.getName().equals(topicVo.getName())) {
					continue;
				}
			}

			if (StringUtils.isNotEmpty(topicGetListRequest.getTopicExceptionType())) {
				// 过滤掉非僵尸topic
				if ("1".equals(topicGetListRequest.getTopicExceptionType())) {
					if (topicVo.getMsgCount() > 0) {// 过滤有消息的topic
						continue;
					}
					if (topicSubscribeMap.containsKey(topicVo.getOriginName())) {// 过滤已经被订阅的topic
						continue;
					}
					//排除创建时间不到三天的topic
					if (System.currentTimeMillis()-topicVo.getInsertTime().getTime() < 3 * 24 * 60 * 60
							* 1000) {
						continue;
					}
				}

				// 过滤掉负责人正常的topic
				if ("2".equals(topicGetListRequest.getTopicExceptionType())) {
					List<String> ownerIds = Arrays.asList(topicVo.getOwnerIds().split(","));
					if (uiQueueOffsetService.isOwnerAvailable(ownerIds)) {
						continue;
					}
				}

				//过滤掉已经被订阅的topic
				if("3".equals(topicGetListRequest.getTopicExceptionType())){
					if (topicSubscribeMap.containsKey(topicVo.getOriginName())) {// 过滤已经被订阅的topic
						continue;
					}
				}
			}

			if (StringUtils.isNotEmpty(topicGetListRequest.getQueueManagementType())) {
				if ("1".equals(topicGetListRequest.getQueueManagementType())) {
					if (!shouldShrink.equals(topicVo.getIsReasonable())) {
						continue;
					}
				}

				if ("2".equals(topicGetListRequest.getQueueManagementType())) {
					if (!shouldExpand.equals(topicVo.getIsReasonable())) {
						continue;
					}
				}
			}

			topicVoList.add(topicVo);
		}

		int t = topicVoList.size();
		if ((page * pageSize) > topicVoList.size()) {
			topicVoList = topicVoList.subList((page - 1) * pageSize, topicVoList.size());
		} else {
			topicVoList = topicVoList.subList((page - 1) * pageSize, page * pageSize);
		}
		return new TopicReportResponse(new Long(t), topicVoList);

	}

	public List<TopicVo> getTopicVos(){
		return topicVoListRf.get();
	}

	@Override
	@PreDestroy
	public void stop() {
		isRunning = false;
	}

	@Override
	public String info() {
		// TODO Auto-generated method stub
		return null;
	}

}