package com.frameworkium.core.common.listeners;

import com.frameworkium.core.common.reporting.TestIdUtils;
import com.frameworkium.core.common.reporting.jira.api.SearchIssues;
import com.frameworkium.core.ui.driver.Driver;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.testng.*;

import java.lang.reflect.Method;
import java.util.List;

import static com.frameworkium.core.common.properties.Property.JIRA_URL;
import static com.frameworkium.core.common.properties.Property.JQL_QUERY;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;

public class MethodInterceptor implements IMethodInterceptor {

    private static final Logger logger = LogManager.getLogger();
    private static boolean interceptMethodsBasedOnName = false;

    @Override
    public List<IMethodInstance> intercept(
            List<IMethodInstance> methods, ITestContext context) {

        List<IMethodInstance> methodsToRun = filterTestsToRunByJQL(methods);
        if (interceptMethodsBasedOnName) {
            logger.info("Filtering tests based on their name.");
            return filterTestsToRunByDriverAndTestClassName(methodsToRun);
        } else {
            return methodsToRun;
        }
    }

    private List<IMethodInstance> filterTestsToRunByJQL(
            List<IMethodInstance> methodsToBeFiltered) {

        if (JQL_QUERY.isSpecified() && JIRA_URL.isSpecified()) {
            logger.info("Filtering specified tests to run with JQL query results");

            List<IMethodInstance> methodsWithTestIDs = methodsToBeFiltered.stream()
                    .filter(m -> TestIdUtils.getIssueOrTestCaseIdValue(m).isPresent())
                    .collect(toList());

            List<String> testIDsFromJQL =
                    new SearchIssues(JQL_QUERY.getValue()).getKeys();

            List<IMethodInstance> methodsToRun = methodsWithTestIDs.stream()
                    .filter(m -> testIDsFromJQL.contains(
                            TestIdUtils.getIssueOrTestCaseIdValue(m).orElseThrow(IllegalStateException::new)))
                    .collect(toList());

            logTestMethodInformation(
                    methodsToBeFiltered, methodsWithTestIDs, methodsToRun);

            return methodsToRun;
        } else {
            // Can't run the JQL without both JIRA_URL and JQL_QUERY
            return methodsToBeFiltered;
        }
    }

    // TODO - make this non-UI specific!
    private List<IMethodInstance> filterTestsToRunByDriverAndTestClassName(
            List<IMethodInstance> methods) {

        return methods.stream()
                .filter(instance -> {
                    String clazz = instance.getMethod().getRealClass().getName();

                    boolean appTest = clazz.endsWith("AppTest");
                    boolean mobiTest = clazz.endsWith("MobiTest");
                    boolean nonMobileNotAppMobiTest = !Driver.isMobile() && !appTest && !mobiTest;
                    boolean nativeAppTest = Driver.isNative() && appTest;
                    boolean nonNativeMobiTest = !Driver.isNative() && mobiTest;

                    return nonMobileNotAppMobiTest || nativeAppTest || nonNativeMobiTest;
                })
                .collect(toList());
    }

    private void logTestMethodInformation(
            List<IMethodInstance> methodsPreFiltering,
            List<IMethodInstance> methodsWithTestIDs,
            List<IMethodInstance> methodsPostFiltering) {

        logger.debug("Running the following test methods:\n{}", () ->
                methodsPostFiltering.stream()
                        .map(m -> getMethodFromIMethod(m).getName())
                        .collect(joining("\n")));

        List<String> methodsWithoutTestIds = methodsPreFiltering.stream()
                .filter(m -> !methodsWithTestIDs.contains(m))
                .map(m -> getMethodFromIMethod(m).getName())
                .collect(toList());

        if (methodsWithoutTestIds.size() > 0) {
            logger.warn("The following tests don't have TestIDs {}",
                    () -> methodsWithoutTestIds.stream().collect(joining(", ")));
        }

        logger.info("Running {} tests specified by JQL query", methodsPostFiltering.size());
    }

    private Method getMethodFromIMethod(IMethodInstance iMethod) {
        return iMethod.getMethod().getConstructorOrMethod().getMethod();
    }
}

@@ -174,55 +174,8 @@
 es;%0A
-import com.frameworkium.core.ui.driver.Driver;%0A
 impo
@@ -210,24 +210,24 @@
 LogManager;%0A
+
 import org.a
@@ -714,72 +714,8 @@
 r();
-%0A    private static boolean interceptMethodsBasedOnName = false;
 %0A%0A  
@@ -850,319 +850,46 @@
     
-List%3CIMethodInstance%3E methodsToRun = filterTestsToRunByJQL(methods);%0A        if (interceptMethodsBasedOnName) %7B%0A            logger.info(%22Filtering tests based on their name.%22);%0A            return filterTestsToRunByDriverAndTestClassName(methodsToRun);%0A        %7D else %7B%0A            return methodsToRun;%0A        %7D
+return filterTestsToRunByJQL(methods);
 %0A   
@@ -2043,16 +2043,16 @@
 ltered;%0A
+
         
@@ -2064,862 +2064,8 @@
  %7D%0A%0A
-    // TODO - make this non-UI specific!%0A    private List%3CIMethodInstance%3E filterTestsToRunByDriverAndTestClassName(%0A            List%3CIMethodInstance%3E methods) %7B%0A%0A        return methods.stream()%0A                .filter(instance -%3E %7B%0A                    String clazz = instance.getMethod().getRealClass().getName();%0A%0A                    boolean appTest = clazz.endsWith(%22AppTest%22);%0A                    boolean mobiTest = clazz.endsWith(%22MobiTest%22);%0A                    boolean nonMobileNotAppMobiTest = !Driver.isMobile() && !appTest && !mobiTest;%0A                    boolean nativeAppTest = Driver.isNative() && appTest;%0A                    boolean nonNativeMobiTest = !Driver.isNative() && mobiTest;%0A%0A                    return nonMobileNotAppMobiTest %7C%7C nativeAppTest %7C%7C nonNativeMobiTest;%0A                %7D)%0A                .collect(toList());%0A    %7D%0A%0A
     

Remove unreachable code