package com.cedricziel.idea.typo3.extbase.persistence.codeInsight;

import com.cedricziel.idea.typo3.extbase.ExtbaseUtils;
import com.cedricziel.idea.typo3.util.ExtbaseUtility;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.PhpIcons;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.lexer.PhpTokenTypes;
import com.jetbrains.php.lang.psi.elements.*;
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;

public class RepositoryMagicMethodsCompletionContributor extends CompletionContributor {
    public RepositoryMagicMethodsCompletionContributor() {
        this.extend(CompletionType.BASIC, PlatformPatterns.psiElement(PhpTokenTypes.IDENTIFIER), new ExtbaseRepositoryMagicMethodsCompletionProvider());
    }

    public static class ExtbaseRepositoryMagicMethodsCompletionProvider extends CompletionProvider<CompletionParameters> {

        protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
            PsiElement position = parameters.getPosition().getOriginalElement();

            if (!(position.getParent() instanceof FieldReference) && !(position.getParent() instanceof MethodReference)) {
                return;
            }

            if (!(position.getParent().getFirstChild() instanceof Variable) && !(position.getParent().getFirstChild() instanceof FieldReference)) {
                return;
            }

            PhpTypedElement variable = (PhpTypedElement) position.getParent().getFirstChild();

            PhpType type = variable.getType();
            type.getTypes().forEach(t -> {
                Collection<PhpClass> classesByFQN = PhpIndex.getInstance(position.getProject()).getClassesByFQN(t);
                classesByFQN
                        .stream()
                        .filter(ExtbaseUtils::isRepositoryClass)
                        .forEach(c -> createLookupElementsForRepository(variable, c, result));
            });
        }

        private void createLookupElementsForRepository(@NotNull PhpTypedElement position, @NotNull PhpClass repositoryClass, @NotNull CompletionResultSet result) {
            String potentialModelClass = ExtbaseUtility.convertRepositoryFQNToEntityFQN(repositoryClass.getFQN());

            Collection<PhpClass> classesByFQN = PhpIndex.getInstance(position.getProject()).getClassesByFQN(potentialModelClass);
            classesByFQN.forEach(c -> {
                c.getFields().forEach(f -> {
                    if (!ExtbaseUtils.fieldHasMagicFinders(f)) {
                        return;
                    }

                    // findBy* matches on single fields only and not on collections
                    result.addElement(new LookupElement() {
                        @NotNull
                        @Override
                        public String getLookupString() {
                            return "findBy" + StringUtils.capitalize(f.getName()) + "($" + f.getName() + ")";
                        }

                        @Override
                        public void renderElement(LookupElementPresentation presentation) {
                            super.renderElement(presentation);

                            presentation.setItemText("findBy" + StringUtils.capitalize(f.getName()));
                            presentation.setTypeText("findBy" + StringUtils.capitalize(f.getName()));
                            presentation.setIcon(PhpIcons.METHOD_ICON);
                            presentation.setTailText("(" + f.getName() + " : " + f.getDeclaredType() + ")", true);
                            presentation.setTypeText(c.getName() + "[]|" + ExtbaseUtils.QUERY_RESULT_INTERFACE);
                        }
                    });

                    // countBy* matches on single fields only and not on collections
                    result.addElement(new LookupElement() {
                        @NotNull
                        @Override
                        public String getLookupString() {
                            return "countBy" + StringUtils.capitalize(f.getName()) + "($" + f.getName() + ")";
                        }

                        @Override
                        public void renderElement(LookupElementPresentation presentation) {
                            super.renderElement(presentation);

                            presentation.setItemText("countBy" + StringUtils.capitalize(f.getName()));
                            presentation.setTypeText("countBy" + StringUtils.capitalize(f.getName()));
                            presentation.setIcon(PhpIcons.METHOD_ICON);
                            presentation.setTailText("(" + f.getName() + " : " + f.getDeclaredType() + ")", true);
                            presentation.setTypeText("int");
                        }
                    });

                    // findOneBy* matches on single fields only and not on collections
                    result.addElement(new LookupElement() {
                        @NotNull
                        @Override
                        public String getLookupString() {
                            return "findOneBy" + StringUtils.capitalize(f.getName()) + "($" + f.getName() + ")";
                        }

                        @Override
                        public void renderElement(LookupElementPresentation presentation) {
                            super.renderElement(presentation);

                            presentation.setItemText("findOneBy" + StringUtils.capitalize(f.getName()));
                            presentation.setTypeText("findOneBy" + StringUtils.capitalize(f.getName()));
                            presentation.setIcon(PhpIcons.METHOD_ICON);
                            presentation.setTailText("(" + f.getName() + " : " + f.getDeclaredType() + ")", true);
                            presentation.setTypeText("null|" + c.getName());
                        }
                    });
                });
            });
        }
    }
}

@@ -336,16 +336,61 @@
 tation;%0A
+import com.intellij.openapi.project.Project;%0A
 import c
@@ -2001,16 +2001,69 @@
 Type();%0A
+            Project project = position.getProject();%0A
         
@@ -2171,36 +2171,22 @@
 stance(p
-osition.getP
 roject
-()
 ).getCla
@@ -2399,24 +2399,23 @@
 ository(
-variable
+project
 , c, res
@@ -2517,32 +2517,24 @@
 ll P
-hpTypedElement position,
+roject project, 
  @No
@@ -2790,36 +2790,22 @@
 stance(p
-osition.getP
 roject
-()
 ).getCla

Fix access to project in 2017.3