package com.atlassian.bitbucket.mockito;


import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;
import com.atlassian.bitbucket.util.PageUtils;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import javax.annotation.Nonnull;
import java.util.Collections;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.Arrays.asList;

/**
 * Useful for stubbing methods that accept a {@link PageRequest} and return a {@link Page} of elements. This answer
 * will collect the original page request from invocation and use it to construct the page in response. The first
 * argument in invocation that is of type {@link PageRequest} will be used. Example usage:
 * <p>
 * Return 3 consecutive pages:
 * <pre>
 *     {@code
 *          when(branchService.getBranches(any(GetBranchesRequest.class), any(PageRequest.class))
 *              .thenAnswer(withPageOf(newBranch("master"), newBranch("feature/STASHDEV-123")))
 *              .thenAnswer(withPageOf(newBranch("feature/STASHDEV-124"), newBranch("feature/STASHDEV-125")))
 *              .thenAnswer(withLastPageOf(newBranch("feature/STASHDEV-127")));
 *     }
 * </pre>
 * <p>
 * Return an empty page straight away:
 * <pre>
 *     {@code
 *          when(branchService.getBranches(any(GetBranchesRequest.class), any(PageRequest.class))
 *              .thenAnswer(withEmptyPageOf(Branch.class));
 *     }
 * </pre>
 */
public class PageAnswer<E> implements Answer<Page<E>> {

    private final Iterable<E> answer;
    private final boolean isLast;

    private PageAnswer(Iterable<E> answer, boolean isLastPage) {
        this.answer = checkNotNull(answer, "answer");
        isLast = isLastPage;
    }

    @Nonnull
    public static <F> PageAnswer<F> withPageOf(@Nonnull Iterable<F> elements) {
        return new PageAnswer<>(elements, false);
    }

    @Nonnull
    @SafeVarargs
    public static <F> PageAnswer<F> withPageOf(@Nonnull F... elements) {
        return withPageOf(asList(elements));
    }

    public static <F> PageAnswer<F> withLastPageOf(@Nonnull Iterable<F> elements) {
        return new PageAnswer<>(elements, true);
    }

    @Nonnull
    @SafeVarargs
    public static <F> PageAnswer<F> withLastPageOf(@Nonnull F... elements) {
        return new PageAnswer<>(asList(elements), true);
    }

    @Nonnull
    public static <F> PageAnswer<F> withEmptyPageOf(@Nonnull Class<F> elementType) {
        checkNotNull(elementType, "elementType");
        return new PageAnswer<>(Collections.<F>emptyList(), true);
    }

    @Nonnull
    @Override
    public Page<E> answer(InvocationOnMock invocation) throws Throwable {
        return PageUtils.createPage(answer, isLast, MockitoUtils.findPageRequest(invocation));
    }
}
