package com.zbkj.service.wangshang.api.internal.util;

import com.zbkj.service.wangshang.api.MybankApiException;
import com.zbkj.service.wangshang.api.MybankApiExceptionEnum;
import com.zbkj.service.wangshang.api.MybankConstants;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * 网络工具类
 */
public class WebUtils {

    private static SSLContext sslContext = null;

    private static HostnameVerifier verifier = null;

    private static SSLSocketFactory socketFactory = null;

    private static class DefaultTrustManager implements X509TrustManager {
        public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate,
                                       String paramString) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate,
                                       String paramString) throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

    static {

        try {
            sslContext = SSLContext.getInstance(MybankConstants.SSL, MybankConstants.SUN_JSSE);
            sslContext.init(null, new TrustManager[]{new DefaultTrustManager()}, new SecureRandom());

            sslContext.getClientSessionContext().setSessionTimeout(15);
            sslContext.getClientSessionContext().setSessionCacheSize(1000);

            socketFactory = sslContext.getSocketFactory();
        } catch (Exception e) {
            MybankLogger.logBizError(e);
        }

        verifier = new HostnameVerifier() {
            public boolean verify(String paramString, SSLSession paramSSLSession) {
                return true;
            }
        };

    }

    /**
     * 执行 HTTP/HTTPS POST请求
     *
     * @param serverUrl      服务请求地址
     * @param xmlContent     请求发送内容
     * @param cookieValue    服务路由分组值
     * @param charset        字符编码
     * @param connectTimeout 连接超时时间
     * @param readTimeout    读取超时时间
     * @param apiFunction    API接口代码
     * @param apiVersion     API接口版本
     * @return 请求处理结果
     * @throws IOException
     */
    public static String doPost(String serverUrl, String xmlContent, String cookieValue, String charset, int connectTimeout,
                                int readTimeout, String apiFunction, String apiVersion) throws MybankApiException {
        HttpURLConnection conn = null;
        OutputStreamWriter out = null;
        String rsp = null;
        try {
            try {
                conn = getConnection(new URL(serverUrl), MybankConstants.METHOD_POST, xmlContent.length());
                if (!StringUtils.isEmpty(cookieValue)) {
                    conn.setRequestProperty(MybankConstants.COOKIE, cookieValue);
                }
                conn.setConnectTimeout(connectTimeout);
                conn.setReadTimeout(readTimeout);
            } catch (IOException e) {
                MybankLogger.logCommError(e, serverUrl, xmlContent, apiFunction, apiVersion);
                throw new MybankApiException(MybankApiExceptionEnum.SERVER_SYSTEM_EXCEPTION, e);
            }
            try {
                out = new OutputStreamWriter(conn.getOutputStream(), charset);
                out.write(xmlContent);
                out.flush();
                rsp = getResponseAsString(conn);
            } catch (IOException e) {
                MybankLogger.logCommError(e, conn, xmlContent, apiFunction, apiVersion);
                throw new MybankApiException(MybankApiExceptionEnum.REQUEST_REMOTE_SERVER_ERROR, e);
            }
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    throw new MybankApiException(MybankApiExceptionEnum.SERVER_SYSTEM_EXCEPTION, e);
                }
            }
            if (conn != null) {
                conn.disconnect();
            }
        }
        return rsp;
    }

    private static HttpURLConnection getConnection(URL url, String method, int length) throws IOException {
        HttpURLConnection conn = null;
        if (MybankConstants.URL_PROTOCOL_HTTPS.equals(url.getProtocol())) {
            HttpsURLConnection connHttps = null;
            connHttps = (HttpsURLConnection) url.openConnection();
            connHttps.setHostnameVerifier(verifier);
            connHttps.setSSLSocketFactory(socketFactory);
            conn = connHttps;
        } else {
            conn = (HttpURLConnection) url.openConnection();
        }
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setRequestMethod(method);
        conn.setRequestProperty(MybankConstants.CONTENT_TYPE, MybankConstants.APPLICATION_XML);
        conn.setRequestProperty(MybankConstants.CONTENT_LENGTH, String.valueOf(length));
        return conn;
    }

    protected static String getResponseAsString(HttpURLConnection conn) throws IOException {
        String charset = getResponseCharset(conn.getContentType());
        InputStream es = conn.getErrorStream();
        if (es == null) {
            return getStreamAsString(conn.getInputStream(), charset);
        } else {
            String msg = getStreamAsString(es, charset);
            if (StringUtils.isEmpty(msg)) {
                throw new IOException(conn.getResponseCode() + MybankConstants.COLON + conn.getResponseMessage());
            } else {
                throw new IOException(msg);
            }
        }
    }

    private static String getResponseCharset(String ctype) {
        String charset = MybankConstants.CHARSET_UTF8;
        if (!StringUtils.isEmpty(ctype)) {
            String[] params = ctype.split(MybankConstants.SEMI_COLON);
            for (String param : params) {
                param = param.trim();
                if (param.startsWith(MybankConstants.CHARSET)) {
                    String[] pair = param.split(MybankConstants.EQUAL_SYMBOL, 2);
                    if (pair.length == 2) {
                        if (!StringUtils.isEmpty(pair[1])) {
                            charset = pair[1].trim();
                        }
                    }
                    break;
                }
            }
        }
        return charset;
    }

    private static String getStreamAsString(InputStream stream, String charset) throws IOException {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(stream, charset));
            StringWriter writer = new StringWriter();
            char[] chars = new char[256];
            int count = 0;
            while ((count = reader.read(chars)) > 0) {
                writer.write(chars, 0, count);
            }
            return writer.toString();
        } finally {
            if (stream != null) {
                stream.close();
            }
        }
    }
}