package fi.vincit.multiusertest.runner.junit;

import fi.vincit.multiusertest.annotation.RunWithUsers;
import fi.vincit.multiusertest.util.FocusType;
import fi.vincit.multiusertest.util.UserIdentifier;
import org.junit.runner.Runner;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.TestClass;

import java.lang.reflect.Constructor;
import java.util.*;

/**
 * Creates instances of JUnit test runners
 */
public class TestRunnerFactory {

    private final TestClass testClass;
    private final Constructor runnerConstructor;

    /**
     *
     * @param testClass Test class
     * @param runnerConstructor Test class constructor
     */
    public TestRunnerFactory(TestClass testClass, Constructor runnerConstructor) {
        this.testClass = testClass;
        this.runnerConstructor = runnerConstructor;
    }

    /**
     * Creates runners for each producer consumer combination.
     * @param producerIdentifiers Producer identifiers
     * @param consumerIdentifiers Consumer identifiers
     * @return All required combinations for given identifiers
     * @throws Exception If an exception is thrown
     */
    public List<Runner> createRunnersForRoles(Collection<UserIdentifier> producerIdentifiers, Collection<UserIdentifier> consumerIdentifiers, FocusType focusType) throws Exception {
        List<Runner> runners = new ArrayList<>();
        if (consumerIdentifiers.isEmpty()) {
            consumerIdentifiers.add(UserIdentifier.getWithProducerRole(FocusType.NONE));
        }
        validateProducers(producerIdentifiers);
        validateConsumers(producerIdentifiers, consumerIdentifiers);

        Set<UserIdentifier> allowedIdentifiers = new HashSet<>();
        allowedIdentifiers.addAll(producerIdentifiers);
        allowedIdentifiers.addAll(consumerIdentifiers);

        for (UserIdentifier producerIdentifier : producerIdentifiers) {
            for (UserIdentifier consumerIdentifier : consumerIdentifiers) {
                Object parentRunner;
                if (consumerIdentifier.getIdentifier() != null
                        && consumerIdentifier.getIdentifier().equals(RunWithUsers.WITH_PRODUCER_ROLE)) {
                    parentRunner = runnerConstructor.newInstance(
                            testClass.getJavaClass(),
                            allowedIdentifiers,
                            producerIdentifier,
                            producerIdentifier,
                            focusType
                    );
                } else {
                    parentRunner = runnerConstructor.newInstance(
                            testClass.getJavaClass(),
                            allowedIdentifiers,
                            producerIdentifier,
                            consumerIdentifier,
                            focusType
                    );
                }

                runners.add((ParentRunner) parentRunner);

            }
        }
        return runners;
    }

    void validateConsumers(Collection<UserIdentifier> producerIdentifiers, Collection<UserIdentifier> consumerIdentifiers) {
        boolean containsExistingUserDefinition = false;
        for (UserIdentifier identifier : producerIdentifiers) {
            if (identifier.getType() == UserIdentifier.Type.USER) {
                containsExistingUserDefinition = true;
            }
        }

        if (containsExistingUserDefinition
                && consumerIdentifiers.contains(UserIdentifier.getWithProducerRole(FocusType.NONE))) {
            throw new IllegalArgumentException("User definitions can't contain WITH_PRODUCER_ROLE when producers have a 'user' definition");
        }
    }

    void validateProducers(Collection<UserIdentifier> producerIdentifiers) {
        if (producerIdentifiers.isEmpty()) {
            throw new IllegalArgumentException("Producer must be specified");
        }

        if (producerIdentifiers.contains(UserIdentifier.getProducer(FocusType.NONE))) {
            throw new IllegalArgumentException("Producer can't use PRODUCER role");
        }

        if (producerIdentifiers.contains(UserIdentifier.getWithProducerRole(FocusType.NONE))) {
            throw new IllegalArgumentException("Producer can't use WITH_PRODUCER_ROLE role");
        }
    }

}

@@ -44,64 +44,8 @@
 t;%0A%0A
-import fi.vincit.multiusertest.annotation.RunWithUsers;%0A
 impo
@@ -1921,566 +1921,20 @@
     
+final 
 Object
- parentRunner;%0A                if (consumerIdentifier.getIdentifier() != null%0A                        && consumerIdentifier.getIdentifier().equals(RunWithUsers.WITH_PRODUCER_ROLE)) %7B%0A                    parentRunner = runnerConstructor.newInstance(%0A                            testClass.getJavaClass(),%0A                            allowedIdentifiers,%0A                            producerIdentifier,%0A                            producerIdentifier,%0A                            focusType%0A                    );%0A                %7D else %7B%0A                   
  par
@@ -2212,16 +2212,16 @@
 cusType%0A
+
         
@@ -2238,26 +2238,8 @@
   );
-%0A                %7D
 %0A%0A  

Remove some duplicated code in TestRunnerFactory