
package org.tio.sitexxx.service.service.base.sms;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.auth.HmacSHA1Signer;
import okhttp3.Response;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.jfinal.kit.Ret;
import org.tio.sitexxx.service.api.sms.BaseSmsIntf;
import org.tio.sitexxx.service.api.sms.BaseSmsResultVo;
import org.tio.sitexxx.service.api.sms.BaseSmsVo;
import org.tio.sitexxx.service.api.sms.impl.AliyunSmsImpl;
import org.tio.sitexxx.service.cache.CacheConfig;
import org.tio.sitexxx.service.cache.Caches;
import org.tio.sitexxx.service.model.main.SmsLog;
import org.tio.sitexxx.service.service.ConfService;
import org.tio.sitexxx.service.utils.RetUtils;
import org.tio.sitexxx.service.vo.Const;
import org.tio.utils.http.HttpUtils;
import org.tio.utils.jfinal.P;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 发送短信
 * 
 */
public class SmsService {

	public static final SmsService me = new SmsService();
	private static Logger log = LoggerFactory.getLogger(SmsService.class);
    private static BaseSmsIntf<BaseSmsVo, BaseSmsResultVo> baseSmsService = new AliyunSmsImpl();

	/**
	 * 是不是一个手机号码
	 * @param mobile
	 * @return
	 * 
	 */
	public static boolean isMobileNo(String mobile) {
		if (mobile == null || mobile.length() != 11 || !mobile.startsWith("1")) {
			return false;
		}
		return NumberUtils.isDigits(mobile);
	}


	// 发送 POST 请求的通用方法
	public static String post(String url, Map < String, String > paramsMap) {
		CloseableHttpClient client = HttpClients.createDefault();
		String responseText = "";
		CloseableHttpResponse response = null;
		try {
			HttpPost method = new HttpPost(url);
			if (paramsMap != null) {
				List< NameValuePair > paramList = new ArrayList<>();
				for (Map.Entry < String, String > param: paramsMap.entrySet()) {
					NameValuePair pair = new BasicNameValuePair(param.getKey(),
							param.getValue());
					paramList.add(pair);
				}
				method.setEntity(new UrlEncodedFormEntity(paramList,
						HmacSHA1Signer.ENCODING));
			}
			response = client.execute(method);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				responseText = EntityUtils.toString(entity, HmacSHA1Signer.ENCODING);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				response.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return responseText;
	}

	/**
	 * 发送短信
	 * @param mobile
	 * @param biztype
	 * @param ip
	 * @param sessionid
	 * @param referer
	 * @param cookieKey
	 * @param extParams
	 * @return
	 * @throws Exception
	 * @author xufei
	 * 2020年12月10日 下午4:50:22
	 */
	public Ret send(String mobile, Short biztype, String ip,String sessionid, String referer,String cookieKey,Map<String, String> extParams) throws Exception {
		if (!isMobileNo(mobile)) {
			log.error("phone:{}, is invalid", mobile);
			saveSmsInfo(ip, mobile, "", "", biztype, "手机号码格式不正确", 0, 0, null, Const.Status.DISABLED, sessionid, referer, cookieKey);
			return RetUtils.failMsg("手机号格式有误");
		}
		Integer ipcount;
		int maxcountperdayip = ConfService.getInt("sms.maxcount.ip.perday", P.getInt("sms.maxcount.ip.perday", 100));
		ipcount = (Integer) Caches.getCache(CacheConfig.SMS_IP_COUNT).get(ip);
		if (ipcount != null && ipcount > maxcountperdayip) {
			log.error("ip:{}, has sent {}", mobile, ipcount);
			saveSmsInfo(ip, mobile, "", "", biztype, "24小时内ip获取验证码次数超上限", 0, ipcount, null, Const.Status.DISABLED, sessionid, referer, cookieKey);
			return RetUtils.failMsg("24小时内获取验证码次数超上限！");
		} else if (ipcount == null) {
			ipcount = 0;
		}
		Integer mobilecount = (Integer) Caches.getCache(CacheConfig.SMS_MOBILE_COUNT).get(mobile);
		if (mobilecount == null) {
			mobilecount = 0;
		} else if (mobilecount >= P.getInt("sms.maxcount.mobile.perday", 10)) {
			saveSmsInfo(ip, mobile, "", "", biztype, "24小时内手机获取验证码次数超上限", mobilecount, ipcount, null, Const.Status.DISABLED, sessionid, referer, cookieKey);
			log.error("mobile:{}, has sent {}", mobile, mobilecount);
			return RetUtils.failMsg("24小时内获取验证码次数超上限！");
		}
		Integer deviceCount = null;
		if (StrUtil.isNotBlank(cookieKey)) {
			deviceCount = (Integer) Caches.getCache(CacheConfig.SMS_DEVICE_COUNT).get(cookieKey);
			if (deviceCount == null) {
				deviceCount = 0;
			}
			if (deviceCount >= P.getInt("sms.maxcount.device.count", 5)) {
				saveSmsInfo(ip, mobile, "", "", biztype, "1个设备1个小时只能发送5次", mobilecount, ipcount, deviceCount, Const.Status.DISABLED, sessionid, referer, cookieKey);
				log.error("mobile:{}, has sent {}", mobile, mobilecount);
				return RetUtils.failMsg("设备发送次数过多，请稍后再试");
			}
		}
		BaseSmsVo smsVo = baseSmsService.initSmsVo(mobile, biztype, "", extParams);

		String code = baseSmsService.getCode(smsVo, true);
		//避免短信多次请求
		if (!smsVo.isCodeAdd()) {
			return RetUtils.failMsg("短信已发送,请稍后再试");
		}
		boolean result = sendDuanXinBao(mobile, code);
		if (result) {
			mobilecount++;
			ipcount++;
			Caches.getCache(CacheConfig.SMS_MOBILE_COUNT).put(mobile, mobilecount);
			Caches.getCache(CacheConfig.SMS_IP_COUNT).put(ip, ipcount);
			if (StrUtil.isNotBlank(cookieKey)) {
				deviceCount++;
				Caches.getCache(CacheConfig.SMS_DEVICE_COUNT).put(cookieKey, deviceCount);
			}
			if (smsVo.isCodeAdd()) {
				baseSmsService.updateCode(smsVo);
			}
			saveSmsInfo(ip, mobile, code, "", biztype, "", mobilecount, ipcount, deviceCount, Const.Status.NORMAL, sessionid, referer, cookieKey);
			return RetUtils.okData("短信发送成功");
		} else {
			log.error("短信发送失败, 手机号码:{}, code: {}, remoteip:{},msg:{}", mobile,code, ip, result);
			return RetUtils.failMsg("短信发送失败");
		}
	}

	private boolean sendDuanXinBao(String mobile, String code) throws Exception {
		Response result = HttpUtils.get("https://api.smsbao.com/sms?u=zt147258&p=f019ead0f5f245469a739d3aced8914a&g=&m="+mobile+"&c="+URLEncoder.encode("【新创联盟】 您的验证码是"+code+"。如非本人操作，请忽略本短信"));
		String content1 = result.body().string();
		boolean content = "0".equals(content1);
		if (content){
			return true;
		} else {
			log.error("短信发送失败 code:"+content1);
		}
		return false;
	}

	private boolean sendYunPin(String mobile, String code) throws UnsupportedEncodingException {
		Map<String, String> params = new HashMap<>();
		params.put("apikey", "3fffa27bd8db02247fa79502781df3d2");
		params.put("tpl_id", "6085496");
		params.put("mobile", mobile);
		params.put("tpl_value", URLEncoder.encode("#code#","utf-8") + "=" + URLEncoder.encode(code, "utf-8"));
		String result = post("https://sms.yunpian.com/v2/sms/tpl_single_send.json", params);
		JSONObject jsonResponse = JSONObject.parseObject(result);
		return jsonResponse.getInteger("code") == 0;
	}

	/**
	 * 校验验证码
	 * @param mobile
	 * @param biztype
	 * @param code
	 * @param extParams
	 * @return
	 * @throws Exception
	 * @author xufei
	 * 2020年12月10日 下午4:56:04
	 */
	public Ret checkCode(String mobile, Short biztype, String code, Map<String, String> extParams, boolean isDel) throws Exception {
		if(!Const.SMS_OPENFLAG) {
			return RetUtils.okMsg("校验成功");
		}
		if (StrUtil.isBlank(mobile) || biztype == null || StrUtil.isBlank(code)) {
			return RetUtils.invalidParam();
		}
		BaseSmsVo smsVo = baseSmsService.initSmsVo(mobile, biztype, "", extParams);
		String cacheCode = baseSmsService.getCode(smsVo, false);
		if (StrUtil.isBlank(cacheCode)) {
			return RetUtils.failMsg("验证码已失效");
		}
		boolean check = code.equals(cacheCode);
		if (check) {
			if (isDel) {
				baseSmsService.delCode(smsVo);
			}
			return RetUtils.okMsg("校验成功");
		} else {
			return RetUtils.failMsg("验证码不正确");
		}

	}

	/**
	 * @param mobile
	 * @param biztype
	 * @param code
	 * @return
	 * @author xufei
	 * 2020年12月16日 上午10:07:57
	 */
	public Ret delCode(String mobile, Short biztype) throws Exception {
		if (StrUtil.isBlank(mobile) || biztype == null) {
			return RetUtils.invalidParam();
		}
		BaseSmsVo smsVo = baseSmsService.initSmsVo(mobile, biztype, "", null);
		baseSmsService.delCode(smsVo);
		return RetUtils.okOper();
	}

	/**
	 * 保存短信发送记录日志
	 * @param remoteip
	 * @param code
	 * @param templateCode
	 * @param errorMsg
	 * @param mobilecount
	 * @param ipcount
	 * 
	 */
	private void saveSmsInfo(String remoteip, String phone, String code, String templateCode, Short biztype, String errorMsg, Integer mobilecount, Integer ipcount,
	        Integer devicecount, Short status, String sessionid, String referer, String cookiekey) {
		SmsLog smsLog = new SmsLog();
		smsLog.setIp(remoteip);
		smsLog.setPhone(phone);
		smsLog.setSmscode(code);
		smsLog.setTempcode(templateCode);
		smsLog.setType(biztype + "");
		smsLog.setStatus(status);
		smsLog.setErrormsg(errorMsg);
		smsLog.setPhone24count(mobilecount);
		smsLog.setIp24count(ipcount);
		if (devicecount != null) {
			smsLog.setDevicecount(devicecount);
		}
		if (StrUtil.isNotBlank(cookiekey)) {
			smsLog.setCookiekey(cookiekey);
		}
		smsLog.setSessionid(sessionid);
		smsLog.setReferer(referer);
		smsLog.save();
	}

}
