package ru.yandex.webmaster3.core.feeds.mbi;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
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.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.jetbrains.annotations.NotNull;

import ru.yandex.webmaster3.core.WebmasterException;
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.util.JavaMethodWitness;
import ru.yandex.webmaster3.core.util.json.JsonMapping;
import ru.yandex.wmtools.common.sita.UserAgentEnum;

/**
 * @author kravchenko99
 * @date 8/5/21
 */

@Slf4j
public class MbiService extends AbstractExternalAPIService {



    private static final String UPDATE_MBI_PATH = "/datacamp/feed/update/features";
    private static final String ADD_MBI_PATH = "/datacamp/feed/register/url";
    private static final String RESEND_TO_PREMOD_PATH = "/datacamp/feed/resendToPremod";
    private static final String OFF_BY_PARTNER = "/datacamp/feed/offByPartner";
    private CloseableHttpClient httpClient;

    @Setter
    private URI mbiHost;

    public void init() {
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(1)
                .setConnectTimeout(100)
                .setSocketTimeout(5 * 60_000)
                .build();

        httpClient = HttpClients.custom()
                .setUserAgent(UserAgentEnum.WEBMASTER.getValue())
                .setMaxConnPerRoute(150)
                .setMaxConnTotal(150)
                .setConnectionTimeToLive(5, TimeUnit.SECONDS)
                .setDefaultRequestConfig(requestConfig)
                .disableCookieManagement()
                .build();
    }

    @NotNull
    @ExternalDependencyMethod("remove-feed-url")
    public Void removeShop(long shopId) {
        return trackQuery(new JavaMethodWitness() {
        }, ALL_ERRORS_INTERNAL, () -> {
            String uri = mbiHost.toString() + OFF_BY_PARTNER +
                    "?shopId=" + shopId;
            HttpPost httpReq = new HttpPost(uri);
            log.info("Mbi off shopId: {}", shopId);

            try (CloseableHttpResponse response = httpClient.execute(httpReq)) {
                log.info("x-market-req-id: {} for shopId {}", response.getFirstHeader("x-market-req-id"), shopId);

                HttpEntity responseEntity = response.getEntity();
                int httpCode = response.getStatusLine().getStatusCode();
                if (httpCode != 200 ) {
                    if (responseEntity != null) {
                        log.error("MBI responded httpCode - {} , body - {}", httpCode, IOUtils.toString(responseEntity.getContent(), StandardCharsets.UTF_8));
                    } else {
                        log.error("MBI responded httpCode - {}", httpCode);
                    }
                    throw new IllegalStateException("MBI return httpCode " + httpCode);
                }
                return null;
            } catch (IOException e) {
                throw new WebmasterException("Mbi request to off shop_id failed for - " + shopId,
                        new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Mbi req fail"), e);
            }
        });
    }

    @NotNull
    @ExternalDependencyMethod("add-feed-url")
    public RegisterFeedResponse addUrl(FeedRegisterUrlRequest request) {
        return trackQuery(new JavaMethodWitness() {
        }, ALL_ERRORS_INTERNAL, () -> {
            String uri = mbiHost.toString() + ADD_MBI_PATH;
            HttpPost httpReq = new HttpPost(uri);
            String requestStr = JsonMapping.writeValueAsString(request);
            log.info("Mbi request: {}, url: {}", requestStr, uri);

            httpReq.setEntity(new StringEntity(requestStr, ContentType.APPLICATION_JSON));

            try (CloseableHttpResponse response = httpClient.execute(httpReq)) {
                log.info("x-market-req-id: {} for url {}", response.getFirstHeader("x-market-req-id"), request.getUrl());

                JsonNode responseBody = null;
                HttpEntity responseEntity = response.getEntity();
                int httpCode = response.getStatusLine().getStatusCode();
                if (responseEntity == null) {
                    log.error("MBI responded with no body");
                    throw new IllegalStateException("MBI responded with no body");
                } else if (httpCode != 200 ) {
                    String body = IOUtils.toString(responseEntity.getContent(), StandardCharsets.UTF_8);
                    log.error("MBI responded httpCode - {} , body - {}",  httpCode, body);
                    var errorsResponse = JsonMapping.OM.readValue(body, MbiErrorsResponse.class);

                    if (httpCode == 400 && errorsResponse != null &&
                            errorsResponse.getErrors() != null &&
                            errorsResponse.getErrors().size() == 1 &&
                            errorsResponse.getErrors().get(0).getMessage().equals(ChangeRevokeStatusException.MESSAGE)) {
                        throw new ChangeRevokeStatusException();
                    }
                    throw new IllegalStateException("MBI return httpCode " + httpCode);
                } else {
                    responseBody = JsonMapping.OM.readTree(responseEntity.getContent());
                    log.info("Mbi response: {}", responseBody);
                }
                return JsonMapping.OM.readValue(new TreeTraversingParser(responseBody), RegisterFeedResponse.class);
            } catch (IOException e) {
                throw new WebmasterException("Mbi request failed for feedUrl - " + request.getUrl(),
                        new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Mbi req fail"), e);
            }
        });
    }

    @ExternalDependencyMethod("update-feed-features")
    public Void updateFeatures(UpdateFeedFeaturesRequest request) {
        return trackQuery(new JavaMethodWitness() {
        }, ALL_ERRORS_INTERNAL, () -> {
            String uri = mbiHost.toString() + UPDATE_MBI_PATH;
            HttpPost httpReq = new HttpPost(uri);
            String requestStr = JsonMapping.writeValueAsString(request);
            log.info("Mbi request: {}, url: {}", requestStr, uri);

            httpReq.setEntity(new StringEntity(requestStr, ContentType.APPLICATION_JSON));

            try (CloseableHttpResponse response = httpClient.execute(httpReq)) {
                log.info("x-market-req-id: {} for partner {}", response.getFirstHeader("x-market-req-id"), request.getPartnerId());

                JsonNode responseBody;
                HttpEntity responseEntity = response.getEntity();
                int httpCode = response.getStatusLine().getStatusCode();
                if (httpCode != 200 ) {
                    if (responseEntity != null) {
                        log.error("MBI responded httpCode - {} , body - {}", httpCode, IOUtils.toString(responseEntity.getContent(), StandardCharsets.UTF_8));
                    } else {
                        log.error("MBI responded httpCode - {}", httpCode);
                    }
                    throw new IllegalStateException("MBI return httpCode " + httpCode);
                }
                return null;
            } catch (IOException e) {
                throw new WebmasterException("Mbi request failed for partner - " + request.getPartnerId(),
                        new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Mbi req fail"), e);
            }
        });
    }

    @NotNull
    @ExternalDependencyMethod("recheck-shop")
    public Void recheckShop(long shopId) {
        return trackQuery(new JavaMethodWitness() {
        }, ALL_ERRORS_INTERNAL, () -> {
            String uri = mbiHost.toString() + RESEND_TO_PREMOD_PATH +
                    "?shopId=" + shopId;
            HttpPost httpReq = new HttpPost(uri);
            log.info("Mbi recheck shopId: {}", shopId);

            try (CloseableHttpResponse response = httpClient.execute(httpReq)) {

                HttpEntity responseEntity = response.getEntity();
                int httpCode = response.getStatusLine().getStatusCode();
                log.info("x-market-req-id: {} for shopId {}", response.getFirstHeader("x-market-req-id"), shopId);

                if (httpCode != 200 ) {
                    if (responseEntity != null) {
                        log.error("MBI responded httpCode - {} , body - {}", httpCode, IOUtils.toString(responseEntity.getContent(), StandardCharsets.UTF_8));
                    } else {
                        log.error("MBI responded httpCode - {}", httpCode);
                    }
                    throw new IllegalStateException("MBI return httpCode " + httpCode);
                }
                return null;
            } catch (IOException e) {
                throw new WebmasterException("Mbi request to recheck shopId failed for - " + shopId,
                        new WebmasterErrorResponse.InternalUnknownErrorResponse(getClass(), "Mbi req fail"), e);
            }
        });
    }

}
