package ru.yandex.webmaster3.admin.action.util;

import java.io.IOException;
import java.net.URI;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.autodoc.common.doc.annotation.Description;
import ru.yandex.webmaster3.admin.security.action.AdminAction;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.data.HttpResponsePart;
import ru.yandex.webmaster3.core.http.WebmasterErrorResponse;
import ru.yandex.webmaster3.core.http.WriteAction;
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.ZoraResponseWithRaw;
import ru.yandex.webmaster3.core.zora.data.response.ZoraUrlFetchResponse;
import ru.yandex.webmaster3.storage.admin.security.AccessObjectEnum;
import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.sita.SitaJson;
import ru.yandex.webmaster3.core.util.http.YandexMimeType;
import ru.yandex.wmtools.common.util.http.YandexHttpStatus;
import ru.yandex.wmtools.common.util.uri.WebmasterUriUtils;

/**
 * TODO: Избавиться от дублиования: отличия от обычного ServerResponseUtil - минимальны
 * Created by ifilippov5 on 22.06.17.
 */
@WriteAction
@Description(value = "Скачивание страницы с сайта пользователя через Ситу")
public class AdminServerResponseUtilAction extends AdminAction<AdminServerResponseUtilRequest,
        AdminServerResponseUtilResponse> {
    private static final Logger log = LoggerFactory.getLogger(AdminServerResponseUtilAction.class);

    private static final ObjectMapper OM = new ObjectMapper()
            .registerModule(new ParameterNamesModule())
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

    static {
        SimpleFilterProvider filterProvider = new SimpleFilterProvider();
        filterProvider.addFilter(SitaJson.TURLFETCHINGRESULT_FILTER,
                SimpleBeanPropertyFilter.serializeAllExcept("HttpResponse", "Trace"));
        OM.disable(SerializationFeature.WRITE_NULL_MAP_VALUES).writer(filterProvider);
        OM.enable(SerializationFeature.INDENT_OUTPUT);
    }

    private static final String PARAM_URL = "url";

    private OfflineZoraService offlineZoraService;

    public AdminServerResponseUtilAction() {
        super(AccessObjectEnum.TOOLS);
    }

    @Override
    public AdminServerResponseUtilResponse process(AdminServerResponseUtilRequest request) throws WebmasterException {
        URI uri;
        try {
            uri = WebmasterUriUtils.toOldUri(request.getUrl());
        } catch (UserException e) {
            throw new WebmasterException("Unable to parse url: " + request.getUrl(),
                    new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), PARAM_URL,
                            request.getUrl()));
        }
        ZoraPDFetchRequest.Builder builder = ZoraPDFetchRequest.builder(uri)
                .source(ZoraSourceEnum.forUserAgent(request.getUserAgent()))
                .ifModifiedSince(request.getIfModifiedSince())
                .priority(0L);

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

        AdminServerResponseUtilResponse error = convertErrors(request, urlFetchResponse);
        if (error != null) {
            return error;
        }

        if (urlFetchResponse.isAllowedInRobotsTxt() != null && !urlFetchResponse.isAllowedInRobotsTxt()) {
            return new AdminServerResponseUtilResponse.UrlIsDisallowedInRobotsTxt(this.getClass());
        }

        if (urlFetchResponse.getExtendedHttpStatus() == null || urlFetchResponse.getExtendedHttpStatus() == YandexHttpStatus.UNKNOWN) {
            return new AdminServerResponseUtilResponse.UnableToDownloadContentResponse(this.getClass());
        }

        Boolean isText = urlFetchResponse.getMimeType() == YandexMimeType.MIME_TEXT
                || urlFetchResponse.getMimeType() == YandexMimeType.MIME_HTML
                || urlFetchResponse.getMimeType() == YandexMimeType.MIME_XML;

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

        HttpResponsePart httpResponsePart = HttpResponsePart.createFromZoraResponse(urlFetchResponse, responseBody);
        String jsonReq;
        String jsonRes;
        try {
            jsonReq = responseMeta.getJsonRequest() == null ? null :
                    OM.writeValueAsString(responseMeta.getJsonRequest());
            jsonRes = responseMeta.getJsonResponse() == null ? null :
                    OM.writeValueAsString(responseMeta.getJsonResponse());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return new AdminServerResponseUtilResponse.NormalResponse(httpResponsePart, jsonReq, jsonRes);
    }

    private AdminServerResponseUtilResponse convertErrors(AdminServerResponseUtilRequest request,
                                                          ZoraUrlFetchResponse urlFetchResponse) {
        YandexHttpStatus sitaHttpStatus = urlFetchResponse.getExtendedHttpStatus();
        switch (sitaHttpStatus) {
            case HTTP_1004_BAD_URL:
                throw new WebmasterException("Unable to parse url",
                        new WebmasterErrorResponse.IllegalParameterValueResponse(this.getClass(), PARAM_URL,
                                request.getUrl()));
            case HTTP_1003_ROBOTS_TXT_DISALLOW:
                return new AdminServerResponseUtilResponse.UrlIsDisallowedInRobotsTxt(this.getClass());
        }
        if (YandexHttpStatus.isExtErrors(sitaHttpStatus)) {
            return new AdminServerResponseUtilResponse.UnableToDownloadContentResponse(this.getClass());
        }
        return null;
    }

    @Required
    public void setOfflineZoraService(OfflineZoraService offlineZoraService) {
        this.offlineZoraService = offlineZoraService;
    }
}

