package com.atlassian.bitbucket.hamcrest;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static org.hamcrest.Matchers.equalTo;

/**
 * Helper builder for creating a {@link Matcher}. For example:
 *
 * <pre>
 * {@code
 *    @Override
 *    protected boolean matchesSafely(InternalUpstreamServer item, Description mismatchDescription) {
 *      return new MatcherBuilder()
 *          .equal("apiUrl", apiUrl, item.getApiUrl())
 *          .equal("baseUrl", baseUrl, item.getBaseUrl())
 *          .equal("issuerId", issuerId, item.getIssuerId())
 *          .equal("id", id, item.getId())
 *          .equal("lastSettingsChangeDate", lastSettingsChangeDate, item.getLastFullSyncDate())
 *          .equal("lastFullSyncDate", lastFullSyncDate, item.getLastSettingsChangeDate())
 *          .equal("namespace", namespace, item.getNamespace())
 *          .matcher("sharedSecret", Matchers.contains("something"), item.getSharedSecret())
 *          .equal("integrationState", integrationState, item.getState())
 *          .equal("type", type, item.getType())
 *          .equal("user", user, item.getUser())
 *          .collect(mismatchDescription);
 *    }
 * }
 * </pre>
 *
 * @since 4.6
 */
public class MatcherBuilder {
    private List<String> errors = new ArrayList<>();

    public MatcherBuilder equal(String name, Object expected, Object actual) {
        return matches(name, equalTo(expected), actual);
    }

    public <T> MatcherBuilder matches(String name, Matcher<? super T> matcher, T actual) {
        if (!matcher.matches(actual)) {
            StringDescription mismatchDescription = new StringDescription();
            matcher.describeMismatch(actual, mismatchDescription);
            errors.add(String.format("%s: %s", name, mismatchDescription.toString()));
        }
        return this;
    }

    public boolean collect(Description mismatchDescription) {
        if (errors.isEmpty()) {
            return true;
        } else {
            mismatchDescription.appendText(errors.stream().collect(Collectors.joining(", ")));
            return false;
        }
    }
}
