package com.ym.mec.biz.service.impl;

import com.alibaba.fastjson.JSON;
import com.ym.mec.auth.api.client.SysUserFeignService;
import com.ym.mec.auth.api.entity.SysUser;
import com.ym.mec.biz.dal.dao.SysConfigDao;
import com.ym.mec.biz.dal.dao.SysMessageDao;
import com.ym.mec.biz.dal.dao.WaitSendMessageDao;
import com.ym.mec.biz.dal.dto.Mapper;
import com.ym.mec.biz.dal.dto.SysMessageDto;
import com.ym.mec.biz.dal.entity.SysMessage;
import com.ym.mec.biz.dal.entity.SysMessageConfig;
import com.ym.mec.biz.dal.entity.WaitSendMessage;
import com.ym.mec.biz.dal.enums.MessageSendMode;
import com.ym.mec.biz.dal.enums.MessageTypeEnum;
import com.ym.mec.biz.dal.enums.SendStatusEnum;
import com.ym.mec.biz.service.SysMessageConfigService;
import com.ym.mec.biz.service.SysMessageService;
import com.ym.mec.common.dal.BaseDAO;
import com.ym.mec.common.entity.ImGroupMessage;
import com.ym.mec.common.entity.ImPlayMidiMessage;
import com.ym.mec.common.entity.ImPrivateMessage;
import com.ym.mec.common.entity.ImTxtMessage;
import com.ym.mec.common.exception.BizException;
import com.ym.mec.common.page.PageInfo;
import com.ym.mec.common.page.QueryInfo;
import com.ym.mec.common.redis.service.RedisCache;
import com.ym.mec.common.service.impl.BaseServiceImpl;
import com.ym.mec.im.ImFeignService;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext;
import com.ym.mec.thirdparty.message.MessageSenderPluginContext.MessageSender;
import com.ym.mec.util.collection.MapUtil;
import com.ym.mec.util.date.DateUtil;
import com.ym.mec.util.string.MessageFormatter;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.Map.Entry;

import static com.ym.mec.biz.dal.enums.MessageTypeEnum.STUDENT_PUSH_VIP_BUY;

@Service
public class SysMessageServiceImpl extends BaseServiceImpl<Long, SysMessage> implements SysMessageService {

	private final static Logger LOGGER = LoggerFactory.getLogger(SysMessageServiceImpl.class);

	@Autowired
	private SysMessageDao sysMessageDao;

	@Autowired
	private SysMessageConfigService sysMessageConfigService;

	@Autowired
	private MessageSenderPluginContext messageSenderPluginContext;

	@Autowired
	private SysUserFeignService sysUserFeignService;

	@Autowired
	private WaitSendMessageDao waitSendMessageDao;

	@Autowired
	private SysConfigDao sysConfigDao;

	@Autowired
	private ImFeignService imFeignService;

	@Autowired
	private RedisCache<String, Object> redisCache;

	// 验证码有效期
	public static final int CODE_EXPIRE = 60 * 5;

	// 发送验证码的间隔时间
	public static final int CODE_INTERVAL_TIME = 60;

	private final int DEFAULT_CODE = 888888;

	@Value("${message.debugMode}")
	private boolean debugMode;

	@Override
	public BaseDAO<Long, SysMessage> getDAO() {
		return sysMessageDao;
	}

	@Override
	public PageInfo<SysMessageDto> queryListPage(QueryInfo queryInfo) {
		PageInfo<SysMessageDto> pageInfo = new PageInfo<SysMessageDto>(queryInfo.getPage(), queryInfo.getRows());
		Map<String, Object> params = new HashMap<String, Object>();
		MapUtil.populateMap(params, queryInfo);
		
		List<SysMessageDto> dataList = null;
		int count = this.findCount(params);
		if (count > 0) {
			pageInfo.setTotal(count);
			params.put("offset", pageInfo.getOffset());
			dataList = sysMessageDao.queryListPage(params);
		}
		if (count == 0) {
			dataList = new ArrayList<SysMessageDto>();
		}
		pageInfo.setRows(dataList);
		return pageInfo;
	}

	/**
	 * 添加消息
	 * @param subject 消息主题
	 * @param content 消息内容
	 * @param receivers 接收人，多个人用逗号分开
	 * @param triggerTime 触发时间
	 * @param mode 消息的发送模式
	 * @param status 消息的当前状态
	 * @param errorMsg 错误消息
	 * @param readStatus 阅读状态
	 * @param memo 备注
	 * @param group 消息组
	 * @return
	 */
	private int addMessage(Map<Integer, String> receivers, String subject, String content, Date triggerTime, MessageSendMode mode, SendStatusEnum status,
			String errorMsg, Integer readStatus, String memo, String group,String jpushType) {
		SysMessage message = null;
		Date date = new Date();

		List<SysMessage> messages = new ArrayList<>();

		Integer userId = null;
		String receiver;
		for (Entry<Integer, String> entry : receivers.entrySet()) {
			userId = entry.getKey();
			receiver = entry.getValue();

			message = new SysMessage();
			if (userId == null) {
				SysUser user = sysUserFeignService.queryUserByMobile(receiver);
				if (user != null) {
					userId = user.getId();
				}
			}
			message.setUserId(userId);
			message.setContent(content);
			message.setCreateOn(date);
			message.setModifyOn(date);
			message.setReceiver(receiver);
			if (triggerTime == null) {
				triggerTime = date;
			}
			message.setSendTime(triggerTime);
			message.setTitle(subject);
			message.setStatus(status);
			message.setType(mode);
			message.setErrorMsg(errorMsg);
			message.setReadStatus(readStatus);
			message.setMemo(memo);
			message.setGroup(group);
			message.setJpushType(jpushType);
			messages.add(message);
		}

		return sysMessageDao.batchInsert(messages);
	}

	@Override
	public int updateMessage(SysMessage message) {
		return sysMessageDao.update(message);
	}

	@Override
	public boolean batchSeoMessage(Set<Integer> userIds, MessageTypeEnum messageType,String memo, Object... args) {
		if (userIds == null || userIds.size() == 0) {
			throw new BizException("接收地址不能为空");
		}
		SysMessageConfig messageConfig = sysMessageConfigService.queryByType(messageType);
		if (messageConfig == null) {
			throw new BizException("消息类型错误");
		}else if(messageConfig.getSendFlag() == null || messageConfig.getSendFlag() == 0){
			return true;
		}
		Date date = new Date();
		SendStatusEnum status = SendStatusEnum.SUCCESSED;
		String content = MessageFormatter.arrayFormat(messageConfig.getContent(), args);
		SysMessage message = null;

		List<SysMessage> messages = new ArrayList<>();
		for (Integer userId : userIds) {
			message = new SysMessage();
			message.setUserId(userId);
			message.setContent(content);
			message.setCreateOn(date);
			message.setModifyOn(date);
			message.setReceiver(userId.toString());
			message.setSendTime(date);
			message.setStatus(status);
			message.setType(MessageSendMode.SEO);
			message.setTitle(messageConfig.getDescription());
			message.setReadStatus(0);
			message.setMemo(memo);
			message.setGroup(messageConfig.getGroup());
			messages.add(message);
		}
		sysMessageDao.batchInsert(messages);
		return true;
	}

	@Override
	public void batchSendImMessage(MessageTypeEnum type, String senderId,String extra, String[] targetIds, String url, Object... args) {
		SysMessageConfig messageConfig = sysMessageConfigService.queryByType(type);
		if (messageConfig == null) {
			throw new BizException("消息类型错误");
		}else if(messageConfig.getSendFlag() == null || messageConfig.getSendFlag() == 0){
			return;
		}
		String content = MessageFormatter.arrayFormat(messageConfig.getContent(), args);
		if(StringUtils.isNotEmpty(url)){
			content += url;
		}
		ImPrivateMessage privateMessage = new ImPrivateMessage();
		privateMessage.setObjectName("RC:TxtMsg");
		privateMessage.setTargetId(targetIds);
		privateMessage.setSenderId(senderId);
		privateMessage.setContent(new ImTxtMessage(content,extra));
		imFeignService.privateSend(privateMessage);
	}

	@Override
	@Async
	public void batchSendImGroupMessage(MessageTypeEnum type, String senderId,String extra, String[] targetIds, String url, Object... args) {
		SysMessageConfig messageConfig = sysMessageConfigService.queryByType(type);
		if (messageConfig == null) {
			throw new BizException("消息类型错误");
		}else if(messageConfig.getSendFlag() == null || messageConfig.getSendFlag() == 0){
			return;
		}
		String content = MessageFormatter.arrayFormat(messageConfig.getContent(), args);
		if(StringUtils.isNotEmpty(url)){
			content += url;
		}
		ImGroupMessage groupMessage = new ImGroupMessage();
		groupMessage.setObjectName("RC:TxtMsg");
		groupMessage.setTargetId(targetIds);
		groupMessage.setSenderId(senderId);
		groupMessage.setContent(new ImTxtMessage(content,extra));
		imFeignService.groupSend(groupMessage);
	}

	@Override
	public void sendPrivateMessage(String studentId, String content) {
		SysUser sysUser = sysUserFeignService.queryUserInfo();
		if(sysUser == null){
			throw new BizException("用户信息获取失败");
		}
		ImPrivateMessage privateMessage = new ImPrivateMessage();
		privateMessage.setObjectName("RC:TxtMsg");
		privateMessage.setTargetId(studentId.split(","));
		privateMessage.setSenderId(sysUser.getId().toString());
		privateMessage.setContent(new ImTxtMessage(content,null));
		imFeignService.privateSend(privateMessage);
	}

	@Override
	public void sendNoAuthPrivateMessage(String sender, String receiver, String content) {
		ImPrivateMessage privateMessage = new ImPrivateMessage();
		privateMessage.setObjectName("RC:TxtMsg");
		privateMessage.setTargetId(receiver.split(","));
		privateMessage.setSenderId(sender);
		privateMessage.setContent(new ImTxtMessage(content,null));
		imFeignService.privateSend(privateMessage);
	}

	@Override
	public void pushWaitSendMessageTask() {
		List<WaitSendMessage> all = waitSendMessageDao.findAll(null);
		for (WaitSendMessage waitSendMessage : all) {
			Map<Integer,String> map = (Map<Integer,String>) JSON.parseObject(waitSendMessage.getReceivers(), Map.class);
			String[] args = null;
			if(waitSendMessage.getAgrs() != null){
				List<String> strings = JSON.parseArray(waitSendMessage.getAgrs(), String.class);
				String[] str = new String[strings.size()];
				args = strings.toArray(str);
			}
			batchSendMessage(MessageSender.valueOf(waitSendMessage.getMessageSender()),
					MessageTypeEnum.valueOf(waitSendMessage.getMessageType()),map,null,0,
					waitSendMessage.getUrl(),waitSendMessage.getJpushType(),args);
			waitSendMessageDao.delete(waitSendMessage.getId());
		}
	}

	@Override
	@Async
	public void batchSendMessage(MessageSender messageSender, MessageTypeEnum type, Map<Integer, String> receivers, Date triggerTime, Integer readStatus,
			String url,String jpushType, Object... args) {
		if (receivers == null || receivers.size() == 0) {
			throw new BizException("接收地址不能为空");
		}
		if(StringUtils.isNotEmpty(jpushType) && jpushType == "STUDENT"){
			//如果不是缴费信息
			if(type != STUDENT_PUSH_VIP_BUY){
				int hour = DateUtil.getHour(new Date());
				//如果当前时间在22点之后
				String messageTime = sysConfigDao.findConfigValue("wait_send_message_time");
				if(StringUtils.isEmpty(messageTime)){
					messageTime = "22";
				}
				if(hour > Integer.parseInt(messageTime)-1){
					//保存推送信息
					WaitSendMessage waitSendMessage = new WaitSendMessage();
					if(args != null){
						waitSendMessage.setAgrs(JSON.toJSONString(args));
					}
					waitSendMessage.setJpushType(jpushType);
					waitSendMessage.setMessageSender(messageSender.name());
					waitSendMessage.setMessageType(type.name());
					waitSendMessage.setReceivers(JSON.toJSONString(receivers));
					waitSendMessage.setUrl(url);
					waitSendMessageDao.insert(waitSendMessage);
					return;
				}
			}
		}
		String[] tos = receivers.values().toArray(new String[receivers.size()]);
		SysMessageConfig messageConfig = sysMessageConfigService.queryByType(type);
		if (messageConfig == null) {
			throw new BizException("消息类型错误");
		}else if(messageConfig.getSendFlag() == null || messageConfig.getSendFlag() == 0){
			return;
		}
		Date date = new Date();
		SendStatusEnum status = SendStatusEnum.WAIT;
		String errorMsg = null;
		// 立即发送
		if (triggerTime == null || date.after(triggerTime)) {
			status = SendStatusEnum.SENDING;
			try {
				if (debugMode == true
						|| messageSenderPluginContext.batchSend(messageSender, messageConfig.getDescription(),
								MessageFormatter.arrayFormat(messageConfig.getContent(), args), tos, url,jpushType)) {
					status = SendStatusEnum.SUCCESSED;
				} else {
					status = SendStatusEnum.FAILED;
				}
			} catch (Exception e) {
				status = SendStatusEnum.FAILED;
				errorMsg = e.getMessage();
				LOGGER.warn("消息发送失败", e);
			}
		}
		MessageSendMode mode = MessageSendMode.SMS;
		if (messageSender.getSendMode().equals("PUSH")) {
			mode = MessageSendMode.PUSH;
		} else if (messageSender.getSendMode().equals("EMAIL")) {
			mode = MessageSendMode.EMAIL;
		}
		addMessage(receivers, messageConfig.getDescription(), MessageFormatter.arrayFormat(messageConfig.getContent(), args), triggerTime, mode, status, errorMsg, readStatus,
				url, messageConfig.getGroup(),jpushType);
	}

	@Override
	@Async
	public void sendMessage(MessageSender messageSender, Integer userId, String title, String content, String receiver, Date triggerTime, Integer readStatus,
			String url, String group,String jpushType) {
		if (StringUtils.isBlank(receiver)) {
			throw new BizException("接收地址不能为空");
		}
		Date date = new Date();
		SendStatusEnum status = SendStatusEnum.WAIT;
		String errorMsg = null;
		// 立即发送
		if (triggerTime == null || date.after(triggerTime)) {
			status = SendStatusEnum.SENDING;
			try {
				if (debugMode == true || messageSenderPluginContext.send(messageSender, receiver, title, content, url,jpushType)) {
					status = SendStatusEnum.SUCCESSED;
				} else {
					status = SendStatusEnum.FAILED;
				}
			} catch (Exception e) {
				status = SendStatusEnum.FAILED;
				errorMsg = e.getMessage();
				LOGGER.warn("消息发送失败", e);
			}
		}
		MessageSendMode mode = MessageSendMode.SMS;
		if (messageSender.getSendMode().equals("PUSH")) {
			mode = MessageSendMode.PUSH;
		} else if (messageSender.getSendMode().equals("EMAIL")) {
			mode = MessageSendMode.EMAIL;
		}
		Map<Integer, String> receivers = new HashMap<Integer, String>();
		receivers.put(userId, receiver);
		addMessage(receivers, title, content, triggerTime, mode, status, errorMsg, readStatus, url, null,jpushType);
	}

	@Override
	public boolean sendSecurityCode(MessageSender messageSender, Integer userId, MessageTypeEnum messageType, String receiver) {
		String key1 = getVerificationCode1CacheKey(messageType, receiver);
		if (redisCache.get(key1) != null) {
			throw new BizException("请勿频繁操作，获取验证码间隔时间为60秒");
		}

		String key = getVerificationCodeCacheKey(messageType, receiver);
		int code = DEFAULT_CODE;
		if (debugMode == false) {
			code = getRandomCode(messageType, receiver);
		}
		Map<Integer, String> receivers = new HashMap<>(1);
		receivers.put(userId, receiver);
		batchSendMessage(messageSender, messageType, receivers, null, 1, "","", code);
		redisCache.put(key, code + "", CODE_EXPIRE);
		redisCache.put(key1, code + "", CODE_INTERVAL_TIME);
		return true;
	}

	@Override
	public String getSendedVerificationCode(MessageTypeEnum type, String mobileNOOrEmailAddr) {
		if (debugMode == true) {
			return DEFAULT_CODE + "";
		}
		String key = getVerificationCodeCacheKey(type, mobileNOOrEmailAddr);
		Object object = redisCache.get(key);
		return object == null ? null : object.toString();
	}

	@Override
	public void delSendedVerificationCode(MessageTypeEnum type, String mobileNOOrEmailAddr) {
		String key = getVerificationCodeCacheKey(type, mobileNOOrEmailAddr);
		if (StringUtils.isNotBlank(key)) {
			redisCache.delete(key);
		}
	}

	@Override
	public Map<String, Integer> queryCountOfUnread(MessageSendMode type, Integer userId,String jpushType) {
		List<Mapper> mappers = sysMessageDao.queryCountOfUnread(type, userId,jpushType);

		Map<String, Integer> map = new HashMap<String, Integer>();

		for (Mapper mapper : mappers) {
			map.put(mapper.getKey().toString(), Integer.parseInt(mapper.getValue().toString()));
		}

		return map;
	}

	public void setDebugMode(boolean debugMode) {
		this.debugMode = debugMode;
	}

	private int getRandomCode(MessageTypeEnum type, String mobileNo) {
		int min = 100000;
		int max = 999999;
		Random random = new Random();

		return random.nextInt(max) % (max - min + 1) + min;
	}

	private static String getVerificationCodeCacheKey(MessageTypeEnum type, String mobileNOOrEmailAddr) {
		return "verificationCode" + type.name() + mobileNOOrEmailAddr;
	}

	private static String getVerificationCode1CacheKey(MessageTypeEnum type, String mobileNOOrEmailAddr) {
		return "verificationCode" + mobileNOOrEmailAddr;
	}

	/**
	 *
	 * @param userId 用户id数组
	 * @param status 状态/1 已读,0未读
	 * @return 修改的条数
	 */
	@Override
	public int updateStatus(Integer userId, int status,String jpushType) {
		return sysMessageDao.updateStatus(userId, status,jpushType);
	}

	@Override
	public int updateOneStatus(Long id, int status) {
		return sysMessageDao.updateOneStatus(id, status);
	}

	@Override
	public List<SysMessage> queryUserInRecentMinList(String mobile, int recentMin, MessageSendMode type) {
		return sysMessageDao.queryUserInRecentMinList(mobile, recentMin, type);
	}
}
