package ru.yandex.webmaster3.storage.host.service;

import org.apache.commons.io.Charsets;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.HttpConstants;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.metrics.externals.AbstractExternalAPIService;
import ru.yandex.webmaster3.core.metrics.externals.ExternalDependencyMethod;
import ru.yandex.webmaster3.core.regions.data.HostRegion;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.JavaMethodWitness;
import ru.yandex.webmaster3.proto.Geodata;
import ru.yandex.webmaster3.core.proto.converter.GeodataProtoConverter;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author avhaliullin
 */
public class HostGeodataInfoService extends AbstractExternalAPIService {
    private static final Logger log = LoggerFactory.getLogger(HostGeodataInfoService.class);
    public static final int SOCKET_TIMEOUT = 10_000;

    private final CloseableHttpClient client =
            HttpClientBuilder.create().setDefaultRequestConfig(
                    RequestConfig.custom()
                            .setConnectTimeout(HttpConstants.DEFAULT_CONNECT_TIMEOUT)
                            .setSocketTimeout(SOCKET_TIMEOUT)
                            .build()
            ).setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                    .setConnectionTimeToLive(10, TimeUnit.SECONDS)
                    .build();

    private String hostGeodataServiceUrl;

    @ExternalDependencyMethod("host-regions")
    public Map<WebmasterHostId, Set<HostRegion>> getHostRegionsInfo(Collection<WebmasterHostId> hostIds) {
        return trackQuery(new JavaMethodWitness() {}, ALL_ERRORS_INTERNAL, () -> {
            log.info("Asking geodata service about {} hosts", hostIds.size());
            if (hostIds.isEmpty()) {
                return new HashMap<>();
            }
            HttpPost post = new HttpPost(hostGeodataServiceUrl);
            String query = hostIds.stream().map(IdUtils::toRobotHostString).collect(Collectors.joining(";"));
            post.setEntity(new StringEntity(query, Charsets.UTF_8));
            try (CloseableHttpResponse response = client.execute(post)) {
                log.info("Got response from Geodata service: {}", response.getStatusLine().getStatusCode());

                if (response.getStatusLine().getStatusCode() != 200) {
                    log.error("Geodata service returned code: {}", response.getStatusLine().getStatusCode());
                    throw new WebmasterException("Geodata service returned code " + response.getStatusLine().getStatusCode(),
                            new WebmasterErrorResponse.GeodataServiceErrorResponse(getClass(), null));
                }

                return GeodataProtoConverter.convert(Geodata.GeoDataMessage.parseFrom(response.getEntity().getContent()));
            } catch (IOException e) {
                log.error("Geodata service failed");
                throw new WebmasterException("Geodata service failed, service url = " + hostGeodataServiceUrl,
                        new WebmasterErrorResponse.GeodataServiceErrorResponse(getClass(), e), e);

            }
        });
    }

    @Required
    public void setHostGeodataServiceUrl(String hostGeodataServiceUrl) {
        this.hostGeodataServiceUrl = hostGeodataServiceUrl;
    }
}
