package com.atlassian.bitbucket.mesh.test;

import com.atlassian.bitbucket.mesh.rpc.v1.RpcRefUpdate;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.hamcrest.TypeSafeMatcher;

import javax.annotation.Nonnull;
import java.util.function.Function;

public class MoreMatchers {

    public static Matcher<RpcRefUpdate> hasOldHash(String oldHash) {
        return new TypeSafeMatcher<RpcRefUpdate>() {
            @Override
            protected boolean matchesSafely(RpcRefUpdate item) {
                return item.getOldHash().equals(oldHash);
            }

            @Override
            public void describeTo(Description description) {
                description.appendText(String.format("old hash: %s", oldHash));
            }
        };
    }

    public static Matcher<RpcRefUpdate> hasNewHash(String newHash) {
        return new TypeSafeMatcher<RpcRefUpdate>() {
            @Override
            protected boolean matchesSafely(RpcRefUpdate item) {
                return item.getNewHash().equals(newHash);
            }

            @Override
            public void describeTo(Description description) {
                description.appendText(String.format("new hash: %s", newHash));
            }
        };
    }

    public static Matcher<RpcRefUpdate> hasRefId(String refId) {
        return new TypeSafeMatcher<RpcRefUpdate>() {
            @Override
            protected boolean matchesSafely(RpcRefUpdate item) {
                return item.getRefId().toStringUtf8().equals(refId);
            }

            @Override
            public void describeTo(Description description) {
                description.appendText(String.format("ref id: %s", refId));
            }
        };
    }

    @Nonnull
    public static <T, U> Matcher<T> hasProperty(@Nonnull Function<T, U> propertyExtractor,
                                                @Nonnull String propertyName,
                                                @Nonnull Matcher<U> downstream) {
        return new TypeSafeDiagnosingMatcher<T>() {

            @Override
            public void describeTo(Description description) {
                description.appendText("an object with property ")
                        .appendValue(propertyName)
                        .appendText(" matching ")
                        .appendDescriptionOf(downstream);
            }

            @Override
            protected boolean matchesSafely(T item, Description mismatchDescription) {
                U value = propertyExtractor.apply(item);
                if (!downstream.matches(value)) {
                    downstream.describeMismatch(value, mismatchDescription);
                    return false;
                }
                return true;
            }
        };
    }
}
