package com.cyksj.service.corp.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cyksj.common.constant.Constant;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.snowflake.Sequence;
import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
import com.cyksj.common.util.Jsons;
import com.cyksj.common.util.StringUtil;
import com.cyksj.dto.XxlJobResult;
import com.cyksj.mapper.corp.*;
import com.cyksj.mapper.manage.coupon.CouponMapper;
import com.cyksj.model.dto.CornMetaDto;
import com.cyksj.model.dto.CorpUserFollowRelationDto;
import com.cyksj.model.entity.*;
import com.cyksj.model.request.CorpActiveBulkMsg;
import com.cyksj.model.request.corp.Attachments;
import com.cyksj.model.request.corp.CorpMsgTemplate;
import com.cyksj.model.request.corp.Text;
import com.cyksj.model.response.corp.CorpMsgTemplateAddResult;
import com.cyksj.redis.RedisService;
import com.cyksj.service.corp.CorpMsgBulkTaskService;
import com.cyksj.service.corp.CorpService;
import com.cyksj.service.corp.CorpUserSendRecordService;
import com.cyksj.service.corp.WxCorpOps;
import com.cyksj.service.sys.SysConfigService;
import com.cyksj.xxljob.XxlJobInfo;
import com.cyksj.xxljob.XxlJobUtil;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/*
 *项目名: netflix
 *文件名: CorpMsgBulkTaskServiceImpl
 *创建者: JavaZou
 *创建时间:2023/6/21 10:24
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class CorpMsgBulkTaskServiceImpl extends ServiceImpl<CorpMsgBulkTaskMapper, CorpMsgBulkTask> implements CorpMsgBulkTaskService {

	private final WxCorpOps corpOps;

	private final CorpMsgBulkTaskMapper corpMsgBulkTaskMapper;

	private final CorpUserFollowRelationMapper corpUserFollowRelationMapper;

	private final WxCorpAppMapper wxCorpAppMapper;

	private final CorpService corpService;

	private final CorpMsgBulkTaskRecordMapper corpMsgBulkTaskRecordMapper;

	private final CorpMsgMapper corpMsgMapper;

	private final CorpUserSendRecordService corpUserSendRecordService;

	private final CorpTagMapper corpTagMapper;

	private final CouponMapper couponMapper;

	private final CorpMsgBulkTaskTagRelationMapper corpMsgBulkTaskTagRelationMapper;

	private final SysConfigService sysConfigService;

	private final RedisService redisService;

	private final int LIMIT = 10000;

	private static final Sequence SEQUENCE = new Sequence(0);

	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();

	@Override
	public void saveOrUpdateMsgBulkTask(CorpMsgBulkTask corpMsgBulkTask) throws Exception {
		if (corpMsgBulkTask.getWxCorpId() == null) corpMsgBulkTask.setWxCorpId(2l);
		if (CollUtil.isNotEmpty(corpMsgBulkTask.getAttachments())) {
			corpMsgBulkTask.setAttachmentsStr(Jsons.toJson(corpMsgBulkTask.getAttachments()));
		} else {
			corpMsgBulkTask.setAttachmentsStr(null);
		}
		setCorn(corpMsgBulkTask);
		if (corpMsgBulkTask.getId() != null) {
			CorpMsgBulkTask dbCorpMsgBulkTask = corpMsgBulkTaskMapper.selectById(corpMsgBulkTask.getId());
			if (StrUtil.isNotEmpty(dbCorpMsgBulkTask.getJobId()) && corpMsgBulkTask.getSendTimeType() == 1) {
				//停止群发任务
				XxlJobUtil.stopJob(dbCorpMsgBulkTask.getJobId());
			}
		}
		Optional.ofNullable(corpMsgBulkTask.getSendScope()).ifPresent(sendScope -> {
			//发送全部用户范围
			if (sendScope == 1) {
				if (StrUtil.isEmpty(corpMsgBulkTask.getChooseTagStr())) {
					corpMsgBulkTask.setChooseTagStr(null);
					corpMsgBulkTask.setTagGroups(null);
				}
			} else if (StrUtil.isEmpty(corpMsgBulkTask.getChooseTagStr())) {
				throw BusinessRuntimeException.getInstance("标签筛选发送范围下,标签不能为空");
			}
		});
		if (StrUtil.isAllEmpty(corpMsgBulkTask.getText(), corpMsgBulkTask.getAttachmentsStr())) {
			throw BusinessRuntimeException.getInstance("群发消息文本和附件不能同时为空");
		}
		this.saveOrUpdate(corpMsgBulkTask);
		//处理群发任务和标签的关系
		handleTaskTagRelation(corpMsgBulkTask);
		//立即群发
		if (corpMsgBulkTask.getSendTimeType() == 1) {
			// 去群发
			TASK_EXECUTOR.execute(() -> {
				addMsgTemplate(corpMsgBulkTask);
			});
			return;
		}
		if (StrUtil.isEmpty(corpMsgBulkTask.getJobId())) {
			// 定时发送
			XxlJobResult xxlJobResult = XxlJobUtil.addJob(XxlJobInfo.getTaskInstance(0, corpMsgBulkTask.getCorn(), "").toString());
			corpMsgBulkTask.setJobId(xxlJobResult.getContent());
			this.updateById(corpMsgBulkTask);
		}
		XxlJobUtil.updateJob(XxlJobInfo.getTaskInstance(Integer.valueOf(corpMsgBulkTask.getJobId()), corpMsgBulkTask.getCorn(), corpMsgBulkTask.getId().toString()).toString());
	}

	@Override
	public void remindBulkTask(String msgid) {
		CorpMsgBulkTaskRecord corpMsgBulkTaskRecord = corpMsgBulkTaskRecordMapper.selectOne(Wrappers.lambdaQuery(CorpMsgBulkTaskRecord.class)
				.eq(CorpMsgBulkTaskRecord::getMsgid, msgid).last("limit 1"));
		if (corpMsgBulkTaskRecord == null) return;
		if (corpMsgBulkTaskRecord.getStatus()) return;
		WxCorpApp wxCorpApp = wxCorpAppMapper.selectById(corpMsgBulkTaskRecord.getCorpId());
		try {
			corpOps.remindBulkTask(msgid, wxCorpApp.getCorpId());
		} catch (Exception e) {
			log.error("提醒群发任务:{}失败:{}", msgid, StringUtil.getErrorText(e));
		}
	}

	@Override
	public Map<String, List<CorpTag>> getTagDetail(Long id) throws Exception {
		CorpMsgBulkTask task = this.getById(id);
		Assert.notNull(task, "群发任务不存在");
		List<CorpTag> corpTags = corpTagMapper.selectList(Wrappers.lambdaQuery(CorpTag.class)
				.in(CorpTag::getTagId, Jsons.parseList(task.getChooseTagStr(), String.class))
				.orderByAsc(CorpTag::getGroupId));
		Map<String, List<CorpTag>> collect = corpTags.stream().collect(Collectors.groupingBy(CorpTag::getGroupName));
		return collect;
	}

	@Override
	public void executeDynamicRuleTagTask(Long tagId) throws Exception {
		CorpTag corpTag = corpTagMapper.selectById(tagId);
		Assert.notNull(corpTag, "标签不存在");
		if (corpTag.getType() != 2) {
			Assert.notNull(corpTag, "非动态标签");
		}
		//执行动态标签标记任务id
		int taskId = 100;
		if (!redisService.setNx(RedisService.key.CORP_DYNAMIC_RULE_TAG_KEY.getName() + tagId, tagId, RedisService.key.CORP_DYNAMIC_RULE_TAG_KEY.getTimeout())) {
			throw BusinessRuntimeException.getInstance("该动态标签标记正在执行中..");
		}
		XxlJobResult xxlJobResult = XxlJobUtil.executeMarkDynamicRuleTagJob(XxlJobInfo.getExecuteTaskInstance(taskId, String.valueOf(tagId)).toString());
	}

	@Override
	public void sendActiveCorpBulkMsg(CorpActiveBulkMsg msg) {
		String content = msg.getContent();
		if (StrUtil.isEmpty(content)) {
			throw BusinessRuntimeException.getInstance("群发内容为空");
		}
		String img = msg.getImg();
		Long wxCorpId = Optional.ofNullable(msg.getWxCorpId()).orElse(2l);
		WxCorpApp wxCorpApp = wxCorpAppMapper.selectById(wxCorpId);
		Assert.notNull(wxCorpApp, "企业微信未配置");
		//客服集合
		List<String> service_follow_ids = null;
		SysConfig serviceConfig = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class)
				.eq(SysConfig::getSysKey, Constant.CORP_SERVICE_FOLLOWS_KEY).last("limit 1"));
		if (serviceConfig != null && StrUtil.isNotBlank(serviceConfig.getSysValue())) {
			try {
				service_follow_ids = Jsons.parseList(serviceConfig.getSysValue(), String.class);
			} catch (Exception e) {
			}
		}
		List<CorpUserFollowRelationDto> corpUserFollowRelations = corpUserFollowRelationMapper.selectCorpUserFollowRelation(wxCorpApp.getId(), service_follow_ids);
		//将成员与客户相同关联聚合
		Map<String, List<CorpUserFollowRelationDto>> collect = corpUserFollowRelations.stream().collect(Collectors.groupingBy(CorpUserFollowRelationDto::getFollowId));
		//附件
		LinkedList<Attachments> attachments = null;
		if (StrUtil.isNotEmpty(img)) {
			attachments = new LinkedList<>();
			Attachments attachment = new Attachments();
			Attachments.Image image = new Attachments.Image();
			image.setPicUrl(img);
			attachment.setImage(image);
			attachments.add(corpService.updateMaterial(attachment, wxCorpApp.getCorpId(), 1));
		}
		CorpMsgTemplate corpMsgTemplate = new CorpMsgTemplate();
		corpMsgTemplate.setAttachments(attachments);
		corpMsgTemplate.setChatType("single");
		if (StringUtils.isNotBlank(content)) {
			Text text = new Text();
			text.setContent(content);
			corpMsgTemplate.setText(text);
		}
		collect.forEach((followId, val) -> {
			try {
				List<String> extendIds = new ArrayList<>(val.size());
				List<Long> userIds = null;
				for (CorpUserFollowRelationDto dto : val) {
					extendIds.add(dto.getCorpUserId());
					if (userIds != null && dto.getUserId() != null) {
						userIds.add(dto.getUserId());
					}
				}
				List<List<String>> split = CollectionUtil.split(extendIds, LIMIT);
				for (List<String> corpUserIdList : split) {
					corpMsgTemplate.setSender(followId);
					corpMsgTemplate.setExternalUserid(corpUserIdList);
					CorpMsgTemplateAddResult result = corpOps.addMsgTemplate(wxCorpApp.getCorpId(), corpMsgTemplate);
				}
			} catch (Exception e) {
				log.error("发送企业微信失败:{}", StringUtil.getErrorText(e));
			}
		});
	}

	/**
	 * 创建企业群发
	 */
	@SneakyThrows
	public void addMsgTemplate(CorpMsgBulkTask corpMsgBulkTask) {
		//获取对应标签的客户
		WxCorpApp wxCorpApp = wxCorpAppMapper.selectById(corpMsgBulkTask.getWxCorpId());
		Assert.notNull(wxCorpApp, "企业微信未配置");
		List<String> tagIds = null;
		if (StrUtil.isNotBlank(corpMsgBulkTask.getChooseTagStr())) {
			tagIds = Jsons.parseList(corpMsgBulkTask.getChooseTagStr(), String.class);
		}
		//客服集合
		List<String> service_follow_ids = null;
		SysConfig serviceConfig = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class)
				.eq(SysConfig::getSysKey, Constant.CORP_SERVICE_FOLLOWS_KEY).last("limit 1"));
		if (serviceConfig != null && StrUtil.isNotBlank(serviceConfig.getSysValue())) {
			try {
				service_follow_ids = Jsons.parseList(serviceConfig.getSysValue(), String.class);
			} catch (Exception e) {
			}
		}
		Integer userScope = corpMsgBulkTask.getUserScope();

		Integer sendScope = corpMsgBulkTask.getSendScope();

		Integer consumerScope = corpMsgBulkTask.getConsumerScope();
		Date joinStartTime = corpMsgBulkTask.getJoinStartTime();
		Date joinEndTime = corpMsgBulkTask.getJoinEndTime();

		List<CorpUserFollowRelationDto> corpUserFollowRelations = null;
		//全部用户
		if (sendScope == 1) {
			//此时tagIds是 需要过滤的用户标签
			corpUserFollowRelations = corpUserFollowRelationMapper.selectCorpUserNotUnderTagIds(tagIds, wxCorpApp.getId(), service_follow_ids, userScope, consumerScope, joinStartTime, joinEndTime);
		} else {
			corpUserFollowRelations = corpUserFollowRelationMapper.selectCorpUserFollowRelationByTagIds(tagIds, wxCorpApp.getId(), service_follow_ids, userScope, consumerScope, joinStartTime, joinEndTime);
		}
		//将成员与客户相同关联聚合
		Map<String, List<CorpUserFollowRelationDto>> userRelationMap = corpUserFollowRelations.stream().collect(Collectors.groupingBy(CorpUserFollowRelationDto::getFollowId));
		//附件
		LinkedList<Attachments> attachments = null;
		if (CollectionUtil.isNotEmpty(corpMsgBulkTask.getAttachments())) {
			attachments = new LinkedList<>();
			for (Attachments attachment : corpMsgBulkTask.getAttachments()) {
				attachments.add(corpService.updateMaterial(attachment, wxCorpApp.getCorpId(), 1));

			}
		}
		sendToMsgTemplate(userRelationMap, attachments, corpMsgBulkTask, wxCorpApp);
	}

	/**
	 * 设置corn表达式
	 * @param corpMsgBulkTask
	 */
	private void setCorn(CorpMsgBulkTask corpMsgBulkTask) {
		if(corpMsgBulkTask.getSendTimeType() == 1) {
			return;
		}

		CornMetaDto cornDto = corpMsgBulkTask.getCornDto();
		String corn = "0 0 %s %s %s %s";
		String day = "*";
		if(StringUtils.isNotBlank(cornDto.getDay()) ){
			day = cornDto.getDay();
			// 按月最后一天
			if(corpMsgBulkTask.getSendTimeType() == 4) {
				if("30".equals(day) || "31".equals(day)) {
					day = "L";
				}
			}
		}
		if(StringUtils.isNotBlank(cornDto.getWeek())) {
			day = StringPool.QUESTION_MARK;
		}
		corn = String.format(corn,cornDto.getHour(),
				day
				,StringPool.ASTERISK
				,StringUtils.isNotBlank(cornDto.getWeek())?CorpMsgBulkTask.Week.getWeek(cornDto.getWeek()):"?");
		corpMsgBulkTask.setCorn(corn);
	}


	/**
	 * 群发
	 */
	private void sendToMsgTemplate(Map<String, List<CorpUserFollowRelationDto>> collect
			, LinkedList<Attachments> attachments,
			                       CorpMsgBulkTask corpMsgBulkTask,
			                       WxCorpApp wxCorpApp) {
		CorpMsgTemplate corpMsgTemplate = new CorpMsgTemplate();
		corpMsgTemplate.setAttachments(attachments);
		corpMsgTemplate.setChatType("single");
		if (StringUtils.isNotBlank(corpMsgBulkTask.getText())) {
			Text text = new Text();
			text.setContent(corpMsgBulkTask.getText());
			corpMsgTemplate.setText(text);
		}
		// 每一万条发一次
		Long sendId = SEQUENCE.nextId();
		collect.forEach((followId, val) -> {
			try {
				List<String> extendIds = new ArrayList<>(val.size());
				List<Long> userIds = null;
				if (StrUtil.isNotEmpty(corpMsgBulkTask.getCouponIds())) {
					userIds = new ArrayList<>(val.size());
				}
				for (CorpUserFollowRelationDto dto : val) {
					extendIds.add(dto.getCorpUserId());
					if (userIds != null && dto.getUserId() != null) {
						userIds.add(dto.getUserId());
					}
				}
				List<List<String>> split = CollectionUtil.split(extendIds, LIMIT);
				for (List<String> corpUserIdList : split) {
					corpMsgTemplate.setSender(followId);
					corpMsgTemplate.setExternalUserid(corpUserIdList);
					CorpMsgTemplateAddResult result = corpOps.addMsgTemplate(wxCorpApp.getCorpId(), corpMsgTemplate);
					//记录企业微信群发任务记录
					CorpMsgBulkTaskRecord corpMsgBulkTaskRecord = new CorpMsgBulkTaskRecord();
					//该企业微信群发任务id ->> corp_msg_bulk_task.id
					corpMsgBulkTaskRecord.setTaskId(corpMsgBulkTask.getId());
					corpMsgBulkTaskRecord.setCorpId(wxCorpApp.getId());
					//每次群发客户总数
					corpMsgBulkTaskRecord.setTotal(corpUserIdList.size());
					//该企业微信下的企业成员id
					corpMsgBulkTaskRecord.setFollowId(followId);
					corpMsgBulkTaskRecord.setSendId(sendId);
					//该企业微信群发任务返回的msgid
					corpMsgTemplate.setExternalUserid(null);
					String content = Jsons.toJson(corpMsgTemplate);
					corpMsgBulkTaskRecord.setMsgid(result.getMsgId());
					corpMsgBulkTaskRecord.setContext(content);
					corpMsgBulkTaskRecordMapper.insert(corpMsgBulkTaskRecord);

					CorpMsg corpMsg = new CorpMsg();
					corpMsg.setCorpId(wxCorpApp.getId());
					corpMsg.setFollowId(followId);
					corpMsg.setMsgid(result.getMsgId());
					corpMsg.setContent(content);
					corpMsgMapper.insert(corpMsg);
					//客户保存
					sendResultRecord(corpMsgBulkTaskRecord, corpUserIdList, result.getFailList());
				}
				//若需要发优惠券
				if (CollUtil.isNotEmpty(userIds)) {
					String couponIds = corpMsgBulkTask.getCouponIds();
					List<Long> couponIdList = Jsons.parseList(couponIds, Long.class);
					for (Long couponId : couponIdList) {
						Coupon coupon = couponMapper.getCouponById(couponId);
						Date validStartTime = null;
						Date validEndTime = null;
						if (coupon != null) {
							if (coupon.getIsValidTime()) {
								validStartTime = coupon.getValidStartTime();
								validEndTime = coupon.getValidEndTime();
							} else {
								validStartTime = DateTime.now();
								validEndTime = DateUtil.offsetDay(validStartTime, coupon.getValidDays());
							}
						}
						if (userIds.size() <= 500) {
							couponMapper.batchSendCouponToUsers(userIds, couponId, CouponUser.Channel.corp_bulk_task, CouponUser.Status.unused, validStartTime, validEndTime);
						} else {
							for (List<Long> userIdsList : Lists.partition(userIds, 500)) {
								couponMapper.batchSendCouponToUsers(userIdsList, couponId, CouponUser.Channel.corp_bulk_task, CouponUser.Status.unused, validStartTime, validEndTime);
							}
						}
					}
				}
			} catch (Exception e) {
				log.error("群发失败任务id{},,{}", corpMsgBulkTask.getId(), e.getMessage());
			}
			corpMsgBulkTaskMapper.updateById(corpMsgBulkTask);
		});
	}

	/**
	 * 客户接受结果记录
	 *
	 * @param corpUserIdList 已发送集合
	 * @param failList       失败集合
	 */
	private void sendResultRecord(CorpMsgBulkTaskRecord corpMsgBulkTaskRecord, List<String> corpUserIdList, List<String> failList) {
		if (CollectionUtil.isNotEmpty(failList)) {
			corpUserIdList.retainAll(failList);
		}

		if (CollectionUtil.isNotEmpty(corpUserIdList)) {
			List<List<String>> split = CollectionUtil.split(corpUserIdList, LIMIT / 4);
			for (List<String> strings : split) {
				List<CorpUserSendRecord> corpUserSendRecords = corpMsgBulkTaskMapper.getsSuccessCorpUser(strings);
				log.info("保存群发记录大小:{}", corpUserSendRecords.size());
				corpUserSendRecords.forEach(corpUserSendRecord -> {
					corpUserSendRecord.setCorpMsgRecordId(corpMsgBulkTaskRecord.getId());
				});
				corpUserSendRecordService.saveBatch(corpUserSendRecords);
			}
		}

//		if (CollectionUtil.isNotEmpty(failList)) {
//			List<CorpUserSendRecord> corpUserSendRecords = corpMsgBulkTaskMapper.getsSuccessCorpUser(failList);
//			if (CollectionUtil.isNotEmpty(corpUserSendRecords)) {
//				corpUserSendRecords.forEach(corpUserSendRecord -> {
//					log.info("失败群发记录大小:{}", corpUserSendRecords.size());
//					corpUserSendRecord.setCorpMsgRecordId(corpMsgBulkTaskRecord.getId());
//					corpMsgBulkTaskRecord.setTaskId(corpMsgBulkTaskRecord.getTaskId());
//					corpUserSendRecordService.saveBatch(corpUserSendRecords);
//				});
//			}
//		}
	}

	/**
	 * 处理群发任务和标签的关系
	 */
	private void handleTaskTagRelation(CorpMsgBulkTask corpMsgBulkTask) throws Exception {
		Long taskId = corpMsgBulkTask.getId();
		List<CorpMsgBulkTaskTagRelation> relations = corpMsgBulkTaskTagRelationMapper.selectList(Wrappers.lambdaQuery(CorpMsgBulkTaskTagRelation.class)
				.eq(CorpMsgBulkTaskTagRelation::getTaskId, taskId));
		String chooseTagStr = corpMsgBulkTask.getChooseTagStr();
		if (corpMsgBulkTask.getSendScope() == 2 && StrUtil.isEmpty(chooseTagStr)) {
			throw BusinessRuntimeException.getInstance("请选择对应的标签");
		}
		if (StrUtil.isEmpty(chooseTagStr)) {
			return;
		}
		List<String> corpTagIds = Jsons.parseList(chooseTagStr, String.class);
		if (CollUtil.isNotEmpty(relations)) {
			//清除删除的关系
			List<Long> del_relation_ids = new ArrayList<>();
			relations.forEach(relation -> {
				Boolean is_del = true;
				for (String tagId : corpTagIds) {
					if (tagId.equals(relation.getTagId())) {
						is_del = false;
						break;
					}
				}
				if (is_del) {
					del_relation_ids.add(relation.getId());
				}
			});
			if (del_relation_ids.size() > 0) {
				corpMsgBulkTaskTagRelationMapper.deleteBatchIds(del_relation_ids);
			}
		}
		corpTagIds.forEach(tagId -> {
			CorpMsgBulkTaskTagRelation tagRelation = corpMsgBulkTaskTagRelationMapper.selectOne(Wrappers.lambdaQuery(CorpMsgBulkTaskTagRelation.class)
					.eq(CorpMsgBulkTaskTagRelation::getTaskId, taskId)
					.eq(CorpMsgBulkTaskTagRelation::getTagId, tagId).last("limit 1"));
			if (tagRelation == null) {
				try {
					tagRelation = new CorpMsgBulkTaskTagRelation();
					tagRelation.setTagId(tagId);
					tagRelation.setTaskId(taskId);
					corpMsgBulkTaskTagRelationMapper.insert(tagRelation);
				} catch (DuplicateKeyException e) {
				}
			}
		});
	}
}
