package ru.yandex.webmaster3.core.data;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.StatusLine;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.yandex.webmaster3.core.zora.data.response.ZoraSslCertErrorEnum;
import ru.yandex.webmaster3.core.zora.data.response.ZoraUrlFetchResponse;
import ru.yandex.webmaster3.core.util.http.YandexMimeType;

import java.nio.charset.Charset;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;


/**
 * @author aherman
 */
@RequiredArgsConstructor
@Builder
@ToString
public class HttpResponsePart {
    private static final Logger log = LoggerFactory.getLogger(HttpResponsePart.class);
    private static final List<String> X_YANDEX_HEADER_PREFIXES = List.of("X-Yandex-", "X-Ya-");

    @JsonProperty("httpCode")
    private final int httpCode;
    @JsonProperty("httpStatus")
    private final String httpStatus;
    @JsonProperty("remoteIp")
    private final String remoteIp;
    @JsonProperty("encoding")
    private final String encoding;
    @JsonProperty("headers")
    private final List<Header> headers;
    @JsonProperty("responseBody")
    private final String responseBody;
    @JsonProperty("requestTime")
    private final Long requestTime;
    @JsonProperty("responseSize")
    private final int responseSize;
    @JsonProperty("redirectTarget")
    private final String redirectTarget;
    @JsonProperty("sslError")
    @Getter
    @Nullable
    private final ZoraSslCertErrorEnum sslError;

    public int getHttpCode() {
        return httpCode;
    }

    public String getHttpStatus() {
        return httpStatus;
    }

    public String getRemoteIp() {
        return remoteIp;
    }

    public String getEncoding() {
        return encoding;
    }

    public List<Header> getHeaders() {
        return headers;
    }

    @Nullable
    public String getResponseBody() {
        return responseBody;
    }

    public Long getRequestTime() {
        return requestTime;
    }

    public int getResponseSize() {
        return responseSize;
    }

    public String getRedirectTarget() {
        return redirectTarget;
    }

    public static class Header {
        private final String name;
        private final String value;

        public Header(
                @JsonProperty("name") String name,
                @JsonProperty("value") String value
        ) {
            this.name = name;
            this.value = value;
        }

        public String getName() {
            return name;
        }

        public String getValue() {
            return value;
        }
    }

    public static HttpResponsePart createFromZoraResponse(ZoraUrlFetchResponse urlFetchResponse, String content) {
        content = StringUtils.trimToNull(content);
        String remoteIp = urlFetchResponse.getIpAddress();
        int publicHttpCode = 0;
        String encodingStr = null;
        int contentLength = 0;
        String publicHttpStatus = null;
        Long requestTime;
        List<Header> headers = null;

        if (urlFetchResponse.hasDocument()) {
            org.apache.http.HttpResponse httpHeaders = urlFetchResponse.getHttpResponse();
            StatusLine statusLine = httpHeaders.getStatusLine();
            int parsedHttpCode = statusLine.getStatusCode();
            if (parsedHttpCode >= 100 && parsedHttpCode < 600) {
                publicHttpCode = parsedHttpCode;
                publicHttpStatus = statusLine.getReasonPhrase();
            }

            org.apache.http.Header contentLengthHeader = httpHeaders.getFirstHeader("Content-Length");
            if (contentLengthHeader != null) {
                try {
                    contentLength = Integer.parseInt(contentLengthHeader.getValue());
                } catch (NumberFormatException ignore) {
                }
            }

            headers = convertHeaders(urlFetchResponse);
        }

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

        if (isText) {
            Charset charset = urlFetchResponse.getCharsetJava();
            encodingStr = getCharsetDisplayName(charset);
        }
        if (content != null && contentLength <= 0) {
            contentLength = content.length();
        }
        requestTime = urlFetchResponse.getResponseTime();
        return new HttpResponsePart(publicHttpCode, publicHttpStatus, remoteIp, encodingStr, headers, isText ? content : null,
                requestTime, contentLength, urlFetchResponse.getRedirTarget(), urlFetchResponse.getSslErrorExplanation());
    }

    private static List<Header> convertHeaders(ZoraUrlFetchResponse responseInfo) {
        if (!responseInfo.hasDocument()) {
            return Collections.emptyList();
        }

        List<Header> result = new LinkedList<>();
        for (org.apache.http.Header header : responseInfo.getHttpResponse().getAllHeaders()) {
            boolean skipHeader = X_YANDEX_HEADER_PREFIXES.stream().anyMatch(p -> header.getName().startsWith(p));
            if (skipHeader) {
                continue;
            }

            result.add(new Header(header.getName(), header.getValue()));
        }
        return result;
    }

    private static String getCharsetDisplayName(Charset charset) {
        StringBuilder sb = new StringBuilder(charset.displayName());
        Set<String> aliases = charset.aliases();
        if (!aliases.isEmpty()) {
            sb.append('(').append(StringUtils.join(aliases, ", ")).append(')');
        }
        return sb.toString();
    }
}
