package ru.yandex.webmaster3.storage.util.yt;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Oleg Bazdyrev
 */
class YtMergeCommand extends YtCommandImpl.PostCommand<YtOperationId> {
    private final List<YtPath> inputTablePaths;
    private final YtPath outputTablePath;
    private final List<String> mergeBy;

    public YtMergeCommand(List<YtPath> inputTablePaths, YtPath outputTablePath, String... mergeBy) {
        this.inputTablePaths = inputTablePaths;
        this.outputTablePath = outputTablePath;
        this.mergeBy = Arrays.asList(mergeBy);
    }

    @Override
    public UriComponentsBuilder createUri(URI proxyUri) {
        return UriComponentsBuilder.fromUri(proxyUri)
                .path(API_V3 + "/merge");
    }

    @Override
    protected Object getYtParameters() {
        List<String> input = inputTablePaths.stream().map(YtPath::toYtPath).collect(Collectors.toList());
        String output = outputTablePath.toYtPath();
        Spec spec = new Spec(Mode.sorted, input, output, true, mergeBy);
        return new Parameters(spec);
    }

    @Override
    public boolean needHeavyProxy() {
        return false;
    }

    @SuppressWarnings("Duplicates")
    @Override
    protected YtResult<YtOperationId> onSuccess(YtStatus status, HttpResponse httpResponse) throws YtCommandException {
        HttpEntity entity = httpResponse.getEntity();
        if (entity == null) {
            return YtResult.createError(status, "Response is empty", null);
        }
        try {
            String id = EntityUtils.toString(entity);
            String operationId = YtService.OM.readValue(id, String.class);
            return YtResult.createResult(status, new YtOperationId(outputTablePath.getCluster(), operationId));
        } catch (IOException e) {
            return YtResult.createError(status, "Unable to read response", e);
        }
    }

    @Override
    public String toString() {
        return "MERGE " + inputTablePaths + " TO " + outputTablePath;
    }

    private static class Parameters {
        @JsonProperty("spec") public final Spec spec;

        Parameters(Spec spec) {
            this.spec = spec;
        }
    }

    private static class Spec {
        @JsonProperty("mode") public final Mode mode;
        @JsonProperty("input_table_paths") public final List<String> inputTablePaths;
        @JsonProperty("output_table_path") public final String outputTablePath;
        @JsonProperty("force_transform") public final boolean forceTransform;
        @JsonProperty("merge_by") public final List<String> mergeBy;

        Spec(Mode mode, List<String> inputTablePaths, String outputTablePath, boolean forceTransform, List<String> mergeBy) {
            this.mode = mode;
            this.inputTablePaths = inputTablePaths;
            this.outputTablePath = outputTablePath;
            this.forceTransform = forceTransform;
            this.mergeBy = mergeBy;
        }
    }

    private enum Mode {
        unordered,
        sorted,
        ordered,
    }
}
