package com.theoryinpractice.testng.inspection;

import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiNameValuePair;
import com.theoryinpractice.testng.util.TestNGUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Hani Suleiman Date: Aug 3, 2005 Time: 3:34:56 AM
 */
public class DependsOnMethodInspection extends BaseJavaLocalInspectionTool
{
    private static final Logger LOGGER = Logger.getInstance("TestNG Runner");
    private static final Pattern PATTERN = Pattern.compile("\"([a-zA-Z1-9_\\(\\)]*)\"");
    private static final ProblemDescriptor[] EMPTY = new ProblemDescriptor[0];
    
    @NotNull
    @Override
    public String getGroupDisplayName() {
        return "TestNG";
    }

    @NotNull
    @Override
    public String getDisplayName() {
        return "dependsOnMethods problem";
    }

    @NotNull
    @Override
    public String getShortName() {
        return "dependsOnMethodTestNG";
    }

    public boolean isEnabledByDefault() {
        return true;
    }

    @Override
    @Nullable
    public ProblemDescriptor[] checkClass(@NotNull PsiClass psiClass, @NotNull InspectionManager manager, boolean isOnTheFly) {

        //LOGGER.info("Looking for dependsOnMethods problems in " + psiClass.getName());

        if (!psiClass.getContainingFile().isWritable()) return null;

        PsiAnnotation[] annotations = TestNGUtil.getTestNGAnnotations(psiClass);
        if(annotations.length == 0) return EMPTY;
        List<ProblemDescriptor> problemDescriptors = new ArrayList<ProblemDescriptor>();

        for (PsiAnnotation annotation : annotations) {
            PsiNameValuePair dep = null;
            PsiNameValuePair[] params = annotation.getParameterList().getAttributes();
            for (PsiNameValuePair param : params) {
                if ("dependsOnMethods".equals(param.getName())) {
                    dep = param;
                    break;
                }
            }

            if (dep != null) {
                if (dep.getValue() != null) {
                    Matcher matcher = PATTERN.matcher(dep.getValue().getText());
                    while (matcher.find()) {
                        String methodName = matcher.group(1);
                        checkMethodNameDependency(manager, psiClass, methodName, dep, problemDescriptors);
                    }
                }
            }
        }
        
        return problemDescriptors.toArray(new ProblemDescriptor[] {} );
    }

    private void checkMethodNameDependency(InspectionManager manager, PsiClass psiClass, String methodName, PsiNameValuePair dep, List<ProblemDescriptor> problemDescriptors) {
        LOGGER.debug("Found dependsOnMethods with text: " + methodName);
        if (methodName.length() > 0 && methodName.charAt(methodName.length() - 1) == ')') {

            LOGGER.debug("dependsOnMethods contains ()" + psiClass.getName());
            // TODO Add quick fix for removing brackets on annotation
            ProblemDescriptor descriptor = manager.createProblemDescriptor(dep,
                                                               "Method '" + methodName + "' should not include () characters.",
                                                               (LocalQuickFix) null,
                                                               ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);

            problemDescriptors.add(descriptor);

        } else {
            boolean methodExists = false;
            PsiMethod[] methods = psiClass.getMethods();
            PsiMethod foundMethod = null;
            for (PsiMethod method : methods) {
                LOGGER.debug("Checking method with name: " + method.getName());
                if (method.getName().equals(methodName)) {
                    methodExists = true;
                    break;
                }
            }

            if (!methodExists) {
                LOGGER.debug("dependsOnMethods method doesn't exist:" + methodName);
                ProblemDescriptor descriptor = manager.createProblemDescriptor(dep,
                                                                   "Method '" + methodName + "' unknown.",
                                                                   (LocalQuickFix) null,
                                                                   ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
                problemDescriptors.add(descriptor);

            } else if(foundMethod != null && !TestNGUtil.hasTest(foundMethod) && !!TestNGUtil.hasConfig(foundMethod)) {
                ProblemDescriptor descriptor = manager.createProblemDescriptor(dep,
                                                                   "Method '" + methodName + "' is not a test or configuration method.",
                                                                   (LocalQuickFix) null,
                                                                   ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
                problemDescriptors.add(descriptor);
            }
        }
    }

}
@@ -2789,16 +2789,23 @@
 private 
+static 
 void che
@@ -3745,50 +3745,8 @@
 e %7B%0A
-            boolean methodExists = false;%0A
     
@@ -3765,17 +3765,22 @@
 ethod%5B%5D 
-m
+foundM
 ethods =
@@ -3793,380 +3793,86 @@
 ass.
-getMethods();%0A            PsiMethod foundMethod = null;%0A            for (PsiMethod method : methods) %7B%0A                LOGGER.debug(%22Checking method with name: %22 + method.getName());%0A                if (method.getName().equals(methodName)) %7B%0A                    methodExists = true;%0A                    break;%0A                %7D%0A            %7D%0A%0A            if (!methodExists
+findMethodsByName(methodName, true);%0A%0A            if (foundMethods.length == 0
 ) %7B%0A
@@ -4422,35 +4422,156 @@
 lse 
-if(foundMethod != null && !
+%7B%0A              boolean hasTestsOrConfigs = false;%0A              for (PsiMethod foundMethod : foundMethods) %7B%0A                 hasTestsOrConfigs %7C= 
 Test
@@ -4602,13 +4602,11 @@
 od) 
-&& !!
+%7C%7C 
 Test
@@ -4634,16 +4634,70 @@
 dMethod)
+;%0A              %7D%0A              if (!hasTestsOrConfigs
 ) %7B%0A    
@@ -4768,32 +4768,34 @@
 Descriptor(dep,%0A
+  
                 
@@ -4974,32 +4974,34 @@
                 
+  
 (LocalQuickFix) 
@@ -4998,32 +4998,34 @@
 QuickFix) null,%0A
+  
                 
@@ -5170,16 +5170,32 @@
 iptor);%0A
+              %7D%0A
         

check base class for test and config methods (IDEADEV-28040)