package com.yonge.cooleshow.biz.dal.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.yonge.cooleshow.biz.dal.dto.JumpUrlDto;
import com.yonge.toolset.base.string.MessageFormatter;
import com.yonge.toolset.mybatis.service.impl.BaseServiceImpl;

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.stereotype.Service;

import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
import com.yonge.cooleshow.auth.api.entity.SysUser;
import com.yonge.cooleshow.biz.dal.dao.SysConfigDao;
import com.yonge.cooleshow.biz.dal.dao.SysMessageDao;
import com.yonge.cooleshow.biz.dal.dto.SysMessageDto;
import com.yonge.cooleshow.biz.dal.entity.SysMessage;
import com.yonge.cooleshow.biz.dal.entity.SysMessageConfig;
import com.yonge.cooleshow.biz.dal.enums.MessageSendMode;
import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
import com.yonge.cooleshow.biz.dal.enums.SendStatusEnum;
import com.yonge.cooleshow.biz.dal.service.SysMessageConfigService;
import com.yonge.cooleshow.biz.dal.service.SysMessageService;
import com.yonge.toolset.mybatis.dal.BaseDAO;
import com.yonge.cooleshow.common.entity.Mapper;
import com.yonge.toolset.base.exception.BizException;
import com.yonge.toolset.base.page.PageInfo;
import com.yonge.toolset.base.page.QueryInfo;
import com.yonge.cooleshow.common.redis.service.RedisCache;
import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext.MessageSender;
import com.yonge.toolset.utils.collection.MapUtil;

import org.springframework.transaction.annotation.Transactional;

@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 SysConfigDao sysConfigDao;
	@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 = 666666;

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


	@Value("${message.debugJigaung:false}")
	private boolean debugJigaung;

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

	@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<Long, String> receivers, String subject, String content, Date triggerTime, MessageSendMode mode, SendStatusEnum status,
			String errorMsg, Integer readStatus, String memo, String group,String jpushType,Long messageConfigId) {
		SysMessage message = null;
		Date date = new Date();

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

		Long userId = null;
		String receiver;
		for (Entry<Long, 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.setClientId(jpushType);
			message.setMessageConfigId(messageConfigId);
			messages.add(message);
		}

		return sysMessageDao.batchInsert(messages);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public int updateMessage(SysMessage message) {
		return sysMessageDao.update(message);
	}

	@Override
	public boolean batchSeoMessage(Set<Long> userIds, MessageTypeEnum messageType,String memo, Object... args) {
		if(userIds != null){
			userIds.removeAll(Collections.singleton(null));
		}
		if (userIds == null || userIds.size() == 0) {
			LOGGER.error("接收地址不能为空");
			return false;
		}
		SysMessageConfig messageConfig = sysMessageConfigService.queryByType(messageType.getCode());
		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 (Long 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 batchSendMessage(MessageSender messageSender, MessageTypeEnum type, Map<Long, String> receivers, Date triggerTime, Integer readStatus,
			String url,String jpushType, Object... args) {
		LOGGER.info("batchSendMessage msgSender {}, type {}, receivers {} ", messageSender, type, receivers);
		if (receivers == null || receivers.size() == 0) {
			LOGGER.error("接收地址不能为空");
			return;
		}
		if (StringUtils.isEmpty(url)) {
			url = selectConfigUrl(type.getCode());
		}

		String[] tos;
		if(messageSender.getSendMode().equals(MessageSender.JIGUANG.getSendMode())) {
			tos= new String[receivers.size()];
			tos = receivers.entrySet()
						   .stream()
						   .map(longStringEntry -> String.valueOf(longStringEntry.getKey()))
						   .collect(Collectors.toList())
						   .toArray(tos);
		} else {
			tos = receivers.values().toArray(new String[receivers.size()]);
		}
		SysMessageConfig messageConfig = sysMessageConfigService.queryByType(type.getCode());
		if (messageConfig == null) {
			throw new BizException("消息类型错误");
		}else if(messageConfig.getSendFlag() == null || messageConfig.getSendFlag() == 0){
			LOGGER.error("batch 发送标志为空" );
			return;
		}
		Date date = new Date();
		SendStatusEnum status = SendStatusEnum.WAIT;
		String errorMsg = null;
		// 立即发送
		if (triggerTime == null || date.after(triggerTime)) {
			status = SendStatusEnum.SENDING;
			try {

				if (isSendRemoteMessage(messageSender)) {
					errorMsg = messageSenderPluginContext.batchSend(messageSender, messageConfig.getDescription(),
														 MessageFormatter.arrayFormat(messageConfig.getContent(), args), tos, url,jpushType,"default",null);
					if (StringUtils.isEmpty(errorMsg)) {
						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,messageConfig.getId());
	}

	@Override
	public void batchSendMessage(MessageSender messageSender, String title, String content, Map<Long, String> receivers, Date triggerTime, Integer readStatus,
			String url, String jpushType, Object... args) {
		if (receivers == null || receivers.size() == 0) {
			LOGGER.error("接收地址不能为空");
			return;
		}

		String[] tos;
		if (messageSender.getSendMode().equals(MessageSender.JIGUANG.getSendMode())) {
			tos = new String[receivers.size()];
			tos = receivers.entrySet().stream().map(longStringEntry -> String.valueOf(longStringEntry.getKey())).collect(Collectors.toList()).toArray(tos);
		} else {
			tos = receivers.values().toArray(new String[receivers.size()]);
		}
		Date date = new Date();
		SendStatusEnum status = SendStatusEnum.WAIT;
		String errorMsg = null;
		// 立即发送
		if (triggerTime == null || date.after(triggerTime)) {
			status = SendStatusEnum.SENDING;
			try {

				if (isSendRemoteMessage(messageSender)) {
					errorMsg = messageSenderPluginContext.batchSend(messageSender, title, MessageFormatter.arrayFormat(content, args), tos, url, jpushType,
							"default", null);
					if (StringUtils.isEmpty(errorMsg)) {
						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, title, MessageFormatter.arrayFormat(content, args), triggerTime, mode, status, errorMsg, readStatus, url, "SYSTEM", jpushType,
				null);
	}

	private boolean isSendRemoteMessage(MessageSender messageSender) {
		if (messageSender.getSendMode().equals(MessageSender.JIGUANG.getSendMode()) && !debugJigaung) {
			return true;
		} else if (messageSender.getSendMode().equals(MessageSender.AWSMS.getSendMode()) && !debugMode) {
			return true;
		}
		return false;
	}

	@Override
	public void batchPushMessage(MessageTypeEnum type, Map<Long, String> receivers, Date triggerTime, Integer readStatus,
			String url,String jpushType,String sound,String channelId, Object... args) {
		if (receivers == null || receivers.size() == 0) {
			throw new BizException("接收地址不能为空");
		}
		String[] tos = receivers.values().toArray(new String[receivers.size()]);
		SysMessageConfig messageConfig = sysMessageConfigService.queryByType(type.getCode());
		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 (isSendRemoteMessage(MessageSender.JIGUANG)) {
					errorMsg = messageSenderPluginContext.batchSend(MessageSender.JIGUANG, messageConfig.getDescription(),
														 MessageFormatter.arrayFormat(messageConfig.getContent(), args), tos, url,jpushType,sound,channelId);
					if (StringUtils.isEmpty(errorMsg)) {
						status = SendStatusEnum.SUCCESSED;
					}  else {
						status = SendStatusEnum.FAILED;
					}
				}
			} catch (Exception e) {
				status = SendStatusEnum.FAILED;
				errorMsg = e.getMessage();
				LOGGER.warn("消息发送失败", e);
			}
		}
		addMessage(receivers, messageConfig.getDescription(), MessageFormatter.arrayFormat(messageConfig.getContent(), args), triggerTime, MessageSendMode.PUSH, status, errorMsg, readStatus,
				url, messageConfig.getGroup(),jpushType,messageConfig.getId());
	}

	@Override
	public void sendMessage(MessageSender messageSender, Long userId, String title, String content, String receiver, Date triggerTime, Integer readStatus, String url, String group,String jpushType) {
		LOGGER.info("batchSendMessage {}, userId {}, receivers {} ", messageSender, userId, receiver);
		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 (isSendRemoteMessage(messageSender)) {
					errorMsg = messageSenderPluginContext.send(messageSender, receiver, title, content, url,
																	  jpushType, "default", null);
					if (StringUtils.isEmpty(errorMsg)) {
						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;
			LOGGER.info("发送邮件 {} ");
		}
		Map<Long, String> receivers = new HashMap<Long, String>();
		receivers.put(userId, receiver);
		addMessage(receivers, title, content, triggerTime, mode, status, errorMsg, readStatus, url, null,jpushType,null);
	}

	@Override
	public boolean sendSecurityCode(MessageSender messageSender, Long userId, MessageTypeEnum messageType, String receiver, String platform) {
		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 && !authCode) {
			code = getRandomCode(messageType, receiver);
		}
		Map<Long, String> receivers = new HashMap<>(1);
		receivers.put(userId, receiver);
		batchSendMessage(messageSender, messageType, receivers, null, 1, "","",platform, 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 List<Mapper> queryCountOfUnread(MessageSendMode type, Long userId, String jpushType) {
		return sysMessageDao.queryCountOfUnread(type, userId, jpushType);


	}

	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
	@Transactional(rollbackFor = Exception.class)
	public int updateStatus(Long userId, int status,String jpushType) {
		return sysMessageDao.updateStatus(userId, status,jpushType);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	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);
	}

	@Override
	public String selectConfigUrl(String messageType,Object ...param) {
		SysMessageConfig sysMessageConfig = sysMessageConfigService.queryByType(messageType);
		if (sysMessageConfig == null) {
			throw new BizException("消息类型错误");
		}
		if(StringUtils.isEmpty(sysMessageConfig.getAction())) {
			return null;
		}
		if (sysMessageConfig.getAction().equals("sms")) {
			return sysMessageConfig.getActionUrl();
		} else if (sysMessageConfig.getAction().equals("h5")) {
			sysMessageConfig.setActionUrl(MessageFormatter.arrayFormat(sysMessageConfig.getActionUrl(), param));
		} else if (sysMessageConfig.getAction().equals("app")) {
			sysMessageConfig.setParams(MessageFormatter.arrayFormat(sysMessageConfig.getParams(), param));
		}
		JumpUrlDto jumpUrlDto = new JumpUrlDto();
		jumpUrlDto.setAction(sysMessageConfig.getAction());
		jumpUrlDto.setUrl(sysMessageConfig.getActionUrl());
		jumpUrlDto.setPageTag(sysMessageConfig.getPageTag());
		jumpUrlDto.setParams(sysMessageConfig.getParams());
		return JSON.toJSONString(jumpUrlDto);
	}
}
