package org.springblade.app.controller;

import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springblade.core.log.logger.BladeLogger;
import org.springblade.core.tool.api.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author lidexi
 * @date 2021/5/12 15:39
 */
@RestController
@Slf4j
@RequestMapping("/huawei/sms")
@Api(tags = "华为短信接口")
public class HuaweiSmsController {
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private BladeLogger logger;

	//无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
	private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
	//无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
	private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";

	private static final String REDIS_KEY_CODE = "sms:code:";

	private static final String REDIS_KEY_TIME = "sms:times:";

	//APP接入地址+接口访问URI
	@Value("${sms.xlpark.url}")
	private	String url;

	//APP_Key
	@Value("${sms.xlpark.appKey}")
	private String appKey;

	//APP_Secret
	@Value("${sms.xlpark.appSecret}")
	private String appSecret;

	//国内短信签名通道号或国际/港澳台短信通道号
	@Value("${sms.xlpark.sender}")
	private String sender;

	//模板ID
	@Value("${sms.xlpark.templateId}")
	private String templateId;


	//模板ID
	@Value("${sms.xlpark.signature}")
	private String signature;

	//签名名称
	@PostMapping("/sendSms")
	public R sendSms(String phone){
		//该手机号码今天获取验证码的次数
		String redisTimesKey = REDIS_KEY_TIME + phone;
		String timesValue = redisTemplate.opsForValue().get(redisTimesKey);
		if(timesValue!=null&&Integer.valueOf(timesValue)>=5){//当天获取验证码次数超过5次
			return R.data("当天获取验证码次数超过5次，请使用微信快捷登录方式进行登录");
		}

		if(timesValue == null){
			timesValue ="0";
		}
		String redisCodeKey = REDIS_KEY_CODE + phone;
		String codeValue = redisTemplate.opsForValue().get(redisCodeKey);
		if(codeValue!=null){
			return R.data("已获取过验证码，请到手机短信复制验证码");
		}
		try{

			String code = ramdomTemplateParam();
			String templateParas = "[\""+code+"\"]"; //模板变量，此处以单变量验证码短信为例，请客户自行生成6位验证码，并定义为字符串类型，以杜绝首位0丢失的问题（例如：002569变成了2569）。

			//请求Body,不携带签名名称时,signature请填null
			String body = buildRequestBody(sender, phone, templateId, templateParas,"", signature);
			if (null == body || body.isEmpty()) {
				System.out.println("body is null.");
				return R.data("获取验证码失败");
			}

			//请求Headers中的X-WSSE参数值
			String wsseHeader = buildWsseHeader(appKey, appSecret);
			if (null == wsseHeader || wsseHeader.isEmpty()) {
				System.out.println("wsse header is null.");
				return R.data("获取验证码失败");
			}

			CloseableHttpClient client = HttpClients.custom()
				.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null,
					(x509CertChain, authType) -> true).build())
				.setSSLHostnameVerifier(new DefaultHostnameVerifier())
				.build();

			HttpResponse response = client.execute(RequestBuilder.create("POST")//请求方法POST
				.setUri(url)
				.addHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
				.addHeader(HttpHeaders.AUTHORIZATION, AUTH_HEADER_VALUE)
				.addHeader("X-WSSE", wsseHeader)
				.setEntity(new StringEntity(body)).build());


			System.out.println(response.toString()); //打印响应头域信息
			String resultEntity = EntityUtils.toString(response.getEntity());
			System.out.println(resultEntity); //打印响应消息实体
			JSONObject jsonObject = new JSONObject(resultEntity);
			if(jsonObject.get("code")!=null){
				String resultCode = jsonObject.get("code").toString();
				if(resultCode.equals("000000")){
					redisTemplate.opsForValue().set(redisCodeKey,code,5, TimeUnit.MINUTES);
					redisTemplate.opsForValue().set(redisTimesKey,String.valueOf(Integer.valueOf(timesValue)+1));
					Map map = new HashMap();
					map.put("phone",phone);
					map.put("code",code);
					logger.info("sendSms", JSONUtil.toJsonStr(map));
					return R.data("获取验证码成功");
				}
			}

		}catch (Exception e){
			return R.data("获取验证码失败");
		}

		return R.data("获取验证码失败");
	}

	//签名名称
	@PostMapping("/validCode")
	public R validCode(@RequestParam("phone") String phone, @RequestParam("code") String code){
		//测试阶段先不要用短信
		return R.data("success");
//		String redisCodeKey = REDIS_KEY_CODE + phone;
//		String codeValue = redisTemplate.opsForValue().get(redisCodeKey);
//		if(codeValue!=null&&codeValue.equals(code)){
//			return R.data("success");
//		}else if(codeValue==null){
//			return R.data("请先获取验证码");
//		}else if(codeValue!=null&&!codeValue.equals(code)){
//			return R.data("验证码错误");
//		}
//		return R.data("fail");
	}

	/**
	 * 构造请求Body体
	 * @param sender
	 * @param phone
	 * @param templateId
	 * @param templateParas
	 * @param statusCallbackUrl
	 * @param signature | 签名名称,使用国内短信通用模板时填写
	 * @return
	 */
	static String buildRequestBody(String sender, String phone, String templateId, String templateParas,
								   String statusCallbackUrl, String signature) {
		if (null == sender || null == phone || null == templateId || sender.isEmpty() || phone.isEmpty()
			|| templateId.isEmpty()) {
			System.out.println("buildRequestBody(): sender, receiver or templateId is null.");
			return null;
		}
		List<NameValuePair> keyValues = new ArrayList<NameValuePair>();

		keyValues.add(new BasicNameValuePair("from", sender));
		keyValues.add(new BasicNameValuePair("to", phone));
		keyValues.add(new BasicNameValuePair("templateId", templateId));
		if (null != templateParas && !templateParas.isEmpty()) {
			keyValues.add(new BasicNameValuePair("templateParas", templateParas));
		}
		if (null != statusCallbackUrl && !statusCallbackUrl.isEmpty()) {
			keyValues.add(new BasicNameValuePair("statusCallback", statusCallbackUrl));
		}
		if (null != signature && !signature.isEmpty()) {
			keyValues.add(new BasicNameValuePair("signature", signature));
		}

		return URLEncodedUtils.format(keyValues, Charset.forName("UTF-8"));
	}

	/**
	 * 构造X-WSSE参数值
	 * @param appKey
	 * @param appSecret
	 * @return
	 */
	static String buildWsseHeader(String appKey, String appSecret) {
		if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
			System.out.println("buildWsseHeader(): appKey or appSecret is null.");
			return null;
		}
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
		String time = sdf.format(new Date()); //Created
		String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce

		byte[] passwordDigest = DigestUtils.sha256(nonce + time + appSecret);
		String hexDigest = Hex.encodeHexString(passwordDigest);

		//如果JDK版本是1.8,请加载原生Base64类,并使用如下代码
		String passwordDigestBase64Str = Base64.getEncoder().encodeToString(hexDigest.getBytes()); //PasswordDigest
		//如果JDK版本低于1.8,请加载三方库提供Base64类,并使用如下代码
		//String passwordDigestBase64Str = Base64.encodeBase64String(hexDigest.getBytes(Charset.forName("utf-8"))); //PasswordDigest
		//若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正

		return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
	}


	private String ramdomTemplateParam(){

		int newNum = (int)((Math.random()*9+1)*100000);
		return String.valueOf(newNum);

	}

}
