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

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.google.common.base.Preconditions;
import com.google.common.collect.Range;
import lombok.Value;
import org.apache.commons.lang3.StringEscapeUtils;

/**
 * @author avhaliullin
 */
public abstract class YtTableRange {
    public abstract Optional<String> renderInPath();

    @Override
    public abstract String toString();

    public static class IndexRange extends YtTableRange {
        private final Range<Long> range;

        private IndexRange(Range<Long> range) {
            this.range = range;
        }

        public Range<Long> getRange() {
            return range;
        }

        @Override
        public Optional<String> renderInPath() {
            if (!range.hasUpperBound() && !range.hasLowerBound()) {
                return Optional.empty();
            }
            String result = "";
            if (range.hasLowerBound()) {
                result += "#" + range.lowerEndpoint();
            }
            result += ':';
            if (range.hasUpperBound()) {
                result += "#" + range.upperEndpoint();
            }
            return Optional.of(result);
        }

        @Override
        public String toString() {
            return renderInPath().orElse("");
        }
    }

    public static class SingleKeyRange extends YtTableRange {
        private final String key;

        private SingleKeyRange(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }

        @Override
        public Optional<String> renderInPath() {
            return Optional.of("\"" + StringEscapeUtils.escapeJson(key) + "\"");
        }

        @Override
        public String toString() {
            return StringEscapeUtils.escapeCsv(key);
        }
    }

    @Value
    public static class MultiKeyRange extends YtTableRange {
        List<Object> keys;

        @Override
        public Optional<String> renderInPath() {
            return Optional.of(keys.stream().map(obj -> {
                if (obj instanceof String) {
                    return "\"" + StringEscapeUtils.escapeJson((String) obj) + "\"";
                } else {
                    return obj.toString();
                }
            }).collect(Collectors.joining(",", "(", ")")));
        }
    }

    public static IndexRange index(Range<Long> range) {
        return new IndexRange(range);
    }

    public static SingleKeyRange singleKey(String key) {
        return new SingleKeyRange(key);
    }

    public static MultiKeyRange multiKey(Object... keys) {
        Preconditions.checkNotNull(keys);
        Preconditions.checkArgument(keys.length > 0 && keys[0] != null);
        return new MultiKeyRange(Arrays.asList(keys));
    }

    public static IndexRange all() {
        return index(Range.all());
    }

    @Value
    public static class RawRange extends YtTableRange {

        String value;

        @Override
        public Optional<String> renderInPath() {
            return Optional.of(value);
        }

        @Override
        public String toString() {
            return value;
        }
    }

    public static RawRange raw(String value) {
        return new RawRange(value);
    }
}
