package com.atlassian.bitbucket.mesh.test.context;

import org.springframework.core.OrderComparator;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * A {@code TestExecutionListener} implementation which chains one or more other listeners, applying them in configured
 * order for methods before the test is executed and in reverse order for methods executed after the test.
 */
public class ChainTestExecutionListener implements TestExecutionListener {

    //Both collections contain the same listeners, with the after list in reverse order from the before list. This is
    //necessary to honor the stack-like contract of listeners.
    private List<TestExecutionListener> afterListeners;
    private List<TestExecutionListener> beforeListeners;

    public ChainTestExecutionListener() {
    }

    public ChainTestExecutionListener(Collection<TestExecutionListener> listeners) {
        setTestExecutionListeners(listeners);
    }

    @Override
    public void afterTestClass(@Nonnull TestContext testContext) throws Exception {
        for (TestExecutionListener listener : afterListeners) {
            listener.afterTestClass(testContext);
        }
    }

    @Override
    public void afterTestMethod(@Nonnull TestContext testContext) throws Exception {
        for (TestExecutionListener listener : afterListeners) {
            listener.afterTestMethod(testContext);
        }
    }

    @Override
    public void beforeTestClass(@Nonnull TestContext testContext) throws Exception {
        for (TestExecutionListener listener : beforeListeners) {
            listener.beforeTestClass(testContext);
        }
    }

    @Override
    public void beforeTestMethod(@Nonnull TestContext testContext) throws Exception {
        for (TestExecutionListener listener : beforeListeners) {
            listener.beforeTestMethod(testContext);
        }
    }

    @Override
    public void prepareTestInstance(@Nonnull TestContext testContext) throws Exception {
        for (TestExecutionListener listener : beforeListeners) {
            listener.prepareTestInstance(testContext);
        }
    }

    public void setTestExecutionListener(TestExecutionListener listener) {
        setTestExecutionListeners(Collections.singleton(listener));
    }

    public void setTestExecutionListeners(Collection<TestExecutionListener> listeners) {
        beforeListeners = new ArrayList<>(listeners);
        OrderComparator.sort(beforeListeners);

        //Reverse the listener order for the after listeners so they are unwound like a stack
        afterListeners = new ArrayList<>(listeners);
        Collections.reverse(afterListeners);
    }
}
