package ru.yandex.qe.dispenser.api.v1.response;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import ru.yandex.qe.dispenser.api.DtoBuilder;
import ru.yandex.qe.dispenser.api.util.JsonSerializerBase;
import ru.yandex.qe.dispenser.api.util.ValidationUtils;

@JsonSerialize(using = DiListRelativePageResponse.Serializer.class)
public class DiListRelativePageResponse<T> extends DiListResponse<T> implements DiPage<T> {
    @Nullable
    private final URI nextPageUrl;

    @JsonCreator
    public DiListRelativePageResponse(@JsonProperty("result") @NotNull final Collection<T> results,
                                      @JsonProperty("next") @Nullable final String next) throws URISyntaxException {
        super(results);
        this.nextPageUrl = next == null ? null : new URI(next);
    }

    private DiListRelativePageResponse(final Builder<T> builder) {
        super(Collections.unmodifiableCollection(ValidationUtils.requireNonNull(builder.results, "Results are required")));
        this.nextPageUrl = builder.nextPageUrl;
    }

    public static <T> Builder<T> builder() {
        return new Builder<>();
    }

    @Nullable
    public URI getNextPageUrl() {
        return nextPageUrl;
    }

    static class Serializer<R extends DiListRelativePageResponse<?>> extends JsonSerializerBase<R> {
        @Override
        public void serialize(@NotNull final R response,
                              @NotNull final JsonGenerator jg,
                              @NotNull final SerializerProvider sp) throws IOException {
            jg.writeStartObject();
            jg.writeStringField("next", Objects.toString(response.getNextPageUrl(), null));
            jg.writeArrayFieldStart("result");
            for (final Object el : response) {
                jg.writeObject(el);
            }
            jg.writeEndArray();
            jg.writeEndObject();
        }
    }

    @Override
    public boolean hasNext() {
        return nextPageUrl != null;
    }

    public static class Builder<T> implements DtoBuilder<DiListResponse<T>> {
        @Nullable
        private Collection<T> results;
        @Nullable
        private URI nextPageUrl;

        public Builder<T> withResults(final Collection<T> results) {
            this.results = results;
            return this;
        }

        public Builder<T> withNextPageUrl(final URI nextPageUrl) {
            this.nextPageUrl = nextPageUrl;
            return this;
        }

        @NotNull
        @Override
        public DiListRelativePageResponse<T> build() {
            return new DiListRelativePageResponse<>(this);
        }
    }
}
