/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.nacos.naming.misc;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.naming.boot.RunningConfig;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.Response;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.*;

/**
 * @author nacos
 */
public class NamingProxy {

    private static final String DATA_ON_SYNC_URL = "/distro/datum";

    private static final String DATA_GET_URL = "/distro/datum";

    private static final String ALL_DATA_GET_URL = "/distro/datums";

    private static final String TIMESTAMP_SYNC_URL = "/distro/checksum";

    public static void syncCheckSums(Map<String, String> checksumMap, String server) {

        try {
            Map<String, String> headers = new HashMap<>(128);

            headers.put("Client-Version", UtilsAndCommons.SERVER_VERSION);
            headers.put("User-Agent", UtilsAndCommons.SERVER_VERSION);
            headers.put("Connection", "Keep-Alive");

            HttpClient.asyncHttpPutLarge("http://" + server + RunningConfig.getContextPath()
                    + UtilsAndCommons.NACOS_NAMING_CONTEXT + TIMESTAMP_SYNC_URL + "?source=" + NetUtils.localServer(),
                headers, JSON.toJSONBytes(checksumMap),
                new AsyncCompletionHandler() {
                    @Override
                    public Object onCompleted(Response response) throws Exception {
                        if (HttpURLConnection.HTTP_OK != response.getStatusCode()) {
                            Loggers.DISTRO.error("failed to req API: {}, code: {}, msg: {}",
                                "http://" + server + RunningConfig.getContextPath() +
                                    UtilsAndCommons.NACOS_NAMING_CONTEXT + TIMESTAMP_SYNC_URL,
                                response.getStatusCode(), response.getResponseBody());
                        }
                        return null;
                    }

                    @Override
                    public void onThrowable(Throwable t) {
                        Loggers.DISTRO.error("failed to req API:" + "http://" + server
                            + RunningConfig.getContextPath()
                            + UtilsAndCommons.NACOS_NAMING_CONTEXT + TIMESTAMP_SYNC_URL, t);
                    }
                });
        } catch (Exception e) {
            Loggers.DISTRO.warn("NamingProxy", e);
        }
    }

    public static byte[] getData(List<String> keys, String server) throws Exception {

        Map<String, String> params = new HashMap<>(8);
        params.put("keys", StringUtils.join(keys, ","));
        HttpClient.HttpResult result = HttpClient.httpGetLarge("http://" + server + RunningConfig.getContextPath()
            + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_GET_URL, new HashMap<>(8), JSON.toJSONString(params));

        if (HttpURLConnection.HTTP_OK == result.code) {
            return result.content.getBytes();
        }

        throw new IOException("failed to req API: " + "http://" + server
            + RunningConfig.getContextPath()
            + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_GET_URL + ". code: "
            + result.code + " msg: " + result.content);
    }

    public static byte[] getAllData(String server) throws Exception {

        Map<String, String> params = new HashMap<>(8);
        HttpClient.HttpResult result = HttpClient.httpGet("http://" + server + RunningConfig.getContextPath()
            + UtilsAndCommons.NACOS_NAMING_CONTEXT + ALL_DATA_GET_URL, new ArrayList<>(), params);

        if (HttpURLConnection.HTTP_OK == result.code) {
            return result.content.getBytes();
        }

        throw new IOException("failed to req API: " + "http://" + server
            + RunningConfig.getContextPath()
            + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_GET_URL + ". code: "
            + result.code + " msg: " + result.content);
    }


    public static boolean syncData(byte[] data, String curServer) throws Exception {
        try {
            Map<String, String> headers = new HashMap<>(128);

            headers.put("Client-Version", UtilsAndCommons.SERVER_VERSION);
            headers.put("User-Agent", UtilsAndCommons.SERVER_VERSION);
            headers.put("Accept-Encoding", "gzip,deflate,sdch");
            headers.put("Connection", "Keep-Alive");
            headers.put("Content-Encoding", "gzip");

            HttpClient.HttpResult result = HttpClient.httpPutLarge("http://" + curServer + RunningConfig.getContextPath()
                + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_ON_SYNC_URL, headers, data);

            if (HttpURLConnection.HTTP_OK == result.code) {
                return true;
            }

            if (HttpURLConnection.HTTP_NOT_MODIFIED == result.code) {
                return true;
            }

            throw new IOException("failed to req API:" + "http://" + curServer
                + RunningConfig.getContextPath()
                + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_ON_SYNC_URL + ". code:"
                + result.code + " msg: " + result.content);
        } catch (Exception e) {
            Loggers.SRV_LOG.warn("NamingProxy", e);
        }
        return false;
    }

    public static String reqAPI(String api, Map<String, String> params, String curServer) throws Exception {
        try {
            List<String> headers = Arrays.asList("Client-Version", UtilsAndCommons.SERVER_VERSION,
                "User-Agent", UtilsAndCommons.SERVER_VERSION,
                "Accept-Encoding", "gzip,deflate,sdch",
                "Connection", "Keep-Alive",
                "Content-Encoding", "gzip");


            HttpClient.HttpResult result;

            if (!curServer.contains(UtilsAndCommons.IP_PORT_SPLITER)) {
                curServer = curServer + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort();
            }


            result = HttpClient.httpGet("http://" + curServer + api, headers, params);

            if (HttpURLConnection.HTTP_OK == result.code) {
                return result.content;
            }

            if (HttpURLConnection.HTTP_NOT_MODIFIED == result.code) {
                return StringUtils.EMPTY;
            }

            throw new IOException("failed to req API:" + "http://" + curServer + api + ". code:"
                + result.code + " msg: " + result.content);
        } catch (Exception e) {
            Loggers.SRV_LOG.warn("NamingProxy", e);
        }
        return StringUtils.EMPTY;
    }

    public static String reqAPI(String api, Map<String, String> params, String curServer, boolean isPost) throws Exception {
        try {
            List<String> headers = Arrays.asList("Client-Version", UtilsAndCommons.SERVER_VERSION,
                "User-Agent", UtilsAndCommons.SERVER_VERSION,
                "Accept-Encoding", "gzip,deflate,sdch",
                "Connection", "Keep-Alive",
                "Content-Encoding", "gzip");


            HttpClient.HttpResult result;

            if (!curServer.contains(UtilsAndCommons.IP_PORT_SPLITER)) {
                curServer = curServer + UtilsAndCommons.IP_PORT_SPLITER + RunningConfig.getServerPort();
            }

            if (isPost) {
                result = HttpClient.httpPost("http://" + curServer + RunningConfig.getContextPath()
                    + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/api/" + api, headers, params);
            } else {
                result = HttpClient.httpGet("http://" + curServer + RunningConfig.getContextPath()
                    + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/api/" + api, headers, params);
            }

            if (HttpURLConnection.HTTP_OK == result.code) {
                return result.content;
            }

            if (HttpURLConnection.HTTP_NOT_MODIFIED == result.code) {
                return StringUtils.EMPTY;
            }

            throw new IOException("failed to req API:" + "http://" + curServer
                + RunningConfig.getContextPath()
                + UtilsAndCommons.NACOS_NAMING_CONTEXT + "/api/" + api + ". code:"
                + result.code + " msg: " + result.content);
        } catch (Exception e) {
            Loggers.SRV_LOG.warn("NamingProxy", e);
        }
        return StringUtils.EMPTY;
    }

    public static class Request {

        private Map<String, String> params = new HashMap<>(8);

        public static Request newRequest() {
            return new Request();
        }

        public Request appendParam(String key, String value) {
            params.put(key, value);
            return this;
        }

        public String toUrl() {
            StringBuilder sb = new StringBuilder();
            for (String key : params.keySet()) {
                sb.append(key).append("=").append(params.get(key)).append("&");
            }
            return sb.toString();
        }
    }
}
