package ru.yandex.webmaster3.worker.sitemap;

import NCrawl.Feeds;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.joda.time.Duration;
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.logbroker.writer.LogbrokerClient;
import ru.yandex.webmaster3.core.metrics.externals.AbstractExternalAPIService;
import ru.yandex.webmaster3.core.metrics.externals.ExternalDependencyMethod;
import ru.yandex.webmaster3.core.util.IdUtils;
import ru.yandex.webmaster3.core.util.JavaMethodWitness;
import ru.yandex.webmaster3.core.util.RetryUtils;
import ru.yandex.webmaster3.core.util.UrlUtils;
import ru.yandex.webmaster3.core.worker.client.WorkerClient;
import ru.yandex.webmaster3.storage.abt.AbtService;
import ru.yandex.webmaster3.storage.abt.model.Experiment;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author avhaliullin
 */
public class SitemapRecrawlServiceImpl extends AbstractExternalAPIService implements SitemapRecrawlService {
    private static final Logger log = LoggerFactory.getLogger(SitemapRecrawlServiceImpl.class);
    private static final RetryUtils.RetryPolicy LB_WRITE_RETRY_POLICY = RetryUtils.linearBackoff(5, Duration.standardSeconds(30));

    private static final String FEED_NAME = "webmaster-addurl-sitemap";

    private static final int SOCKET_TIMEOUT_MS = 2000;
    private static final int CONNECT_TIMEOUT_MS = HttpConstants.DEFAULT_CONNECT_TIMEOUT;

    private final CloseableHttpClient client;
    private String sitemapRecrawlServiceUrl;

    private AbtService abtService;
    private LogbrokerClient samovarFeedsExtLogbrokerClient;

    // Робот просит слать не больше 10 rps
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(10 / WorkerClient.TOTAL_WORKERS);

    {
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(SOCKET_TIMEOUT_MS)
                .setConnectTimeout(CONNECT_TIMEOUT_MS)
                .build();

        client = HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionTimeToLive(30, TimeUnit.SECONDS)
                .build();
    }

    @Override
    @ExternalDependencyMethod("recrawlSitemap")
    public void recrawlSitemap(String sitemapUrl) {
        WebmasterHostId hostId = null;
        try {
            hostId = IdUtils.urlToHostId(sitemapUrl);
        } catch (WebmasterException e) {
            log.error("Error parsing {}", sitemapUrl);
        }

        if (hostId != null) {
            trackExecution(new JavaMethodWitness() {}, ALL_ERRORS_INTERNAL, () -> {
                requestRecrawlSamovar(sitemapUrl);
            });
        }

//        else {
//            trackExecution(new JavaMethodWitness() {}, ALL_ERRORS_INTERNAL, () -> {
//                log.info("Recrawl sitemap: {}", sitemapUrl);
//
//                HttpGet req = new HttpGet(sitemapRecrawlServiceUrl + "/sitemap?url=" + sitemapUrl);
//                doRequest(req);
//            });
//        }
    }

    private void doRequest(HttpUriRequest req) {
        try (CloseableHttpResponse response = client.execute(req)) {
            if (response.getStatusLine().getStatusCode() != 200) {
                String msg = "Sitemap recrawl service returned code: " + response.getStatusLine().getStatusCode();
                String responseStr = "";
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    responseStr = new BufferedReader(new InputStreamReader(entity.getContent()))
                            .lines().collect(Collectors.joining("\n"));
                }
                throw new WebmasterException(msg + "\n" + responseStr,
                        new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), null));
            }
        } catch (IOException e) {
            throw new WebmasterException("Sitemap recrawl failed",
                    new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), null), e);
        }
    }

    public void requestRecrawlSamovar(String fullUrl) {
        String canonizedUrl = UrlUtils.canonizeUrlForRobot(fullUrl);
        if (canonizedUrl == null) {
            log.error("Failed to canonize url: {}", fullUrl);
            canonizedUrl = fullUrl;
        }

        log.info("Sending sitemap to Samovar: {}", fullUrl);
        byte[] data = Feeds.TFeedExt.newBuilder()
                .setFeedName(FEED_NAME)
                .setUrl(canonizedUrl)
                .build()
                .toByteArray();

        try {
            RATE_LIMITER.acquire();
            RetryUtils.execute(LB_WRITE_RETRY_POLICY, () -> samovarFeedsExtLogbrokerClient.write(data));
        } catch (Exception e) {
            throw new WebmasterException("Unable to send request to Logbroker",
                    new WebmasterErrorResponse.LogbrokerErrorResponse(this.getClass(), e));
        }
    }

    @Required
    public void setSitemapRecrawlServiceUrl(String sitemapRecrawlServiceUrl) {
        this.sitemapRecrawlServiceUrl = sitemapRecrawlServiceUrl;
    }

    @Required
    public void setAbtService(AbtService abtService) {
        this.abtService = abtService;
    }

    public void setSamovarFeedsExtLogbrokerClient(LogbrokerClient samovarFeedsExtLogbrokerClient) {
        this.samovarFeedsExtLogbrokerClient = samovarFeedsExtLogbrokerClient;
    }
}
