// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// 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 org.apache.cloudstack.ratelimit.integration;

import java.io.BufferedReader;
import java.io.EOFException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;

import com.google.gson.Gson;

import org.apache.cloudstack.api.response.SuccessResponse;

import com.cloud.api.ApiGsonHelper;
import com.cloud.utils.exception.CloudRuntimeException;

/**
 * Base class for API Test
 *
 */
public abstract class APITest {

    protected String rootUrl = "http://localhost:8080/client/api";
    protected String sessionKey = null;
    protected String cookieToSent = null;

    /**
     * Sending an api request through Http GET
     * @param command command name
     * @param params command query parameters in a HashMap
     * @return http request response string
     */
    protected String sendRequest(String command, HashMap<String, String> params) {
        try {
            // Construct query string
            StringBuilder sBuilder = new StringBuilder();
            sBuilder.append("command=");
            sBuilder.append(command);
            if (params != null && params.size() > 0) {
                Iterator<String> keys = params.keySet().iterator();
                while (keys.hasNext()) {
                    String key = keys.next();
                    sBuilder.append("&");
                    sBuilder.append(key);
                    sBuilder.append("=");
                    sBuilder.append(URLEncoder.encode(params.get(key), "UTF-8"));
                }
            }

            // Construct request url
            String reqUrl = rootUrl + "?" + sBuilder.toString();

            // Send Http GET request
            URL url = new URL(reqUrl);
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod("GET");

            if (!command.equals("login") && cookieToSent != null) {
                // add the cookie to a request
                conn.setRequestProperty("Cookie", cookieToSent);
            }
            conn.connect();

            if (command.equals("login")) {
                // if it is login call, store cookie
                String headerName = null;
                for (int i = 1; (headerName = conn.getHeaderFieldKey(i)) != null; i++) {
                    if (headerName.equals("Set-Cookie")) {
                        String cookie = conn.getHeaderField(i);
                        cookie = cookie.substring(0, cookie.indexOf(";"));
                        String cookieName = cookie.substring(0, cookie.indexOf("="));
                        String cookieValue = cookie.substring(cookie.indexOf("=") + 1, cookie.length());
                        cookieToSent = cookieName + "=" + cookieValue;
                    }
                }
            }

            // Get the response
            StringBuilder response = new StringBuilder();
            BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            try {
                while ((line = rd.readLine()) != null) {
                    response.append(line);
                }
            } catch (EOFException ex) {
                // ignore this exception
                System.out.println("EOF exception due to java bug");
            }
            rd.close();

            return response.toString();

        } catch (Exception e) {
            throw new CloudRuntimeException("Problem with sending api request", e);
        }
    }

    protected String createMD5String(String password) {
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new CloudRuntimeException("Error", e);
        }

        md5.reset();
        BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes()));

        // make sure our MD5 hash value is 32 digits long...
        StringBuffer sb = new StringBuffer();
        String pwStr = pwInt.toString(16);
        int padding = 32 - pwStr.length();
        for (int i = 0; i < padding; i++) {
            sb.append('0');
        }
        sb.append(pwStr);
        return sb.toString();
    }

    protected Object fromSerializedString(String result, Class<?> repCls) {
        try {
            if (result != null && !result.isEmpty()) {
                // get real content
                int start;
                int end;
                if (repCls == LoginResponse.class || repCls == SuccessResponse.class) {

                    start = result.indexOf('{', result.indexOf('{') + 1); // find
                                                                          // the
                                                                          // second
                                                                          // {

                    end = result.lastIndexOf('}', result.lastIndexOf('}') - 1); // find
                                                                                // the
                                                                                // second
                                                                                // }
                                                                                // backwards

                } else {
                    // get real content
                    start = result.indexOf('{', result.indexOf('{', result.indexOf('{') + 1) + 1); // find
                                                                                                   // the
                                                                                                   // third
                                                                                                   // {
                    end = result.lastIndexOf('}', result.lastIndexOf('}', result.lastIndexOf('}') - 1) - 1); // find
                                                                                                             // the
                                                                                                             // third
                                                                                                             // }
                                                                                                             // backwards
                }
                if (start < 0 || end < 0) {
                    throw new CloudRuntimeException("Response format is wrong: " + result);
                }
                String content = result.substring(start, end + 1);
                Gson gson = ApiGsonHelper.getBuilder().create();
                return gson.fromJson(content, repCls);
            }
            return null;
        } catch (RuntimeException e) {
            throw new CloudRuntimeException("Caught runtime exception when doing GSON deserialization on: " + result, e);
        }
    }

    /**
     * Login call
     * @param username user name
     * @param password password (plain password, we will do MD5 hash here for you)
     * @return login response string
     */
    protected void login(String username, String password) {
        //String md5Psw = createMD5String(password);
        // send login request
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("response", "json");
        params.put("username", username);
        params.put("password", password);
        String result = this.sendRequest("login", params);
        LoginResponse loginResp = (LoginResponse)fromSerializedString(result, LoginResponse.class);
        sessionKey = loginResp.getSessionkey();

    }
}
