package com.iformall.schedule;

import com.alibaba.fastjson.JSONObject;
import com.iformall.domain.dto.WxCUserBasicInfoDto;
import com.iformall.domain.po.*;
import com.iformall.domain.po.msg.WxMsgRecord;
import com.iformall.domain.vo.WxCouponSendVo;
import com.iformall.enums.*;
import com.iformall.mapper.*;
import com.iformall.mq.MqBaseProducer;
import com.iformall.service.*;
import com.iformall.utils.CreditUtil;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class CouponSendSchedule {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private WxMallMapper wxMallMapper;

	@Autowired
	private WxLevelConfigMapper WxLevelConfigMapper;

	@Autowired
	private WxLevelConfigService wxLevelConfigService;

	@Autowired
	private WxCUserBasicInfoMapper wxCUserBasicInfoMapper;

	@Autowired
	private WxCouponSendMapper wxCouponSendMapper;

	@Autowired
	private WxCouponActionLogMapper wxCouponActionLogMapper;

	@Autowired
	private WxOrderService wxOrderService;

	@Autowired
	private WxCouponActionLogService wxCouponActionLogService;

	@Autowired
	private WxCouponSendConfigMapper couponSendConfigMapper;

	@Autowired
	private WxCouponSendService wxCouponSendService;
	@Autowired
	private WxScoreRulesService wxScoreRulesService;

	@Autowired
	private MqBaseProducer mqBaseProducer;
	@Autowired
	private PushLimitService pushLimitService;


	//@Scheduled(cron = "0 0 6 1 * ?") // 每月1号发放会员的权益券
	@Scheduled(cron = "0 0 6 * * ?") // 测试每天6点发券
	//@Scheduled(cron = "*/30 * * * * ?") // 测试30秒中一次
	public void couponSendSchedule() {

		List<WxMall> mallList = wxMallMapper.findList(new WxMall());

		if (mallList.size() == 0) {
			logger.info("定时发券: No Mall info found");
			return;
		}

		Date startTime = Date.from(LocalDateTime.now()
				.with(TemporalAdjusters.firstDayOfMonth())
				.with(LocalTime.MIN)
				.atZone(ZoneId.systemDefault())
				.toInstant());
		Date endTime = Date.from(LocalDateTime.now()
				.with(TemporalAdjusters.lastDayOfMonth())
				.with(LocalTime.MAX)
				.atZone(ZoneId.systemDefault())
				.toInstant());
		//查找所有couponSendConfig
		Map<String, Integer> mallConfigMap = getAllCouponSendConfig(EnumCouponSendSendType.TIMED.getCode());
		mallList.stream().forEach(mall -> {
			//判断启用状态，状态为关闭时跳过不执行
			if (isCouponSendDisable(mallConfigMap, mall)) {
				logger.info("定时发券: {}发券状态关闭, 任务跳过不执行", mall.getName());
				return;
			}
			WxLevelConfig wxLevelConfig = new WxLevelConfig();
			wxLevelConfig.setTenantId(mall.getTenantId());
			List<WxLevelConfig> levelConfigList = WxLevelConfigMapper.findList(wxLevelConfig);
			levelConfigList = levelConfigList.stream()
					.sorted(Comparator.comparing(WxLevelConfig::getPoints))
					.collect(Collectors.toList());

			for (int levelIndex = 0; levelIndex < levelConfigList.size(); levelIndex++) {
				WxLevelConfig l = levelConfigList.get(levelIndex);
				if (l.getCapability() == null)
					continue;
				Integer levelStartScore = l.getPoints();
				Integer levelEndScore = levelIndex + 1 < levelConfigList.size() ? levelConfigList.get(levelIndex + 1).getPoints() : null;

				JSONObject jo = null;
				Long result = null;
				try {
					jo = JSONObject.parseObject(l.getCapability());
					result = wxLevelConfigService.getCapabilitiesOfSendCarConpon(jo);
				} catch (Exception e) {
					logger.error("定时发券: 不能解析发券规则 levelId=" + l.getId() + e.getMessage());
					continue;
				}
				Long sendCount = result;
				if (sendCount > 0) {

					WxCUserBasicInfoDto wxCUserBasicInfoDto = new WxCUserBasicInfoDto();
					wxCUserBasicInfoDto.setTenantId(l.getTenantId());
					wxCUserBasicInfoDto.setLevelStartScore(levelStartScore);
					wxCUserBasicInfoDto.setLevelEndScore(levelEndScore);
					List<WxCUserBasicInfo> wxCUserBasicInfoList = wxCUserBasicInfoMapper.findListByScore(wxCUserBasicInfoDto);

					logger.info("定时发券:给LEVEL[" + l.getLevel() +
							"]积分[" + levelStartScore + "-" + levelEndScore + "]发券" + sendCount + "张");

					wxCUserBasicInfoList.stream().forEach(cu -> {

						WxCouponSend wxCouponSend = new WxCouponSend();
						wxCouponSend.setTenantId(l.getTenantId());
						wxCouponSend.setSendType(EnumCouponSendSendType.TIMED.getCode());
						wxCouponSend.setStatus(EnumCouponSendStatus.VALID.getCode());
						List<WxCouponSendVo> couponSendList = wxCouponSendMapper.findListVo(wxCouponSend);
						int remain = couponSendList.stream().map(cs -> {
							int count = cs.getRemainInventory();
							logger.info("定时发券:给[" + cu.getNickName() +
									"]券[" + cs.getTitle() + "]还剩" +
									count + "张");
							return count;
						}).reduce(Integer::sum).get();

						if (remain < sendCount) {
							logger.error("定时发券:库存不足[" + cu.getNickName() + "]应发" + sendCount + "张,总共剩" + remain + "张");
							return;
						}

						int sentCount = couponSendList.stream().map(cs -> {
							Map params = new HashMap<String, Object>();
							params.put("tenantId", cs.getTenantId());
							params.put("cUserId", cu.getId());
							params.put("couponId", cs.getCouponId());
							params.put("startTime", startTime);
							params.put("endTime", endTime);
							params.put("channelType", EnumCouponSendSendType.TIMED.getCode());
							int count = wxCouponActionLogMapper.getCountByUserAndCouponAndDate(params);
							logger.info("定时发券:已经给[" + cu.getNickName() +
									"]券[" + cs.getTitle() + "] " +
									count + "张");
							return count;
						}).reduce(Integer::sum).get();

						int remainSend = (int) (sendCount - sentCount);
						logger.info("定时发券:给[" + cu.getNickName() +
								"]总共已发送券" + sentCount + "张,还需发" + remainSend + "张");

						for (int count = 0; count < remainSend; count++) {
							boolean sent = false;
							for (int couponIndex = 0; couponIndex < couponSendList.size(); couponIndex++) {
								WxCouponSendVo cs = couponSendList.get(couponIndex);
								if (cs.getRemainInventory() <= 0)
									continue;

								// 发放免费券
								try {
									WxCouponOrder couponOrder = wxOrderService.sendFreeCouponToUser(cu.getId(), cs.getCouponId(), null);
									wxCouponActionLogService.addOne(l.getTenantId(), cs.getCouponId(), couponOrder.getId(), cs.getSendType(), cs.getId());
								} catch (Exception e) {
									logger.error("定时发券:发券失败 levelId=" + l.getId() + " couponId=" + cs.getCouponId() + " userId=" + cu.getId() + e.getMessage());
									continue;
								}

								logger.info("定时发券:商场[" + mall.getName() +
										"]会员等级[" + l.getLevel() +
										"]发送券[" + cs.getTitle() +
										"]给:" + cu.getNickName() +
										" 电话-" + cu.getPhone() +
										" id-" + cu.getId());
								sent = true;
								break;
							}
							if (!sent)
								break;
						}
					});
				}
			}
		});
	}

	/**
	 * 生日券发放
	 */
	@Scheduled(cron = "0 30 15 * * ?") // 测试每天8:30点发券
	public void birthdayCouponSendSchedule() {
		logger.info("生日发券: 任务开始");
		List<WxMall> mallList = wxMallMapper.findList(new WxMall());
		if (mallList.size() == 0) {
			logger.info("生日发券: No Mall info found");
			return;
		}

		Date startTime = Date.from(LocalDateTime.now()
				.with(TemporalAdjusters.firstDayOfYear())
				.with(LocalTime.MIN)
				.atZone(ZoneId.systemDefault())
				.toInstant());
		Date endTime = Date.from(LocalDateTime.now()
				.with(TemporalAdjusters.lastDayOfYear())
				.with(LocalTime.MAX)
				.atZone(ZoneId.systemDefault())
				.toInstant());

		//查找所有couponSendConfig
		Map<String, Integer> mallConfigMap = getAllCouponSendConfig(EnumCouponSendSendType.BIRTHDAY.getCode());
		DateFormat df = new SimpleDateFormat("MM-dd");
		mallList.forEach(mall -> {
			//判断启用状态，状态为关闭时跳过不执行
			if (isCouponSendDisable(mallConfigMap, mall)) {
				logger.info("生日发券: {} 发券状态关闭, 任务跳过不执行", mall.getName());
				return;
			}
			//查找生日券
			WxCouponSend wxCouponSend = new WxCouponSend();
			wxCouponSend.setTenantId(mall.getTenantId());
			wxCouponSend.setSendType(EnumCouponSendSendType.BIRTHDAY.getCode());
			wxCouponSend.setStatus(EnumCouponSendStatus.VALID.getCode());
			List<WxCouponSendVo> couponSendList = wxCouponSendMapper.findListVo(wxCouponSend);
			if (CollectionUtils.isEmpty(couponSendList)) {
				logger.info("生日发券: 会员生日券投放列表为空 couponSendList = {}, 任务跳过不执行", couponSendList);
				return;
			}

			WxCouponSend birthdayConfig = wxCouponSendService.getConfig(EnumCouponSendSendType.BIRTHDAY.getCode(), mall.getTenantId());
			String conditions = birthdayConfig.getConditions();
			JSONObject configJo = null;
			try {
				configJo = JSONObject.parseObject(conditions);
			} catch (Exception e) {
				logger.error("生日发券: 解析conditions异常: {}", e.getMessage());
			}
			if (Objects.isNull(conditions) || Objects.isNull(configJo)) {
				logger.info("生日发券: 会员生日券配置解析异常 conditions = {}, 任务跳过不执行", conditions);
				return;
			}

			//默认生日当天发送
			int beforeDays = 0;
			if (configJo.containsKey(WxCouponSend.KEY_BEFOREDAYS)) {
				beforeDays = configJo.getIntValue(WxCouponSend.KEY_BEFOREDAYS);
			}

			//未来 ${beforeDays}天时间
			Calendar today = Calendar.getInstance();
			Date begin = today.getTime();
			today.add(Calendar.DAY_OF_YEAR, beforeDays);
			Date end = today.getTime();

			List<WxCUserBasicInfo> wxCUserBasicInfoList = wxCUserBasicInfoMapper.findBirthdayList(mall.getTenantId(), df.format(begin.getTime()), df.format(end.getTime()));
			if (CollectionUtils.isEmpty(wxCUserBasicInfoList)) {
				logger.info("生日发券: {} 满足发券的用户列表为空 wxCUserBasicInfoList = {}, 任务跳过不执行", mall.getName(), wxCUserBasicInfoList);
				return;
			}
			//过滤掉已经发送过生日券的用户
			List<WxCUserBasicInfo> newUserList = wxCUserBasicInfoList.stream().filter(wxCUserBasicInfo -> {
				Calendar now = Calendar.getInstance();
				//本次之前的20分钟
				now.set(Calendar.MINUTE, now.get(Calendar.MINUTE) - 30);
				Map<String, Object> params = new HashMap<>();
				params.put("tenantId", wxCUserBasicInfo.getTenantId());
				params.put("cUserId", wxCUserBasicInfo.getId());
				params.put("couponId", null);
				params.put("startTime", startTime);
				params.put("endTime", now.getTime());
				params.put("channelType", EnumCouponSendSendType.BIRTHDAY.getCode());
				int count = wxCouponActionLogMapper.getCountByUserAndCouponAndDate(params);
				//判断之前是否发送过会员生日券
				if (count > 0) {
					logger.info("生日发券: 已经给会员{}发送过生日券, 本次不再发送", wxCUserBasicInfo.getNickName());
					return false;
				} else {
					return true;
				}
			}).collect(Collectors.toList());

			Set<WxCUserBasicInfo> couponSendedUsers = new HashSet<>();
			for (WxCouponSendVo cs : couponSendList) {
				if (cs.getRemainInventory() <= 0) {
					logger.info("生日发券: [title={},id={}]卡券库存不足!", cs.getTitle(), cs.getCouponId());
					continue;
				}
				for (WxCUserBasicInfo cu : newUserList) {
					// 发放生日券
					try {
						WxCouponOrder couponOrder = wxOrderService.sendFreeCouponToUser(cu.getId(), cs.getCouponId(), null);
						wxCouponActionLogService.addOne(cu.getTenantId(), cs.getCouponId(), couponOrder.getId(), cs.getSendType(), cs.getId());
						couponSendedUsers.add(cu);
					} catch (Exception e) {
						logger.error("定时发券:发券失败 levelId=" + cu.getId() + " couponId=" + cs.getCouponId() + " userId=" + cu.getId() + e.getMessage());
						continue;
					}
					logger.info("定时发券:商场[" + mall.getName() +
							"]会员生日[" + cu.getBirthdate().toInstant() +
							"]发送券[" + cs.getTitle() +
							"]给:" + cu.getNickName() +
							" 电话-" + cu.getPhone() +
							" id-" + cu.getId());
				}
			}

			int birthdayScale = CreditUtil.getBirthdayScoreScale(mall.getTenantId(), wxScoreRulesService);
			for (WxCUserBasicInfo cu : couponSendedUsers) {
				// 记录积分倍率日期
				try {
					WxCUserBasicInfo toUpdate = new WxCUserBasicInfo();
					toUpdate.setId(cu.getId());
					toUpdate.setScoreDate(cu.getBirthdate());
					wxCUserBasicInfoMapper.updateById(toUpdate);
				} catch (Exception e) {
					logger.error("定时发券:记录积分倍率日期失败 {}", e.getMessage());
					continue;
				}
				//短信限制
				//boolean couponLimit = pushLimitService.checkMsg(cu.getTenantId());
				//if (couponLimit) {
					sendSMS(cu, mall, birthdayScale);
				//} else {
				//	logger.info("生日发券: {}", ErrorCode.PUSH_LIMIT_NOT_INRANG.getMessage());
				//}
			}
		});
		logger.info("生日发券: 任务结束");
	}

	private Map<String, Integer> getAllCouponSendConfig(Integer sendType) {
		WxCouponSendConfig queryConfig = new WxCouponSendConfig();
		queryConfig.setSendType(sendType);
		List<WxCouponSendConfig> couponSendConfigList = couponSendConfigMapper.findList(queryConfig);
		return CollectionUtils.isNotEmpty(couponSendConfigList) ?
				couponSendConfigList.stream().collect(Collectors.toMap(WxCouponSendConfig::getTenantId, WxCouponSendConfig::getValue)) : null;
	}

	private boolean isCouponSendDisable(Map<String, Integer> mallConfigMap, WxMall mall) {
		if (mallConfigMap != null && Objects.nonNull(mallConfigMap.get(mall.getTenantId()))) {
			Integer disable = mallConfigMap.get(mall.getTenantId());
			return Objects.equals(disable, EnumEnableType.Disable.getCode());
		}
		return false;
	}

	private void sendSMS(WxCUserBasicInfo cUserBasicInfo, WxMall mall, int birthdayScale) {
		WxMsgRecord wxMsgRecord = new WxMsgRecord();
		wxMsgRecord.setMsgType(EnumMsgRecordType.SMS.getCode());
		wxMsgRecord.setReceiver(cUserBasicInfo.getPhone());
		wxMsgRecord.setTenantId(cUserBasicInfo.getTenantId());
		Map<String, String> dynamicContentMap = new HashMap<>();

		if (birthdayScale > WxScoreRules.DEFAULT_SCALE) {
			float creditScale = new BigDecimal(birthdayScale).divide(new BigDecimal(WxScoreRules.DEFAULT_SCALE)).floatValue();
			dynamicContentMap.put("creditScale", String.valueOf(creditScale));
			wxMsgRecord.setModelType(EnumMsgModel.COUPON_BIRTHDAY_OPENED.getCode());
		} else {
			//没有配置积分规则
			wxMsgRecord.setModelType(EnumMsgModel.COUPON_BIRTHDAY_OPENED_NO.getCode());
		}
		dynamicContentMap.put("userName", Objects.isNull(cUserBasicInfo.getNickName()) ? "" : cUserBasicInfo.getNickName());
		dynamicContentMap.put("mallName", mall.getName());
		wxMsgRecord.setDynamicContentMap(dynamicContentMap);
		mqBaseProducer.sendMessage(wxMsgRecord, EnumMsgMqTopic.DEFAULT.getCode(), EnumMsgMqTag.DEFAULT.getCode(), EnumMsgMqKey.DEFAULT.getCode());
	}
}