package com.yd.rmi.tencent.wechatinterf.service.impl;

import com.yd.dal.service.transaction.TranLogDALService;
import com.yd.rmi.cache.SystemConfigService;
import com.yd.rmi.tencent.wechat.vo.accesstoken.AccessTokenByCodeRequest;
import com.yd.rmi.tencent.wechat.vo.accesstoken.AccessTokenByCodeResponse;
import com.yd.rmi.tencent.wechatinterf.pojo.accesstoken.AccessTokenRequest;
import com.yd.rmi.tencent.wechatinterf.pojo.accesstoken.AccessTokenResponse;
import com.yd.rmi.tencent.wechatinterf.pojo.authorize.AuthorizeRequest;
import com.yd.rmi.tencent.wechatinterf.pojo.jscode2session.Jscode2sessionRequest;
import com.yd.rmi.tencent.wechatinterf.pojo.jscode2session.Jscode2sessionResponse;
import com.yd.rmi.tencent.wechatinterf.pojo.ticket.TicketRequest;
import com.yd.rmi.tencent.wechatinterf.pojo.ticket.TicketResponse;
import com.yd.rmi.tencent.wechatinterf.pojo.token.TokenRequest;
import com.yd.rmi.tencent.wechatinterf.pojo.token.TokenResponse;
import com.yd.rmi.tencent.wechatinterf.pojo.unifiedorder.UnifiedorderResponse;
import com.yd.rmi.tencent.wechatinterf.service.WechatInterfService;
import com.yd.util.CommonUtil;
import com.yd.util.EncryptUtil;
import com.yd.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;


@Service("wechatInterfService")
public class WechatInterfServiceImpl implements WechatInterfService {
	private final static Logger logger = LoggerFactory.getLogger(WechatInterfServiceImpl.class);
	@Autowired
	private SystemConfigService systemConfigService;
	@Autowired
	private TranLogDALService tranLogDALService;

	/**
	 * 公众号支付时网页授权
	 * 1. 获取code
	 * @param authorizeRequest
	 * @return 
	 * @return
	 */
	@Override
	public void authorize(AuthorizeRequest authorizeRequest) {
		String p_appid = authorizeRequest.getAppid();
		String p_redirect_uri = authorizeRequest.getRedirect_uri(); 
		String p_response_type = authorizeRequest.getResponse_type();
		String p_scope = authorizeRequest.getScope(); 
		String p_state = authorizeRequest.getState();
		String url = systemConfigService.getSingleConfigValue("TENCENT_WECHAT_AUTHORIZE_URL")+"?"
				+"appid="+p_appid
				+"&redirect_uri="+p_redirect_uri
				+"&response_type="+p_response_type
				+"&scope="+p_scope
				+"&state="+p_state
				+"#wechat_redirect";
		
		RestTemplate template = new RestTemplate();
		String response = template.getForObject(url, String.class);

		tranLogDALService.logDB("wechat", "authorize", "out", p_appid, url, null, null);
		System.out.println("--------------response--------->"+response);
	}
	
	/**
	 * 公众号支付时网页授权，这里返回值有openid
	 * 2. 通过code换取accesstoken
	 * @param accessTokenRequest
	 * @return AccessTokenResponse
	 */
	@Override
	public AccessTokenResponse accessToken(AccessTokenRequest accessTokenRequest) {
		String p_appid = accessTokenRequest.getAppid() == null || "".equals(accessTokenRequest.getAppid()) ? systemConfigService.getSingleConfigValue("TENCENT_WECHAT_APPID"):accessTokenRequest.getAppid();
		String p_secret = accessTokenRequest.getSecret() == null || "".equals(accessTokenRequest.getSecret()) ? systemConfigService.getSingleConfigValue("TENCENT_WECHAT_APP_SECRET"):accessTokenRequest.getSecret();
		String p_grant_type = accessTokenRequest.getGrant_type() == null || "".equals(accessTokenRequest.getGrant_type()) ? systemConfigService.getSingleConfigValue("TENCENT_WECHAT_ACCESS_TOKEN_GRANT_TYPE") : accessTokenRequest.getGrant_type();
		String url = systemConfigService.getSingleConfigValue("TENCENT_WECHAT_ACCESS_TOKEN_URL")+"?"
						+"appid="+p_appid
						+"&secret="+p_secret
						+"&code="+accessTokenRequest.getCode()
						+"&grant_type="+p_grant_type;
		tranLogDALService.logDB("wechat", "accessToken2", "in", accessTokenRequest.getCode(), url, null, null);
		String responseStr = transaction(url,null,null);
		tranLogDALService.logDB("wechat", "accessToken2", "out", accessTokenRequest.getCode(), responseStr, null, null);
		
		return (AccessTokenResponse)JsonUtil.jsonToObj(responseStr,AccessTokenResponse.class);
	}
	
	/**
	 * 微信分享授权第一步
	 * 获取公众号的access_token
	 * 注意这和网页授权部分的access_token不是一回事！该接口有调用次数限制，且独立（不依赖于authorize接口）
	 * @param tokenRequest
	 * @return
	 */
	@Override
	public TokenResponse token(TokenRequest tokenRequest) {
		String p_appid = tokenRequest.getAppid() == null || "".equals(tokenRequest.getAppid()) ? systemConfigService.getSingleConfigValue("TENCENT_WECHAT_APPID"):tokenRequest.getAppid();
		String p_grant_type = tokenRequest.getGrant_type() == null || "".equals(tokenRequest.getGrant_type()) ? systemConfigService.getSingleConfigValue("TENCENT_WECHAT_TOKEN_GRANT_TYPE") : tokenRequest.getGrant_type();
		String p_secret = tokenRequest.getSecret() == null || "".equals(tokenRequest.getSecret()) ? systemConfigService.getSingleConfigValue("TENCENT_WECHAT_APP_SECRET") : tokenRequest.getSecret();
		String url = systemConfigService.getSingleConfigValue("TENCENT_WECHAT_TOKEN_URL")+"?"
						+"grant_type="+p_grant_type
						+"&appid="+p_appid
						+"&secret="+p_secret;

		System.out.println("【》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》【微信分享 --4--token-url】"+url+"】");
		tranLogDALService.logDB("wechat", "accessToken", "in", tokenRequest.getAppid(), url, null, null);
		String responseStr = transaction(url,null,null);
		tranLogDALService.logDB("wechat", "accessToken", "out", tokenRequest.getAppid(), responseStr, null, null);
		
		return (TokenResponse)JsonUtil.jsonToObj(responseStr,TokenResponse.class);
	}
	
	/**
	 * 微信分享授权第二步
	 * 获取jsapi_ticket
	 * @param ticketRequest
	 * @return
	 */
	@Override
	public TicketResponse ticket(TicketRequest ticketRequest) {
		String p_type = ticketRequest.getType() == null || "".equals(ticketRequest.getType()) ? systemConfigService.getSingleConfigValue("TENCENT_WECHAT_TICKET_TYPE"):ticketRequest.getType();
		String url = systemConfigService.getSingleConfigValue("TENCENT_WECHAT_TICKET_URL")+"?"
						+"access_token="+ticketRequest.getAccessToken()
						+"&type="+p_type;

		tranLogDALService.logDB("wechat", "ticket", "in", ticketRequest.getAccessToken(), url, null, null);
		String responseStr = transaction(url,null,null);
		tranLogDALService.logDB("wechat", "ticket", "out", ticketRequest.getAccessToken(), responseStr, null, null);
		
		return (TicketResponse) JsonUtil.jsonToObj(responseStr,TicketResponse.class);
	}

	/**
	 * 小程序wx.login登录凭证校验,使用 临时登录凭证code 获取 session_key 和 openid 等
	 * @param request
	 * @return Jscode2sessionResponse
	 */
	@Override
	public Jscode2sessionResponse jscode2session(Jscode2sessionRequest request) {
		Jscode2sessionResponse response = null;
		String appid = CommonUtil.isNullOrBlank(request.getAppid()) ? systemConfigService.getSingleConfigValue("TENCENT_MINI_PROGRAM_APPID"):request.getAppid();
		String secret = CommonUtil.isNullOrBlank(request.getSecret()) ? systemConfigService.getSingleConfigValue("TENCENT_MINI_PROGRAM_SECRET"):request.getSecret();
		String grantType = CommonUtil.isNullOrBlank(request.getGrant_type()) ? "authorization_code":request.getGrant_type();
		String url = systemConfigService.getSingleConfigValue("TENCENT_JSCODE2SESSION_URL")+"?"
				+"appid="+appid
				+"&secret="+secret
				+"&js_code="+request.getJs_code()
				+"&grant_type="+grantType;

		String responseStr = transaction(url,null,null);
		try{
			response = (Jscode2sessionResponse)JsonUtil.jsonToObj(responseStr, Jscode2sessionResponse.class);;
		}catch(Exception e){

		}
		return response;
	}

	/**
     * 签名算法
     * 方法用途：对所有传入参数按照字段名的Unicode码从小到大排序（字典序），并生成url参数串
     * @param paraMap 需要排序的Map对象
     * @param urlEncode 是否需要RULENCODE
     * @param keyToLower 是否需要将Key转换为全小写 true：key转化成小写，false：不转化
     */
    private static String getSignValue(Map<String, String> paraMap,boolean urlEncode,boolean keyToLower,String key){
        String signValue = null;
        String tmpString = null;
        if (paraMap!=null) {
            try {
                Map<String , String> tmpMap = paraMap;
                //将map中的映射关系存入到一个list集合中，而这个关系的数据类型就是Map.Entry
                List<Entry<String, String>> infoIds = new ArrayList<Entry<String, String>>(tmpMap.entrySet());
                //对所有传入的参数按照字段名的ASCII码从小到到排序（字典序）
                Collections.sort(infoIds,new Comparator<Entry<String, String>>() {
                	@Override
                    public int compare(Entry<String, String> o1, Entry<String, String> o2) {
                        return (o1.getKey()).toString().compareTo(o2.getKey());
                    }
                });
                //构建URL键值对的格式
                StringBuilder buf = new StringBuilder();
                for (Entry<String, String> item : infoIds){
                    if(item.getKey()!=null){
                        String infokey = item.getKey();
                        String value = item.getValue();
                        if(value != null && !"".equals(value)){
                        	 if (urlEncode) {
                                 value= URLEncoder.encode(value,"UTF-8");
                             }
                             if (keyToLower) {
                                 buf.append(infokey.toLowerCase()).append("=").append(value);
                             }else{
                                 buf.append(infokey).append("=").append(value);
                             }
                             buf.append("&");
                        }
                    }
                }
                tmpString = buf.toString();
                if (!tmpString.isEmpty()) {
                    tmpString = tmpString.substring(0,tmpString.length()-1);
                }
                System.out.println("----tmpString-----"+tmpString);
            } catch (Exception e) {
                System.out.println("----出错了!!!!-----");
                return null;
            }
            
        }
        String stringSignTemp = tmpString+"&key="+key;
        return EncryptUtil.encrypt(stringSignTemp,null).toUpperCase();
    }
    /**
     * 对响应的报文进行校验
     */
	@Override
	public boolean signValite(UnifiedorderResponse unifiedorderResponse) {
		boolean result = false;
		String key = null;
		String code = null;
		code = unifiedorderResponse.getReturn_code();
		if ("SUCCESS".equals(code)) {//对返回报文进行签名校验
			String return_code = unifiedorderResponse.getReturn_code();
			String return_msg = unifiedorderResponse.getReturn_msg();
			String device_infoResp = unifiedorderResponse.getDevice_info();
			String nonce_strResp = unifiedorderResponse.getNonce_str();
			String appid = unifiedorderResponse.getAppid();
			String mch_id = unifiedorderResponse.getMch_id();
			String sign = unifiedorderResponse.getSign();
			String openid = unifiedorderResponse.getOpenid();
			String trade_type = unifiedorderResponse.getTrade_type();
			String result_code = unifiedorderResponse.getResult_code();
			String prepay_id = unifiedorderResponse.getPrepay_id();
			String err_code = unifiedorderResponse.getErr_code();
			String err_code_des = unifiedorderResponse.getErr_code_des();
			String code_url = unifiedorderResponse.getCode_url();
			
			if(appid.equals("YD-TENCENT_WECHAT_APPID")){
				key = systemConfigService.getSingleConfigValue("YD-TENCENT_WECHAT_API_KEY");//第三方用户唯一凭证密钥
			}else if (appid.equals("TENCENT_WECHAT_APPID")) {
				key = systemConfigService.getSingleConfigValue("TENCENT_WECHAT_API_KEY");
			}
			Map<String,String> paraMap = new HashMap<String,String>();
			paraMap.put("device_info", device_infoResp);
			paraMap.put("return_code", return_code);
			paraMap.put("return_msg", return_msg);
			paraMap.put("appid", appid);
			paraMap.put("mch_id", mch_id);
			paraMap.put("nonce_str", nonce_strResp);
			paraMap.put("openid", openid);
			paraMap.put("result_code", result_code);
			paraMap.put("err_code", err_code);
			paraMap.put("err_code_des", err_code_des);
			if("SUCCESS".equals(result_code)){
				paraMap.put("trade_type", trade_type);
				paraMap.put("prepay_id", prepay_id);
				paraMap.put("code_url", code_url);
			}
			
			String signValue = getSignValue(paraMap,false,false,key);
			
			if (sign.equals(signValue)) {
				result = true;
			}
		
	    }
		return result;
	}
	@Override
	public String transaction(String httpURL,String requestXML,String requestMethod){
		HttpsURLConnection conn = null; 
		OutputStream out = null; 
		InputStream in = null;
		try {
			URL url = new URL(httpURL);
			conn = (HttpsURLConnection) url.openConnection();
			conn.setReadTimeout(30000);
			conn.setDoInput(true);
			conn.setDoOutput(true);
			conn.setAllowUserInteraction(true);
			if (requestMethod != null) {
				conn.setRequestMethod(requestMethod);  
				conn.setRequestProperty("Content-type", "text/html");
			} 
			conn.setRequestProperty("Accept-Charset", "utf-8");  
			conn.setRequestProperty("contentType", "utf-8");  
			conn.setHostnameVerifier(new HostnameVerifier() {
				@Override
				public boolean verify(String arg0, SSLSession arg1) {
					return true;
				}
			});
			conn.connect();
			if(requestXML != null && !"".equals(requestXML)){
				requestXML = URLEncoder.encode(requestXML, "utf-8");
				out = conn.getOutputStream();
				out.write(requestXML.getBytes("utf-8"));
			}
			in  = conn.getInputStream();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				if(out != null){out.close();}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
		StringBuffer sbf = new StringBuffer();
		if(in != null){
			try {
				BufferedReader reader = new BufferedReader(new InputStreamReader(in,"UTF-8"));
				String str = null ;
				while((str = reader.readLine()) != null){
					sbf.append(str);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sbf.toString();
	}

	/**
	 * 获取access_token:access_token是公众号的全局唯一接口调用凭据，公众号调用各接口时都需使用access_token
	 */
	@Override
	public AccessTokenResponse getAccessToken(AccessTokenRequest accessTokenRequest) {
		String appId = accessTokenRequest.getAppid();
		String grantType = accessTokenRequest.getGrant_type();
		String secret = accessTokenRequest.getSecret();
		String url = systemConfigService.getSingleConfigValue("TENCENT_WECHAT_TOKEN_URL")+"?"
				+"grant_type="+ grantType
				+"&appid="+appId
				+"&secret="+ secret;

		System.out.println("【》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》【微信分享 --4--token-url】"+url+"】");
		tranLogDALService.logDB("wechat", "accessToken", "in", accessTokenRequest.getAppid(), url, null, null);
		String responseStr = weChatTransaction(url,null);
		tranLogDALService.logDB("wechat", "accessToken", "out", accessTokenRequest.getAppid(), responseStr, null, null);

		return (AccessTokenResponse)JsonUtil.jsonToObj(responseStr, AccessTokenResponse.class);
	}

	/**
	 * 通过code换取accessToken,公众号支付时网页授权，这里返回值有openid
	 */
	@Override
	public AccessTokenByCodeResponse getAccessTokenByCode(AccessTokenByCodeRequest accessTokenByCodeRequest) {
		String appId = accessTokenByCodeRequest.getAppid();
		String secret = accessTokenByCodeRequest.getSecret();
		String grantType = accessTokenByCodeRequest.getGrant_type();
		String url = systemConfigService.getSingleConfigValue("TENCENT_WECHAT_ACCESS_TOKEN_URL")+"?"
				+"appid="+ appId
				+"&secret="+ secret
				+"&code="+ accessTokenByCodeRequest.getCode()
				+"&grant_type="+ grantType;
		tranLogDALService.logDB("wechat", "accessTokenByCode", "in", accessTokenByCodeRequest.getCode(), url, null, null);
		String responseStr = weChatTransaction(url,null);
		tranLogDALService.logDB("wechat", "accessTokenByCode", "out", accessTokenByCodeRequest.getCode(), responseStr, null, null);

		return (AccessTokenByCodeResponse)JsonUtil.jsonToObj(responseStr, AccessTokenByCodeResponse.class);
	}

	/**
	 * 微信接口交互
	 */
	@Override
	public String weChatTransaction(String httpURL, String requestXML){
		String responseXML;
		OutputStream output = null;
		InputStream input = null;
		try {
			URL url = new URL(httpURL);
			HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
			connection.setReadTimeout(30000);
			connection.setDoInput(true);
			connection.setDoOutput(true);
			connection.setAllowUserInteraction(true);
			connection.setRequestProperty("Accept-Charset", "utf-8");
			connection.setRequestProperty("contentType", "utf-8");
			connection.setHostnameVerifier(new HostnameVerifier() {
				@Override
				public boolean verify(String hostname, SSLSession session) {
					return true;
				}
			});
			connection.connect();
			if (requestXML != null && !"".equals(requestXML)) {
				output = connection.getOutputStream();
				output.write(requestXML.getBytes(StandardCharsets.UTF_8));
			}
			input = connection.getInputStream();
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			try {
				if (output != null) {
					output.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		StringBuilder stringBuffer = new StringBuilder();
		if (input != null) {
			try {
				BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
				String string ;
				while ((string = reader.readLine()) != null) {
					stringBuffer.append(string);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		responseXML = stringBuffer.toString();
		return responseXML;




		/*
		InputStream in = null;
		String responseXML = null;
		try {
			URL url = new URL(httpURL);
			StringBuilder sbf = new StringBuilder();
			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
			conn.setReadTimeout(30000);
			conn.setDoInput(true);
			conn.setDoOutput(true);
			conn.setAllowUserInteraction(true);
			conn.setRequestProperty("Accept-Charset", "utf-8");
			conn.setRequestProperty("contentType", "utf-8");
			conn.setHostnameVerifier((arg0, arg1) -> true);
			conn.connect();
			in  = conn.getInputStream();
			if(in != null){
				BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
				String str;
				while((str = reader.readLine()) != null){
					sbf.append(str);
				}
				responseXML = sbf.toString();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				if(in != null){in.close();}
			} catch (Exception e3) {
				e3.printStackTrace();
			}
		}
		return responseXML;

		 */
	}

}