package com.cku.oa.sys.service;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.cku.core.ZAErrorCode;
import com.cku.core.ZAException;
import com.cku.oa.kennel.entity.Kennel;
import com.cku.oa.show.dao.MainShowsDao;
import com.cku.oa.show.entity.MainShows;
import com.cku.oa.sys.dao.RemindDao;
import com.cku.oa.sys.entity.LoginServer;
import com.cku.oa.sys.entity.Remind;
import com.cku.oa.sys.entity.SMSChangePassword;
import com.cku.oa.sys.entity.user.IEnum;
import com.cku.oa.sys.entity.user.Member;
import com.cku.oa.sys.service.user.MemberService;
import com.cku.oa.sys.util.IPLimitUtil;
import com.cku.oa.sys.util.ValidateUtil;
import com.cku.oa.sys.util.ZtSmsUtil;
import com.cku.restful.v1.appreciation.model.RestAppreciationValidCodeEnum;
import com.github.sd4324530.fastweixin.util.StrUtil;
import com.thinkgem.jeesite.common.config.Global;
import com.thinkgem.jeesite.common.service.CrudService;
import com.thinkgem.jeesite.common.servlet.ValidateCodeServlet;
import com.thinkgem.jeesite.common.utils.CacheUtils;
import com.thinkgem.jeesite.common.utils.DateUtils;
import com.thinkgem.jeesite.common.utils.JedisUtils;
import com.thinkgem.jeesite.common.utils.PropertiesLoader;
import com.thinkgem.jeesite.modules.sys.dao.UserDao;
import com.thinkgem.jeesite.modules.sys.entity.User;
import com.thinkgem.jeesite.modules.sys.utils.UserUtils;

/**
 * 业务提醒功能Service
 * 
 * @author fanhuibin
 * @version 2016-08-11
 */
@Service
@Transactional(readOnly = true)
public class SMSRemindService extends CrudService<RemindDao, Remind> {
	private static final String CACHE_NAME = "smsCache";
	private static final String DOG_SHOW_CACHE = "dogShowCache";
	private static final String NEW_CACHE_NAME = "newSmsCache";
	private static final String KENNEL_OWNER_CHANGE_CACHE = "kennekOwnerChangeCache";
	private static final String DOG_BIRTH_CERTIFICATE_TRANSFER = "dogBirthCertificateTransfer";
	// type 绑定安全手机bindMobile、注册register、重置密码resetPassword、换绑安全手机changeMobile
	public static final String TYPE_SMS_BIND_MOBILE = "bindMobile";// 绑定安全手机
	public static final String TYPE_SMS_REGISTER = "register";// 注册
	public static final String TYPE_SMS_RESET_PASSWORD = "resetPassword";// 重置密码
	public static final String TYPE_SMS_CHANGE_MOBILE = "changeMobile";// 换绑安全手机
	public static final String TYPE_SMS_BIND_USER = "bindUser";// 绑定会员
	public static final String TYPE_SMS_BIND_RENEWALS = "renewals";// 俱乐部会员续费
	public static final String TYPE_SMS_DOG_SPORTING_MEMBER = "dogSportingMember";// 犬运动会员查询
	public static final String TYPE_SMS_USER_CANCEL = "userCancel";
	public static final String TYPE_SMS_GOLD_CERTIFIED_APPLY_AUDIT = "goldCertifiedAudit";// 金质证书父母犬审核

	@Autowired
	private MemberService memberService;
	@Autowired
	private UserDao userDao;
	@Autowired
	private UserService userService;
	@Autowired
	private LoginServerService loginServerService;

	@Autowired
	private MainShowsDao mainShowsDao;

//	@Value("${noseIp}")
//	private String noseIp;
//	@Value("${noseLimit}")
//	private String noseLimit;

	private static PropertiesLoader loader = new PropertiesLoader("config.properties");

	/**
	 * 赛事验证
	 * 
	 * @param request
	 */
	public void sendShowSMSCode(HttpServletRequest request) {
		String mobile = request.getParameter("mobile"); // 手机号
		String mainShowId = request.getParameter("mainShowId"); // 赛事id
		String pedigreeCertifiedCode = request.getParameter("pedigreeCertifiedCode"); // 血统证书号
		Member member = UserUtils.getLoginMember();

		// 参数校验
		if (StringUtils.isBlank(mainShowId)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "主犬展id为空");
		}
		MainShows mainShows = mainShowsDao.get(mainShowId);
		if (mainShows == null) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "主犬展id有误");
		}

		SMSChangePassword oldSMS = (SMSChangePassword) CacheUtils.get(DOG_SHOW_CACHE,
				"sms_dog_show" + pedigreeCertifiedCode + mobile);
		if (oldSMS != null) {
			Date oldDate = oldSMS.getDate();
			if ((System.currentTimeMillis() - oldDate.getTime()) < 100 * 1000) {
				throw new ZAException(ZAErrorCode.ZA_VALID_QUEST_FREQUENTLY, "验证码已发送请耐心等待");
			}
		}

//		String key = "" + Math.round(Math.random() * 10000);
		String key = "" + (Math.round(Math.random() * 8999) + 1000);// 不会出现三位数和五位数的情况
		SMSChangePassword change = new SMSChangePassword();
		change.setDate(new Date());
		change.setKey(key);
		CacheUtils.put(DOG_SHOW_CACHE, "sms_dog_show" + pedigreeCertifiedCode + mobile, change);

		ZtSmsUtil.sendNote("会员" + member.getMemberCode() + "正在为您的犬只" + pedigreeCertifiedCode + "报名,报名场次为"
				+ mainShows.getMainShowName() + "本次犬展报名验证码是:" + key + ",如非授权操作,请忽略本短信。", mobile);

	}

	/**
	 * 发送验证码的短信(犬主变更)
	 * 
	 * @param method
	 */
	public void sendCodeMessage(String method, HttpServletRequest request) {
		Member member = UserUtils.getLoginMember();
		// 检查与上一次发送验证码的时间间隔，小于1分钟不允许重复发送验证码
		User user = UserUtils.getLoginUser();
		SMSChangePassword oldSMS = (SMSChangePassword) CacheUtils.get(CACHE_NAME, user.getId() + "-" + "sms" + method);
		if (oldSMS != null) {
			Date oldDate = oldSMS.getDate();
			if ((System.currentTimeMillis() - oldDate.getTime()) < 60 * 1000) {
				throw new ZAException(ZAErrorCode.ZA_VALID_QUEST_FREQUENTLY, "申请短信验证码的间隔过短");
			}
		}
		// IPLimitUtil.CheckLimit("sms", request, 10); //每天的短信限额是10条
		// 发送短信
		String key = "" + Math.round(Math.random() * 1000000);
		SMSChangePassword change = new SMSChangePassword();
		change.setUser(user);
		change.setDate(new Date());
		change.setKey(key);
		CacheUtils.put(CACHE_NAME, user.getId() + "-" + "sms" + method, change);
		CacheUtils.put(NEW_CACHE_NAME, "newSmsDate" + method, new Date());
		ZtSmsUtil.sendNote("您正在办理犬主变更业务，验证码是" + key + "，用于审核通过犬主变更申请。涉及犬只归属权安全，请勿向他人泄露。", member.getSafeMobile());
	}

	/**
	 * @Description： 发送验证码，并存缓存
	 * 
	 * @param type        绑定安全手机bindMobile、注册register、重置密码resetPassword、换绑安全手机changeMobile
	 * @param checkRepeat true 为查重，false 不查重
	 * @author: yuanshuai
	 * @date: 2017/12/22 14:53
	 */
	public void sendVerifyCode(HttpServletRequest request, String mobile, boolean checkRepeat, String type) {
		if (StringUtils.isBlank(mobile)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号为空");
		}
		// 查重
		if (checkRepeat) {
			String repeatMsg = "当前手机已被其他用户注册或绑定，不允许重复使用，请联系客服，致电400-660-7000";
			String userId = UserUtils.getLoginUser().getId();
			LoginServer loginServer = loginServerService.getLoginServerByUrl(request);
			if (type.equals("register") && userDao.check("login_name", mobile, loginServer.getUserType()).size() > 0) {
				throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, repeatMsg);
			}
			if (userService.bindMobileCheck(mobile, userId, loginServer.getUserType()).contains("false")) {
				throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, repeatMsg);
			}
			if (memberService.safeMobileCheck(mobile, null, loginServer.getMemberSite()).contains("false")) {
				throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, repeatMsg);
			}
		}
		// 是否做次数限制
		if (request != null) {
			// 检查与上一次发送验证码的时间间隔，小于1分钟不允许重复发送验证码
			SMSChangePassword oldSMS = (SMSChangePassword) CacheUtils.get(CACHE_NAME, "newSms" + mobile);
			if (oldSMS != null) {
				Date oldDate = oldSMS.getDate();
				if ((System.currentTimeMillis() - oldDate.getTime()) < 60 * 1000) {
					throw new ZAException(ZAErrorCode.ZA_VALID_QUEST_FREQUENTLY, "申请短信验证码的间隔过短");
				}
			}
			//某些业务不设置每日发送上限
			if(!TYPE_SMS_GOLD_CERTIFIED_APPLY_AUDIT.equals(type)) {
				// 每天的短信限额是10条
				IPLimitUtil.CheckLimit("newSms" + type, request, 10);
				Integer count = (Integer) CacheUtils.get(NEW_CACHE_NAME, "smsCount" + type + mobile);
				String date = (String) CacheUtils.get(NEW_CACHE_NAME, "smsCount-date" + type + mobile);
				String nowDate = DateUtils.formatDate(new Date(), "yyyy-MM-dd");
				if (date != null && date.equals(nowDate)) {
					if (count == null || count <= 3) {
						CacheUtils.put(NEW_CACHE_NAME, "smsCount" + type + mobile, count == null ? 1 : count + 1);
					} else {
						throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "该手机号已超过每日发送验证码次数上限");
					}
				} else {
					CacheUtils.put(NEW_CACHE_NAME, "smsCount" + type + mobile, 1);
					CacheUtils.put(NEW_CACHE_NAME, "smsCount-date" + type + mobile, nowDate);
				}
			}
		}

		String validateCode = request.getParameter("validateCode");

		if (validateCode != null) {
			if (StringUtils.isBlank(validateCode)) {
				throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码为空");
			}

			Session session = UserUtils.getSession();
			String code = (String) session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
			// 重置验证码
			session.setAttribute(ValidateCodeServlet.VALIDATE_CODE, null);

			if (code == null) {
				throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码无效，请从新获取验证码");
			}
			if (!validateCode.toLowerCase().equals(code)) {
				// 重置验证码
				// session.setAttribute(ValidateCodeServlet.VALIDATE_CODE,null);
				throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码有误");
			}

		}

		// 发送短信
		String key = "" + Math.round(Math.random() * 1000000);
		SMSChangePassword change = new SMSChangePassword();
		change.setUser(new User());
		change.setDate(new Date());
		change.setKey(key);
		CacheUtils.put(CACHE_NAME, "newSms" + mobile, change);
		CacheUtils.put(NEW_CACHE_NAME, "newSmsDate" + mobile, new Date());
		ZtSmsUtil.sendVerificationCode(key, mobile, type);
	}

	/**
	 * @Description： 检验验证码，返回true和false
	 * 
	 * @author: yuanshuai
	 * @date: 2017/12/22 15:38
	 */
	public boolean checkVerifyCode(String mobile, String key) {
		if (Global.isDevMode() && "6666".equals(key)) {
			return true;
		}
		SMSChangePassword sms = (SMSChangePassword) CacheUtils.get(CACHE_NAME, "newSms" + mobile);
		if (sms == null) {
			return false;
		}
		String realKey = sms.getKey();
		if (realKey.equals(key)) {
			CacheUtils.remove(NEW_CACHE_NAME, "newSms" + mobile);// 短信验证码验证通过后，移除该号码的缓存验证码
			return true;
		} else {
			return false;
		}
	}

	/**
	 *
	 * @description: 获取用户安全手机缓存的验证码
	 * @author: cuihuaiyu
	 * @date: 2017年12月26日 下午5:33:19
	 */
	public Object getSafeCode(String id) {
		User user = UserUtils.getLoginUser();
		return CacheUtils.get(CACHE_NAME, user.getId() + "-" + "sms" + id);
	}

	public void removeSafeCode(String id, String key) {

		User user = UserUtils.getLoginUser();
		SMSChangePassword sms = (SMSChangePassword) CacheUtils.get(CACHE_NAME, user.getId() + "-" + "sms" + id);
		String realKey = sms.getKey();
		if (realKey.equals(key)) {
			CacheUtils.remove(CACHE_NAME, user.getId() + "-" + "sms" + id);// 短信验证码验证通过后，移除该号码的缓存验证码
		}
	}

	/**
	 * 给鼻纹组使用
	 * 
	 * @param request
	 */
	public void sendNoseLine(HttpServletRequest request) {
		String content = request.getParameter("code");
		String mobile = request.getParameter("mobile");

		if (StringUtils.isBlank(mobile) || StringUtils.isBlank(content)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "参数错误");
		}
		if (Global.isDevMode()) { // 开发环境每天300条
			Long count = JedisUtils.incr("nose-" + LocalDate.now().toString());
			if (count.compareTo(Long.parseLong(loader.getProperty("noseLimit"))) == 1) { // 超过300条
				throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "当日请求已超过" + loader.getProperty("noseLimit") + "条");
			}
		} else { // 线上环境限制鼻纹ip
			String ip = IPLimitUtil.getIpAddr(request);
			logger.info("===== nose ip:{}", ip);
			// 是否开启ip验证
			if (loader.getProperty("noseCheckIp").equals("true")) {
				List<String> ips = Arrays.asList(loader.getProperty("noseIp").split(","));
				if (!ips.contains(ip)) {
					throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "非法ip");
				}
			}
		}
		ZtSmsUtil.sendNoseVerificationCode(content, mobile, TYPE_SMS_REGISTER);
	}

	/**
	 * @Description: 犬舍变更
	 * @author: zhangxiang
	 * @date: 2021/10/22 15:45
	 */
	public void sendOwnerChangeSMSCode(Kennel kennel, String mobile) {
		// 参数校验
		if (StrUtil.hasBlank(mobile)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号不能为空");
		}
		SMSChangePassword oldSMS = (SMSChangePassword) CacheUtils.get(KENNEL_OWNER_CHANGE_CACHE,
				"sms_kennnel_owner_change" + mobile);
		if (oldSMS != null) {
			Date oldDate = oldSMS.getDate();
			if ((System.currentTimeMillis() - oldDate.getTime()) < 55 * 1000) {
				throw new ZAException(ZAErrorCode.ZA_VALID_QUEST_FREQUENTLY, "验证码已发送请耐心等待");
			}
		}
		int key = (int) ((Math.random() * 9 + 1) * 1000);
		SMSChangePassword change = new SMSChangePassword();
		change.setDate(new Date());
		change.setKey(key + "");
		CacheUtils.put(KENNEL_OWNER_CHANGE_CACHE, "sms_kennnel_owner_change" + mobile, change);
		if (!ZtSmsUtil.validateSmsWhiteList(mobile)) {
			logger.debug("短信发送失败，开发模式不能发送短信");
			return;
		}
		ZtSmsUtil.sendNote("尊敬的会员，您的" + kennel.getName() + "正在办理主理人变更业务，验证码是:" + key
				+ ",    5分钟内有效，用于确认犬舍主理人变更申请。涉及犬舍归属权安全，请勿向他人泄露。", mobile);

	}

	public void sendTransferSMSCode(String mobile) {
		// 参数校验
		if (StrUtil.hasBlank(mobile)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号不能为空");
		}
		SMSChangePassword oldSMS = (SMSChangePassword) CacheUtils.get(DOG_BIRTH_CERTIFICATE_TRANSFER,
				"dog_birth_certificate_transfer" + mobile);
		if (oldSMS != null) {
			Date oldDate = oldSMS.getDate();
			if ((System.currentTimeMillis() - oldDate.getTime()) < 55 * 1000) {
				throw new ZAException(ZAErrorCode.ZA_VALID_QUEST_FREQUENTLY, "验证码已发送请耐心等待");
			}
		}
		int key = (int) ((Math.random() * 9 + 1) * 1000);
		SMSChangePassword change = new SMSChangePassword();
		change.setDate(new Date());
		change.setKey(key + "");
		CacheUtils.put(DOG_BIRTH_CERTIFICATE_TRANSFER, "dog_birth_certificate_transfer" + mobile, change);
		ZtSmsUtil.sendNote("您正在办理新生犬登记卡转让业务，验证码是" + key + ",用于审核通过登记卡转让申请。涉及犬只归属权安全，请勿向他人泄露。", mobile);
	}

	public void sendUserCancelSMSCode(String mobile) {
		// 参数校验
		if (StrUtil.hasBlank(mobile)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号不能为空");
		}
		SMSChangePassword oldSMS = (SMSChangePassword) CacheUtils.get(TYPE_SMS_USER_CANCEL, "user_cancel" + mobile);
		if (oldSMS != null) {
			Date oldDate = oldSMS.getDate();
			if ((System.currentTimeMillis() - oldDate.getTime()) < 55 * 1000) {
				throw new ZAException(ZAErrorCode.ZA_VALID_QUEST_FREQUENTLY, "验证码已发送请耐心等待");
			}
		}
		int key = (int) ((Math.random() * 9 + 1) * 1000);
		SMSChangePassword change = new SMSChangePassword();
		change.setDate(new Date());
		change.setKey(key + "");
		CacheUtils.put(TYPE_SMS_USER_CANCEL, "user_cancel" + mobile, change);
		if (!ZtSmsUtil.validateSmsWhiteList(mobile)) {
			logger.debug("短信发送失败，开发模式不能发送短信");
			return;
		}
		ZtSmsUtil.sendNote("验证码：" + key + "，请勿向他人泄露。如非本人操作，请忽略本短信。", mobile);

	}

	public void checkUserCancelSmsCode(String mobile, String smsCode) {
		if (Global.isDevMode() && "6666".equals(smsCode)) {// 不是开发者手机号开发环境只验证6666
			return;
		}
		if (StringUtils.isBlank(mobile) || StringUtils.isBlank(smsCode)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号、验证码为空");
		}

		SMSChangePassword obj = (SMSChangePassword) CacheUtils.get(TYPE_SMS_USER_CANCEL, "user_cancel" + mobile);
		if (obj == null) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码无效，请重新获取");
		}
		if (!smsCode.equals(obj.getKey())) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码有误，请填写正确的验证码！");
		}
		// 校验通过后移除验证授权码
		CacheUtils.remove(TYPE_SMS_USER_CANCEL, "user_cancel" + mobile);
	}

	public void sendAppreciationApplySMSCode(String mobile, Integer codeType) throws Exception {
		if (StringUtils.isBlank(mobile)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号为空");
		}
		if (!ValidateUtil.phoneMobile(mobile)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号格式不正确");
		}
		RestAppreciationValidCodeEnum validCode = IEnum.codeOf(RestAppreciationValidCodeEnum.class, codeType)
				.orElse(null);
		if (Objects.isNull(validCode)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码获取类型错误，无法获取！");
		}
		String cacheKey = new StringBuilder().append("appreciation_").append(validCode.name().toLowerCase()).append("_")
				.append(mobile).toString();
		// 次数限制
		SMSChangePassword obj = (SMSChangePassword) CacheUtils.get(CACHE_NAME, cacheKey);
		if (obj != null) {
			Date oldDate = obj.getDate();
			if ((System.currentTimeMillis() - oldDate.getTime()) < 60 * 1000) {
				throw new ZAException(ZAErrorCode.ZA_VALID_QUEST_FREQUENTLY, "申请短信验证码的间隔不能少于60s!");
			}
		}
		// 发送短信
		String key = "" + Math.round(Math.random() * 1000000);
		SMSChangePassword change = new SMSChangePassword();
		change.setUser(new User());
		change.setDate(new Date());
		change.setKey(key);
		CacheUtils.put(CACHE_NAME, cacheKey, change);
		ZtSmsUtil.sendNote(String.format(validCode.message(), key), mobile);
	}

	public void checkAppreciationApplySmsCode(String mobile, String smsCode, Integer codeType) {
		if (!ZtSmsUtil.validateSmsWhiteList(mobile) && "6666".equals(smsCode)) {// 不是开发者手机号开发环境只验证6666
			return;
		}
		if (StringUtils.isBlank(mobile) || StringUtils.isBlank(smsCode)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "手机号、验证码为空");
		}

		RestAppreciationValidCodeEnum validCode = IEnum.codeOf(RestAppreciationValidCodeEnum.class, codeType)
				.orElse(null);
		if (Objects.isNull(validCode)) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码获取类型错误，无法获取！");
		}
		String cacheKey = new StringBuilder().append("appreciation_").append(validCode.name().toLowerCase()).append("_")
				.append(mobile).toString();

		SMSChangePassword obj = (SMSChangePassword) CacheUtils.get(CACHE_NAME, cacheKey);
		if (obj == null) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码无效，请重新获取");
		}
		if (!smsCode.equals(obj.getKey())) {
			throw new ZAException(ZAErrorCode.ZA_VALID_FAILED, "验证码有误，请填写正确的验证码！");
		}
		// 校验通过后移除验证授权码
		CacheUtils.remove(CACHE_NAME, cacheKey);
	}

}