package org.dromara.settlement.common.bank.minsheng;

import com.alibaba.fastjson.JSONObject;

import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.alibaba.nacos.shaded.com.google.gson.GsonBuilder;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
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.conn.ssl.SSLConnectionSocketFactory;
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.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.settlement.api.domain.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.*;

public class MinShengUtil {
	private static final Logger log = LoggerFactory.getLogger(MinShengUtil.class);

	private static class SingletonHolder {
		private static final MinShengUtil INSTANCE = new MinShengUtil();
	}

	/**
	 * 初始化状态
	 */
	private static boolean sslInited = false;
	private static SSLConnectionSocketFactory sslsf;

	private MinShengUtil() {

	}

	public static final MinShengUtil getInstance() {
		return SingletonHolder.INSTANCE;
	}

	public static void main(String[] args) {
		System.out.println(5%2);
	}

	private void initSSL(String clientKeyStoryPath, String clientKeyStoryPassword) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException {
		//构造SSL环境
		SSLContext sslContext = SSLContext.getInstance("SSL");
		//keyStore类型，JKS、pkcs12
		KeyStore keyStore = KeyStore.getInstance("pkcs12");
		//创建jks密钥访问库
		if (null != clientKeyStoryPassword && null != clientKeyStoryPath && clientKeyStoryPassword.length() > 0 && clientKeyStoryPath.length() > 0) {
			keyStore.load(new FileInputStream(clientKeyStoryPath), clientKeyStoryPassword.toCharArray());
		} else {
			throw new RuntimeException("SSL认证证书路径或密码为空。");
		}
		//创建管理jks密钥库的x509密钥管理器，用来管理密钥，需要key的密码
		KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
		keyManagerFactory.init(keyStore, clientKeyStoryPassword.toCharArray());
		//只允许使用TLSv1协议
		sslContext.init(keyManagerFactory.getKeyManagers(), null, null);
		HostnameVerifier ignoreHostnameVerifier = new HostnameVerifier() {
			public boolean verify(String hostname, SSLSession session) {
				return true;
			}
		};
		sslsf = new SSLConnectionSocketFactory(sslContext, new String[]{"TLSv1"}, null, ignoreHostnameVerifier);
	}

	public Result<String> request(MinShengConfig config, String transCode, String reqid, String body) {
		// AES加密
		String request = "";
		String res = "";
		CloseableHttpClient httpclient = null;
		CloseableHttpResponse response = null;
		try {
			List<NameValuePair> params = null;
			boolean securitySwitch = config.securitySwitch == null || config.securitySwitch;
			//加密和加签
			if (securitySwitch) {
				EcpSecurityResult sr = EcpSecurityUtil.encryptAndSign(body, config.sm2MhtPriCertPath, config.sm2MhtPriCertPsword,
						config.sm2CmbcPubCert);
				// 组装form表单数据
				params = getRequestForm(reqid,transCode,sr.getSignature(), sr.getCipherText(), config);
			} else {
				params = getRequestForm(reqid,transCode,null, body, config);
			}
			// 是否使用https
			boolean isSSL = config.isSSL;
			if (isSSL) {
				if (!sslInited) {
					initSSL(config.rsaCertPath, config.rsaCertPsword);
				}
				httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
			} else {
				httpclient = HttpClients.custom().build();
			}
			HttpPost httpPost = new HttpPost(config.URL);
			RequestConfig httpConfig = RequestConfig.custom().setSocketTimeout(60000).setConnectTimeout(60000)
					.setConnectionRequestTimeout(60000).build();
			httpPost.setConfig(httpConfig);
			UrlEncodedFormEntity reqEntity = new UrlEncodedFormEntity(params, MinShengConstants.DEFAULT_CHARSET);
			httpPost.setEntity(reqEntity);
			try {
				StringWriter writer = new StringWriter();
				IOUtils.copy(reqEntity.getContent(), writer, "utf-8");
				request = URLDecoder.decode(writer.toString(), "UTF-8");
			} catch (IOException e) {
				log.error("民生银行api参数解析出错：", e);
			}
			log.info("request: (" + config.URL+"),"+request);
			response = httpclient.execute(httpPost);
			if(response == null){
				throw new RuntimeException("发送请求后返回报文为空");
			}
			HttpEntity respEntity = response.getEntity();
			if (respEntity != null) {
				res = EntityUtils.toString(respEntity, MinShengConstants.DEFAULT_CHARSET);
			}
			log.info("response: " + res);
		} catch (Exception e) {
			// TODO 自动生成的 catch 块
			log.error("民生银行api调用出错（" + JSONObject.toJSONString(config) + "," + transCode + "," + reqid + "," + body
					+ "）：", e);
		} finally {
			try {
				if (null != response) {
					response.close();
				}
				if (null != httpclient) {
					httpclient.close();
				}
			} catch (IOException e) {
				log.error("民生银行api调用时IO出错（" + JSONObject.toJSONString(config) + "," + transCode + "," + reqid + "," + body
						+ "）：", e);
			}
		}
		Result<String> rtn = new Result<String>();
		rtn.setResult(request);
		rtn.setRtnData(res);
		return rtn;
	}

	public Result<MinShengResponse> response(MinShengConfig config, String response) {
		// 解密请求
		Result<MinShengResponse> rtn = new Result<MinShengResponse>();
		if (StringUtils.isBlank(response)) {
			rtn.addError("响应内容不能为空");
			return rtn;
		}
		try {
			Gson gson = new GsonBuilder().create();
			Map<String, String> headMap = gson.fromJson(response, Map.class);
			if (headMap == null) {
				rtn.addError("响应内容不能为空");
				return rtn;
			}
			MinShengResponse data = new MinShengResponse();
			data.setTransCode(headMap.get("transCode"));
			data.setReqSeq(headMap.get("reqSeq"));
			data.setRespSeq(headMap.get("respSeq"));
			data.setMerchantNum(headMap.get("merchantNum"));
			data.setTxTime(headMap.get("txTime"));
			data.setRespDate(headMap.get("respDate"));
			data.setRespTime(headMap.get("respTime"));
			data.setRespStatus(headMap.get("respStatus"));
			data.setRespDesc(headMap.get("respDesc"));
			data.setRespCode(headMap.get("respCode"));
			data.setCharset(headMap.get("charset"));
			data.setRemark(headMap.get("remark"));
			data.setReserve(headMap.get("reserve"));
			data.setReserve2(headMap.get("reserve2"));
			data.setSgntr(headMap.get("sgntr"));
			String body = headMap.get("body");
			if (StringUtils.isNotEmpty(body)){
				String bodyJson = body;
				if (config.securitySwitch) {
					//log.debug("开始解密验签。密文：" + body);
					EcpSecurityResult sr = EcpSecurityUtil.decryptAndVerify(body, data.getSgntr(),
							config.sm2MhtPriCertPath, config.sm2MhtPriCertPsword, config.sm2CmbcPubCert);
					if (StringUtils.isEmpty(sr.getCipherText())) {
						rtn.addError("解密失败");
						return rtn;
					}
					if (!sr.isVerifyPass()) {
						rtn.addError("验签失败");
						return rtn;
					}
					bodyJson = sr.getPlainText();
				}
				data.setData(JSONObject.parseObject(bodyJson));
			}
			data.setReqid(data.getReqSeq());
			data.setResultcode(data.getRespStatus());
			data.setResultmsg(data.getRespDesc() + "（"+data.getRespCode()+"）");
			rtn.setRtnData(data);
		} catch (Exception e) {
			// TODO 自动生成的 catch 块
			log.error("解析民生银行响应出错（" + JSONObject.toJSONString(config) + "," + response + "）：", e);
		}
		return rtn;
	}

	/**
	 * 封装请求的form表单
	 *
	 * @param sgntr
	 * @param body
	 * @param clientConfig
	 * @return
	 */
	private List<NameValuePair> getRequestForm(
			String reqid, String transCode,String sgntr, String body, MinShengConfig clientConfig) {
		List<NameValuePair> form = new ArrayList<NameValuePair>();
		form.add(new BasicNameValuePair(MinShengConstants.TRANSCODE, transCode));
		form.add(new BasicNameValuePair(MinShengConstants.PRODUCT_CODE, clientConfig.productCode));
		form.add(new BasicNameValuePair(MinShengConstants.MERCHANT_NUM, clientConfig.merchantNum));
		form.add(new BasicNameValuePair(MinShengConstants.SYSTEM_CODE, clientConfig.systemCode));
		form.add(new BasicNameValuePair(MinShengConstants.TRANSTYPE, MinShengConstants.TRANSL_TYPE));
		form.add(new BasicNameValuePair(MinShengConstants.VERSION, MinShengConstants.VERSION_02));
		form.add(new BasicNameValuePair(MinShengConstants.REQ_SEQ, reqid));
		String reqTime = new SimpleDateFormat("yyyyMMddHHmmssSSS")
				.format(new Date(System.currentTimeMillis()));
		form.add(new BasicNameValuePair(MinShengConstants.REQ_TIME, reqTime));
		form.add(new BasicNameValuePair(MinShengConstants.TRANS_DATE, reqTime.substring(0, 8)));
		form.add(new BasicNameValuePair(MinShengConstants.TRANS_TIME, reqTime.substring(8, 17)));
		form.add(new BasicNameValuePair(MinShengConstants.CHARSET, MinShengConstants.DEFAULT_CHARSET));

		form.add(new BasicNameValuePair(MinShengConstants.REMARK, ""));
		form.add(new BasicNameValuePair(MinShengConstants.RESERVE, ""));
		form.add(new BasicNameValuePair(MinShengConstants.RESERVE2, ""));

		form.add(new BasicNameValuePair(MinShengConstants.SGNTR, sgntr));
		form.add(new BasicNameValuePair(MinShengConstants.BODY, body));
		return form;
	}
}
