package liquibase.extension.testing.testsystem.spock;

import liquibase.Scope;
import liquibase.extension.testing.testsystem.TestSystem;
import org.spockframework.runtime.extension.AbstractMethodInterceptor;
import org.spockframework.runtime.extension.IMethodInvocation;
import org.spockframework.runtime.model.FieldInfo;
import org.spockframework.runtime.model.SpecInfo;

import java.util.ArrayList;
import java.util.List;

public class LiquibaseIntegrationMethodInterceptor extends AbstractMethodInterceptor {

    private final SpecInfo spec;
    private final LiquibaseIntegrationTestExtension.ErrorListener errorListener;

    LiquibaseIntegrationMethodInterceptor(SpecInfo spec, LiquibaseIntegrationTestExtension.ErrorListener errorListener) {
        this.spec = spec;
        this.errorListener = errorListener;
    }

    @Override
    public void interceptSetupSpecMethod(IMethodInvocation invocation) throws Throwable {
        final List<FieldInfo> containers = findAllContainers();
        startContainers(containers, invocation);

        invocation.proceed();
    }

    @Override
    public void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable {
        final List<FieldInfo> containers = findAllContainers();
        stopContainers(containers, invocation);

        invocation.proceed();
    }


    private List<FieldInfo> findAllContainers() {
        List<FieldInfo> returnList = new ArrayList<>();
        for (FieldInfo fieldInfo : spec.getAllFields()) {
            if (TestSystem.class.isAssignableFrom(fieldInfo.getType())) {
                assert fieldInfo.isShared() : "TestEnvironment field " + fieldInfo.getName() + " must be @Shared";
                returnList.add(fieldInfo);
            }
        }
        return returnList;
    }

    private static void startContainers(List<FieldInfo> containers, IMethodInvocation invocation) throws Exception {
        for (FieldInfo field : containers) {
            TestSystem env = readContainerFromField(field, invocation);
            env.start();

        }
    }

    private void stopContainers(List<FieldInfo> containers, IMethodInvocation invocation) throws Exception {
        for (FieldInfo field : containers) {
            TestSystem testSystem = readContainerFromField(field, invocation);

            try {
                testSystem.stop();
            } catch (Exception e) {
                Scope.getCurrentScope().getLog(getClass()).warning("Cannot stop "+testSystem.getDefinition());
            }

        }
    }

    private static TestSystem readContainerFromField(FieldInfo f, IMethodInvocation invocation) {
        return (TestSystem) f.readValue(invocation.getInstance());
    }
}

@@ -93,47 +93,221 @@
 ase.
-extension.testing.testsystem.TestSystem
+configuration.ConfigurationValueConverter;%0Aimport liquibase.configuration.LiquibaseConfiguration;%0Aimport liquibase.extension.testing.testsystem.TestSystem;%0Aimport liquibase.util.StringUtil;%0Aimport org.junit.Assume
 ;%0Aim
@@ -590,16 +590,70 @@
 il.List;
+%0Aimport java.util.SortedSet;%0Aimport java.util.TreeSet;
 %0A%0Apublic
@@ -734,16 +734,106 @@
 ptor %7B%0A%0A
+    private static final SortedSet%3CTestSystem.Definition%3E testSystems = new TreeSet%3C%3E();%0A%0A
     priv
@@ -939,16 +939,581 @@
 tener;%0A%0A
+    private static final String configuredTestSystems;%0A%0A    static %7B%0A        //cache configured test systems for faster lookup%0A        configuredTestSystems = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).getCurrentConfiguredValue(ConfigurationValueConverter.STRING, null, %22liquibase.sdk.testSystem.test%22).getValue();%0A        if (configuredTestSystems != null) %7B%0A            for (String definition : StringUtil.splitAndTrim(configuredTestSystems, %22,%22))%0A                testSystems.add(TestSystem.Definition.parse(definition));%0A        %7D%0A    %7D%0A%0A
     Liqu
@@ -2854,19 +2854,26 @@
 tSystem 
-env
+testSystem
  = readC
@@ -2910,16 +2910,17 @@
 ation);%0A
+%0A
         
@@ -2927,11 +2927,197 @@
     
-env
+Assume.assumeTrue(%22Not running test against %22 + testSystem.getDefinition() + %22: liquibase.sdk.testSystem.test is %22 + configuredTestSystems, testSystem.shouldTest());%0A%0A            testSystem
 .sta
@@ -3286,32 +3286,32 @@
 : containers) %7B%0A
-
             Test
@@ -3366,32 +3366,117 @@
 , invocation);%0A%0A
+            if (!testSystem.shouldTest()) %7B%0A                continue;%0A            %7D%0A%0A
             try 

Fix spock TestSystem handling