package ru.yandex.webmaster3.worker.mirrors;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * @author akhazhoyan 02/2018
 */
@JsonIgnoreProperties(ignoreUnknown = true)
class MirrorServiceResponse {
    private static final ObjectMapper OM = new ObjectMapper()
            .setSerializationInclusion(JsonInclude.Include.NON_NULL);

    private final Status status;
    private final List<Result> results;
    private final String comment;

    MirrorServiceResponse(@JsonProperty("Status") Status status,
                          @JsonProperty("Results") List<Result> results,
                          @JsonProperty("Comment") String comment) {
        this.status = Preconditions.checkNotNull(status);
        this.results = Preconditions.checkNotNull(results);
        this.comment = comment;
    }

    @JsonProperty("Status")
    Status getStatus() {
        return status;
    }

    @JsonProperty("Results")
    List<Result> getResults() {
        return results;
    }

    @JsonProperty("Comment")
    String getComment() {
        return comment;
    }

    String asJson() throws JsonProcessingException {
        return OM.writeValueAsString(this);
    }

    static MirrorServiceResponse fromJson(String json) throws IOException {
        return OM.readValue(json, MirrorServiceResponse.class);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MirrorServiceResponse that = (MirrorServiceResponse) o;
        return this.status == that.status &&
            this.results.size() == that.results.size() &&
            Objects.equals(
                    this.results.stream().collect(Collectors.toMap(i -> i, i -> 1, (i, j) -> i + j)),
                    that.results.stream().collect(Collectors.toMap(i -> i, i -> 1, (i, j) -> i + j))
            );
    }

    @Override
    public int hashCode() {
        int hash = 31 * status.hashCode();
        for (Result r : results) {
            hash += r.hashCode();
        }
        return hash;
    }

    enum Status {
        OK,
        ERROR_USER,
        ERROR_INTERNAL,
        ERROR_TIMEOUT
    }

    static class Result {
        private final String status;
        private final String host;
        private final String newMain;
        private final String target;
        private final Integer httpCode;
        private final String comment;

        public Result(
                @JsonProperty("Status") String status,
                @JsonProperty("Host") String host,
                @JsonProperty("NewMain") String newMain,
                @JsonProperty("Target") String target,
                @JsonProperty("HttpCode") Integer httpCode,
                @JsonProperty("Comment") String comment) {
            this.status = Preconditions.checkNotNull(status);
            this.host = Preconditions.checkNotNull(host);
            this.newMain = newMain;
            this.target = target;
            this.httpCode = httpCode;
            this.comment = comment;
        }

        @JsonProperty("Status")
        public String getStatus() {
            return status;
        }

        @JsonProperty("Host")
        public String getHost() {
            return host;
        }

        @JsonProperty("NewMain")
        public String getNewMain() {
            return newMain;
        }

        @JsonProperty("Target")
        public String getTarget() {
            return target;
        }

        @JsonProperty("HttpCode")
        public Integer getHttpCode() {
            return httpCode;
        }

        @JsonProperty("Comment")
        public String getComment() {
            return comment;
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Result that = (Result) o;
            return Objects.equals(status, that.status) &&
                    Objects.equals(host, that.host) &&
                    Objects.equals(newMain, that.newMain) &&
                    Objects.equals(target, that.target) &&
                    Objects.equals(httpCode, that.httpCode);
        }

        @Override
        public int hashCode() {
            return Objects.hash(status, host, newMain, target, httpCode);
        }
    }
}
