package com.atlassian.bitbucket.internal.mirroring.mirror.nav;

import com.atlassian.bitbucket.util.MoreCollectors;
import com.atlassian.bitbucket.util.UrlUtils;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

class PathUrlBuilder implements MirroringUrl {
    private final String baseUrl;
    private final ListMultimap<String, Object> parameters;
    private final List<String> path;

    PathUrlBuilder(String baseUrl, String... values) {
        this.baseUrl = StringUtils.appendIfMissing(baseUrl, "/");
        parameters = ArrayListMultimap.create();
        path = Arrays.stream(values)
                .map(UrlUtils::encodeURL)
                .collect(MoreCollectors.toImmutableList());
    }

    PathUrlBuilder(PathUrlBuilder builder, String... values) {
        this(builder, Arrays.stream(values));
    }

    private PathUrlBuilder(PathUrlBuilder builder, Stream<?> values) {
        this.baseUrl = builder.baseUrl;
        parameters = ArrayListMultimap.create(builder.parameters);
        ImmutableList.Builder<String> pathBuilder = ImmutableList.builder();
        pathBuilder.addAll(builder.path);
        values.map(UrlUtils::encodeURL).forEach(pathBuilder::add);

        path = pathBuilder.build();
    }

    @Nonnull
    @Override
    public String getAbsolute() {
        return appendRelative(new StringBuilder(baseUrl)).toString();
    }

    @Nonnull
    public String getRelative() {
        return appendRelative(new StringBuilder("/")).toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || !(o instanceof PathUrlBuilder)) {
            return false;
        }
        PathUrlBuilder that = (PathUrlBuilder) o;
        return Objects.equals(baseUrl, that.baseUrl) &&
                Objects.equals(parameters, that.parameters) &&
                Objects.equals(path, that.path);
    }

    @Override
    public int hashCode() {
        return Objects.hash(baseUrl, parameters, path);
    }

    PathUrlBuilder addPathNotEncoded(String...values) {
        return new PathUrlBuilder(this, Arrays.stream(values).map(UrlUtils::dontEncode));
    }

    PathUrlBuilder parameter(@Nonnull String name, Object value) {
        parameters.put(name, value);
        return this;
    }

    PathUrlBuilder parameterNotEncoded(@Nonnull String name, Object value) {
        parameters.put(name, UrlUtils.dontEncode(value));
        return this;
    }

    private StringBuilder appendRelative(StringBuilder url) {
        url.append(StringUtils.join(path, '/'));

        if (!parameters.isEmpty()) {
            url.append('?').append(UrlUtils.buildQueryParamsFromMap(parameters.asMap()));
        }
        return url;
    }
}
