package ru.yandex.webmaster3.core.blackbox.service;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.TreeTraversingParser;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URISyntaxException;

/**
 * @author aherman
 */
abstract class BlackboxRequest<T> {
    private static final Logger log = LoggerFactory.getLogger(BlackboxRequest.class);

    protected static final ObjectMapper OM = new ObjectMapper()
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

    abstract HttpUriRequest createRequest(URIBuilder uriBuilder) throws URISyntaxException;

    BlackboxResponse<T> parseResponse(HttpResponse response) throws IOException {
        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            return BlackboxResponse.createError(BlackboxStatus.REQUEST_ERROR);
        }
        String responseContent = EntityUtils.toString(response.getEntity());
        log.debug("BL: {}", responseContent);

        try {
            JsonNode jsonNode = OM.readTree(responseContent);
            StatusHolder status = OM.readValue(new TreeTraversingParser(jsonNode), StatusHolder.class);
            if (status.status.value != BlackboxStatus.VALID) {
                return BlackboxResponse.createError(status.status.value);
            }
            return parseJson(jsonNode);
        } catch (Exception e) {
            log.error("Unable to parse Blackbox response", e);
            return BlackboxResponse.createError(BlackboxStatus.PARSING_ERROR);
        }
    }

    abstract BlackboxResponse<T> parseJson(JsonNode jsonNode) throws IOException;

    private static class StatusComposite {
        private final int id;
        private final BlackboxStatus value;

        public StatusComposite(
                @JsonProperty("id") int id,
                @JsonProperty("value") BlackboxStatus value)
        {
            this.id = id;
            this.value = value;
        }

        public int getId() {
            return id;
        }

        public BlackboxStatus getValue() {
            return value;
        }
    }

    private static class StatusHolder {
        private final StatusComposite status;

        public StatusHolder(@JsonProperty("status") StatusComposite status) {
            this.status = status;
        }
    }
}
