package de.espend.idea.laravel.controller;

import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.psi.elements.Field;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import de.espend.idea.laravel.LaravelSettings;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.PhpElementsUtil;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.HashSet;

/**
 * @author Daniel Espendiller <daniel@espendiller.net>
 */
public class ControllerCollector {

    public static void visitControllerActions(@NotNull final Project project, @NotNull ControllerActionVisitor visitor, @Nullable String prefix) {

        Collection<PhpClass> allSubclasses = new HashSet<PhpClass>() {{
            addAll(PhpIndex.getInstance(project).getAllSubclasses("\\Illuminate\\Routing\\Controller"));
            addAll(PhpIndex.getInstance(project).getAllSubclasses("\\App\\Http\\Controllers\\Controller"));
        }};

        HashSet<String> commonTraits = new HashSet<String>();
        commonTraits.add("ValidatesRequests");
        commonTraits.add("DispatchesCommands");
        commonTraits.add("AuthorizesRequests");
        commonTraits.add("Controller");

        String ns = getDefaultNamespace(project) + "\\";
        String prefixedNs = ns + (prefix != null && !prefix.equals("") ? prefix + "\\":"");

        for(PhpClass phpClass: allSubclasses) {
            if(!phpClass.isAbstract()) {
                for(Method method: phpClass.getMethods()) {
                    String className = phpClass.getPresentableFQN();
                    String methodName = method.getName();
                    if(!method.isStatic() && method.getAccess().isPublic() && !methodName.startsWith("__")) {
                        PhpClass phpTrait = method.getContainingClass();
                        if(phpTrait == null || !commonTraits.contains(phpTrait.getName())) {

                            boolean prioritised = false;
                            if(prefix != null && className.startsWith(prefixedNs)) {
                                className = className.substring(prefixedNs.length());
                                prioritised = true;
                            } else if(className.startsWith(ns)) {
                                className = className.substring(ns.length());
                            }

                            if(StringUtils.isNotBlank(className)) {
                                visitor.visit(phpClass, method, className + "@" + methodName, prioritised);
                            }
                        }
                    }
                }
            }
        }
    }

    @NotNull
    public static String getDefaultNamespace(@NotNull Project project) {

        String controllerNamespace = LaravelSettings.getInstance(project).routerNamespace;
        if(controllerNamespace != null && StringUtils.isNotBlank(controllerNamespace)) {
            return StringUtils.stripStart(controllerNamespace, "\\");
        }

        for(PhpClass providerPhpClass: PhpIndex.getInstance(project).getAllSubclasses("\\Illuminate\\Foundation\\Support\\Providers\\RouteServiceProvider")) {

            Field namespace = providerPhpClass.findOwnFieldByName("namespace", false);
            if(namespace == null) {
                continue;
            }

            PsiElement defaultValue = namespace.getDefaultValue();
            if(defaultValue == null) {
                continue;
            }

            String stringValue = PhpElementsUtil.getStringValue(defaultValue);
            if(stringValue != null) {
                return StringUtils.stripStart(stringValue, "\\");
            }
        }

        return "App\\Http\\Controllers";
    }

    public interface ControllerActionVisitor {
        void visit(@NotNull PhpClass phpClass, @NotNull Method method, @NotNull String name, boolean prioritised);
    }

    public static void visitController(@NotNull final Project project, @NotNull ControllerVisitor visitor, @Nullable String prefix) {

        Collection<PhpClass> allSubclasses = new HashSet<PhpClass>() {{
            addAll(PhpIndex.getInstance(project).getAllSubclasses("\\Illuminate\\Routing\\Controller"));
            addAll(PhpIndex.getInstance(project).getAllSubclasses("\\App\\Http\\Controllers\\Controller"));
        }};

        String ns = getDefaultNamespace(project) + "\\";
        String prefixedNs = ns + (prefix != null && !prefix.equals("") ? prefix + "\\":"");

        for(PhpClass phpClass: allSubclasses) {

            if(phpClass.isAbstract()) {
                continue;
            }

            String className = phpClass.getPresentableFQN();

            boolean prioritised = false;
            if(prefix != null && className.startsWith(prefixedNs)) {
                className = className.substring(prefixedNs.length());
                prioritised = true;
            } else if(className.startsWith(ns)) {
                className = className.substring(ns.length());
            }

            if(StringUtils.isNotBlank(className)) {
                visitor.visit(phpClass, className, prioritised);
            }
        }
    }

    public interface ControllerVisitor {
        void visit(@NotNull PhpClass phpClass, @NotNull String name, boolean prioritised);
    }

}


@@ -583,44 +583,9 @@
 til.
-Collection;%0Aimport java.util.HashSet
+*
 ;%0A%0A/
@@ -678,24 +678,116 @@
 ollector %7B%0A%0A
+    private static final Set%3CString%3E commonControllerTraits = getCommonControllerTraits();%0A%0A
     public s
@@ -1224,254 +1224,8 @@
 %7D;%0A%0A
-        HashSet%3CString%3E commonTraits = new HashSet%3CString%3E();%0A        commonTraits.add(%22ValidatesRequests%22);%0A        commonTraits.add(%22DispatchesCommands%22);%0A        commonTraits.add(%22AuthorizesRequests%22);%0A        commonTraits.add(%22Controller%22);%0A%0A
     
@@ -1825,24 +1825,24 @@
 ingClass();%0A
-
             
@@ -1883,16 +1883,26 @@
  !common
+Controller
 Traits.c
@@ -5193,32 +5193,343 @@
        %7D%0A    %7D%0A%0A
+    @NotNull%0A    private static Set%3CString%3E getCommonControllerTraits() %7B%0A        Set%3CString%3E traits = new HashSet%3C%3E();%0A%0A        traits.add(%22ValidatesRequests%22);%0A        traits.add(%22DispatchesCommands%22);%0A        traits.add(%22AuthorizesRequests%22);%0A        traits.add(%22Controller%22);%0A%0A        return traits;%0A    %7D%0A%0A
     public inter

optimize common controller traits set generation