package ru.yandex.autotests.direct.utils;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import ru.yandex.autotests.direct.Properties;
import ru.yandex.autotests.direct.data.ApiMode;
import ru.yandex.autotests.direct.data.Cmd;
import ru.yandex.autotests.direct.objects.advqstrings.Word;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNot.not;
import static ru.yandex.autotests.direct.data.AdvqConstants.MAX_ATTEMPTS_CONNECTION;

/**
 * Отправляет запросы к апи и принимает ответ.
 * @author xy6er
 */
public abstract class ApiHelper {
    private static final Logger LOGGER = LogManager.getLogger(ApiHelper.class);
    private static final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(Properties.getInstance().getConnectTimeout());
    private static final String DB_NAME = Properties.getInstance().getDbName();
    private static HttpClient httpClient;

    private static HttpClient getHttpClient() {
        if (httpClient == null) {
            RequestConfig config = RequestConfig.custom()
                    .setConnectTimeout(TIMEOUT)
                    .setConnectionRequestTimeout(TIMEOUT)
                    .setSocketTimeout(TIMEOUT)
                    .build();
            httpClient = HttpClients.custom().setDefaultRequestConfig(config).build();
        }
        return httpClient;
    }

    /**
     * Отправляет запрос к апи и возвращает ответ.
     * Если, после MAX_ATTEMPTS_CONNECTION попыток соединения ответ не получен, возвращается null
     * @param apiMode Тестируемая среда
     * @param cmd AdvQ команда
     * @param word Фраза, по которой нужен срез
     * @return Ответ апи или Error
     */
    public static String getApiResponse(ApiMode apiMode, Cmd cmd, Word word, Map<String, String> params) {
        String errorMessage = "";
        String url = getApiUrl(apiMode, cmd, word, params);
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Content-type", "text/html");

        for (int attempts = 1; attempts <= MAX_ATTEMPTS_CONNECTION; attempts++) {
            HttpResponse response = null;
            try {
                long startTime = System.currentTimeMillis();
                response = getHttpClient().execute(httpGet);
                long duration = System.currentTimeMillis() - startTime;
                LOGGER.info("Время для получения ответа: " + TimeUnit.MILLISECONDS.toSeconds(duration));

                assertThat("Response == null", response, notNullValue());
                assertThat("Ответ от api - 'Internal Server Error' по адресу " + url,
                        response.getStatusLine().getStatusCode(), not(equalTo(500)));
                assertThat("StatusCode от api не равен 200 по адресу " + url,
                        response.getStatusLine().getStatusCode(), equalTo(200));
                assertThat("Ответ от api null по адресу " + url,
                        response.getEntity().getContent(), notNullValue());

                String content = EntityUtils.toString(response.getEntity(), Charset.defaultCharset());
                LOGGER.info("Ответ:\n" + response.getStatusLine());
                LOGGER.info(content);
                return content;
            } catch (Throwable e) {
                errorMessage = "Ошибка: " + e.getMessage();
                e.printStackTrace();
                LOGGER.info("Не удалось соединиться к базе. Попытка №=" + attempts + "\n" + errorMessage, e);
            } finally {
                if (response != null) {
                    EntityUtils.consumeQuietly(response.getEntity());
                }
            }
        }

        throw new AdvqError("После трех попыток не удалось поключиться к базе: " + apiMode + "\n" + errorMessage);
    }

    public static String getApiResponse(ApiMode apiMode, Cmd cmd, Word word) {
        return getApiResponse(apiMode, cmd, word, new TreeMap<String, String>());
    }

    /**
     * Формирует урл запроса к апи.
     * @param apiMode Тестируемая среда
     * @param cmd AdvQ команда
     * @param word Фраза, по которой нужен срез
     * @return Урл запроса к апи
     */
    private static String getApiUrl(ApiMode apiMode, Cmd cmd, Word word, Map<String, String> params) {
        try {
            String url = String.format("%s%s?words=%s&dbname=%s",
                    apiMode.getUrl(), cmd, URLEncoder.encode(word.getValue(), "UTF-8"), DB_NAME);
            for (Map.Entry<String, String> entry : params.entrySet()) {
                String name = entry.getKey();
                String value = entry.getValue();
                url += "&" + name + "=" + value;
            }
            LOGGER.info("Урл для получения данных: " + url);
            return url;
        } catch (UnsupportedEncodingException ex) {
            throw new AdvqError("Не удалось конвертировать url в UTF-8", ex);
        }
    }

}
