package ru.yandex.webmaster3.storage.util.serverresponseutil;

import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.HttpCodeInfo;
import ru.yandex.webmaster3.core.data.HttpResponsePart;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.util.ZoraResponseDocumentUtil;
import ru.yandex.webmaster3.core.zora.OfflineZoraService;
import ru.yandex.webmaster3.core.zora.ZoraConversionUtil;
import ru.yandex.webmaster3.core.zora.ZoraSourceEnum;
import ru.yandex.webmaster3.core.zora.data.request.ZoraPDFetchRequest;
import ru.yandex.webmaster3.core.zora.data.response.ZoraRawPDFetchResponse;
import ru.yandex.webmaster3.core.zora.data.response.ZoraUrlFetchResponse;
import ru.yandex.webmaster3.storage.util.serverresponseutil.exception.ServerResponseHttpUtilException;
import ru.yandex.webmaster3.storage.util.serverresponseutil.exception.ServerResponseUtilException;
import ru.yandex.webmaster3.storage.util.serverresponseutil.model.ServerResponseUtilErrorType;
import ru.yandex.webmaster3.storage.util.serverresponseutil.model.ServerResponseUtilInputData;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.util.http.YandexHttpStatus;
import ru.yandex.wmtools.common.util.uri.WebmasterUriUtils;

/**
 * @author: ishalaru
 * DATE: 20.05.2019
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class ServerResponseUtilService {
    private static final String PARAM_URL = "url";
    // WMCSUPPORT-2438 - ignored Zora statuses
    private static final Set<YandexHttpStatus> IGNORED_HTTP_CODES = Collections.unmodifiableSet(
            EnumSet.of(
                    YandexHttpStatus.EXT_HTTP_2011_BAD_CHARSET,
                    YandexHttpStatus.EXT_HTTP_2012_BAD_LANGUAGE,
                    YandexHttpStatus.EXT_HTTP_2014_EMPTYDOC
            ));

    private final OfflineZoraService offlineZoraService;

    public HttpResponsePart process(ServerResponseUtilInputData inputData) throws WebmasterException {
        URI uri;
        String urlS = inputData.getUrl();
        try {
            uri = WebmasterUriUtils.toOldUri(urlS);
        } catch (UserException | NullPointerException e) {
            throw new WebmasterException("Unable to parse url: " + urlS,
                    new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), PARAM_URL, urlS));
        }

        ZoraPDFetchRequest.Builder builder = ZoraPDFetchRequest.builder(uri)
                .source(ZoraSourceEnum.forUserAgent(inputData.getUserAgent()))
                .timeout(Duration.standardSeconds(20))
                .ifModifiedSince(inputData.getIfModifiedSince())
                .priority(0L);

        log.info("Server response user agent: {}", inputData.getUserAgent());

        ZoraRawPDFetchResponse rawResponse = offlineZoraService.realtimeFetchUrl(builder.build()).getResponse();
        ZoraUrlFetchResponse urlFetchResponse = ZoraConversionUtil.toUrlFetchResponse(rawResponse);

        checkResponse(inputData, urlFetchResponse);

        if (urlFetchResponse.isAllowedInRobotsTxt() != null && !urlFetchResponse.isAllowedInRobotsTxt()) {
            log.info("Url {} is not allowed in robots.txt", urlS);
            throw new ServerResponseUtilException(ServerResponseUtilErrorType.SERVER_RESPONSE_TOOL__URL_IS_DISALLOWED_IN_ROBOTS_TXT);
        }

        YandexHttpStatus extHttpStatus = urlFetchResponse.getExtendedHttpStatus();
        if (extHttpStatus == null || extHttpStatus == YandexHttpStatus.UNKNOWN) {
            log.info("No Zora http status for url {}", urlS);
            throw new ServerResponseUtilException(ServerResponseUtilErrorType.SERVER_RESPONSE_TOOL__UNABLE_TO_DOWNLOAD_CONTENT);
        }
        if (extHttpStatus.getCode() >= 1000 && !IGNORED_HTTP_CODES.contains(extHttpStatus)) {
            HttpCodeInfo httpCodeInfo = HttpCodeInfo.fromHttpStatus(extHttpStatus);
            log.info("Zora returned bad http status for url {}: {}", urlS, httpCodeInfo);
            throw new ServerResponseHttpUtilException(ServerResponseUtilErrorType.SERVER_RESPONSE_TOOL__EXT_HTTP_STATUS, httpCodeInfo);
        }

        String responseBody = null;
        try {
            responseBody = ZoraResponseDocumentUtil.getResponseString(urlFetchResponse);
        } catch (IOException | InternalException e) {
            log.error("Unable to get response body for url={}", urlS, e);
        }

        return HttpResponsePart.createFromZoraResponse(urlFetchResponse, responseBody);
    }

    private void checkResponse(ServerResponseUtilInputData inputData, ZoraUrlFetchResponse urlFetchResponse) {
        YandexHttpStatus sitaHttpStatus = urlFetchResponse.getExtendedHttpStatus();
        String urlS = inputData.getUrl();
        switch (sitaHttpStatus) {
            case HTTP_1004_BAD_URL:
                log.info("Unable to parse url {}", urlS);
                throw new ServerResponseUtilException(ServerResponseUtilErrorType.SERVER_RESPONSE_TOOL__ILLEGAL_ARGUMENT_EXCEPTION);
            case HTTP_1003_ROBOTS_TXT_DISALLOW:
                log.info("Url {} is not allowed in robots.txt", urlS);
                throw new ServerResponseUtilException(ServerResponseUtilErrorType.SERVER_RESPONSE_TOOL__URL_IS_DISALLOWED_IN_ROBOTS_TXT);
        }
        if (YandexHttpStatus.isExtErrors(sitaHttpStatus)) {
            log.info("Zora returned bad http status for url {}: {}", urlS, sitaHttpStatus);
            throw new ServerResponseUtilException(ServerResponseUtilErrorType.SERVER_RESPONSE_TOOL__UNABLE_TO_DOWNLOAD_CONTENT);
        }
    }

}
