package org.springblade.common.upyzt.net;

import org.springblade.common.upyzt.Upyzt;
import org.springblade.common.upyzt.exception.APIConnectionException;
import org.springblade.common.upyzt.exception.AuthenticationException;
import org.springblade.common.upyzt.util.StreamUtils;
import org.springblade.common.upyzt.util.UpyztSignature;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HttpURLConnectionClient extends HttpClient {
    /**
     * Initializes a new instance of the {@link org.springblade.common.upyzt.net.HttpURLConnectionClient}.
     */
    public HttpURLConnectionClient() {
        super();
    }

    /**
     * Sends the given request to Upyzt's API.
     *
     * @param request the request
     * @return the response
     * @throws APIConnectionException if an error occurs when sending or receiving
     */
    @Override
    public UpyztResponse request(UpyztRequest request) throws APIConnectionException, AuthenticationException {
        HttpURLConnection conn = null;

        try {
            conn = createUpyztConnection(request);

            // trigger the request
            int responseCode = conn.getResponseCode();
            HttpHeaders headers = HttpHeaders.of(conn.getHeaderFields());
            String responseBody;

            if (responseCode >= 200 && responseCode < 300) {
                responseBody = StreamUtils.readToEnd(conn.getInputStream(), APIResource.CHARSET);
                boolean verified = UpyztSignature.verify(headers, responseBody,
                        request.options.getVerifyPublicKey(), APIResource.CHARSET.name());
                if (!verified) {
                    throw new AuthenticationException("响应签名验证失败，请检查验签公钥是否正确");
                }
            } else {
                responseBody = StreamUtils.readToEnd(conn.getErrorStream(), APIResource.CHARSET);
            }

            return new UpyztResponse(responseCode, responseBody, headers);
        } catch (IOException e) {
            throw new APIConnectionException(
                    String.format(
                            "请求 Upyzt (%s) 失败: %s，请检查网络连接是否正常（例如是否开启代理等）后再重试",
                            Upyzt.getApiBase(), e.getMessage()),
                    e);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    static HttpHeaders getHeaders(UpyztRequest request) {
        Map<String, List<String>> userAgentHeadersMap = new HashMap<>();

        userAgentHeadersMap.put("User-Agent", Collections.singletonList(buildUserAgentString()));
        userAgentHeadersMap.put(
                "X-Upyzt-Client-User-Agent", Collections.singletonList(buildXUpyztClientUserAgentString()));

        return request.getHeaders().withAdditionalHeaders(userAgentHeadersMap);
    }

    private static HttpURLConnection createUpyztConnection(UpyztRequest request)
            throws IOException {
        HttpURLConnection conn = (HttpURLConnection) request.url.openConnection();
        conn.setRequestMethod(request.method.name());
        conn.setConnectTimeout(request.options.getConnectTimeout());
        conn.setReadTimeout(request.options.getReadTimeout());
        conn.setUseCaches(false);
        for (Map.Entry<String, List<String>> entry : getHeaders(request).map().entrySet()) {
            conn.setRequestProperty(entry.getKey(), String.join(",", entry.getValue()));
        }
        if (request.content != null) {
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", request.content.getContentType());

            try (OutputStream output = conn.getOutputStream()) {
                request.writeOutputStream(output);
            }
        }

        return conn;
    }
}
