package de.espend.idea.php.toolbox.provider.source.contributor;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.parser.PhpElementTypes;
import com.jetbrains.php.lang.psi.elements.*;
import de.espend.idea.php.toolbox.dict.json.JsonProvider;
import de.espend.idea.php.toolbox.extension.SourceContributorInterface;
import de.espend.idea.php.toolbox.provider.source.SourceContributorDeclarationHandlerParameter;
import de.espend.idea.php.toolbox.provider.source.SourceContributorParameter;
import de.espend.idea.php.toolbox.provider.source.contributor.utils.ReturnSourceUtil;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/**
 * @author Daniel Espendiller <[email protected]>
 */
public class ArrayReturnSourceContributor implements SourceContributorInterface {

    @NotNull
    @Override
    public Collection<LookupElement> getLookupElements(@NotNull SourceContributorParameter parameter) {

        Collection<LookupElement> lookupElements = new ArrayList<>();

        ReturnVisitor visitor = null;

        String sourceParameter = parameter.getSourceParameter();
        if(sourceParameter == null) {
            return Collections.emptyList();
        }

        for (Pair<String, String> s : ReturnSourceUtil.extractParameter(sourceParameter)) {
            if(visitor == null) {
                visitor = new MyReturnLookupVisitor(parameter.getJsonProvider(), lookupElements);
            }

            visitReturnElements(parameter.getProject(), s.getFirst(), s.getSecond(), visitor);
        }

        return lookupElements;
    }

    @NotNull
    @Override
    public Collection<PsiElement> getPsiTargets(@NotNull SourceContributorDeclarationHandlerParameter parameter) {

        String contents = parameter.getHandlerParameter().getContents();
        if(StringUtils.isBlank(contents)) {
            return Collections.emptyList();
        }

        String sourceParameter = parameter.getSourceParameter();
        if(sourceParameter == null) {
            return Collections.emptyList();
        }

        final Collection<PsiElement> psiElements = new ArrayList<>();

        ReturnVisitor visitor = null;

        for (Pair<String, String> s : ReturnSourceUtil.extractParameter(sourceParameter)) {
            if(visitor == null) {
                visitor = new MyReturnTargetVisitor(contents, psiElements);
            }

            visitReturnElements(parameter.getProject(), s.getFirst(), s.getSecond(), visitor);
        }

        return psiElements;
    }

    private void visitReturnElements(@NotNull Project project, @NotNull String className, @NotNull String methodName, @NotNull final ReturnVisitor visitor) {

        for (PhpClass phpClass : PhpIndex.getInstance(project).getAllSubclasses(className)) {

            final Method method = phpClass.findOwnMethodByName(methodName);
            if(method == null) {
                continue;
            }

            method.acceptChildren(new PsiRecursiveElementWalkingVisitor() {
                @Override
                public void visitElement(PsiElement element) {

                    if(!(element instanceof PhpReturn)) {
                        super.visitElement(element);
                        return;
                    }

                    PsiElement firstChild = ((PhpReturn) element).getFirstPsiChild();
                    if(!(firstChild instanceof ArrayCreationExpression)) {
                        return;
                    }

                    for (PsiElement arrayValue : firstChild.getChildren()) {

                        if(arrayValue.getNode().getElementType() != PhpElementTypes.ARRAY_VALUE) {
                            continue;
                        }

                        PsiElement stringLiteral = arrayValue.getFirstChild();
                        if(!(stringLiteral instanceof StringLiteralExpression)) {
                            continue;
                        }

                        String contents = ((StringLiteralExpression) stringLiteral).getContents();
                        if(StringUtils.isNotBlank(contents)) {
                            visitor.visit(method, (StringLiteralExpression) stringLiteral, contents);
                        }

                    }

                    super.visitElement(element);
                }
            });

        }
    }

    private interface ReturnVisitor {
        void visit(@NotNull Method method, @NotNull StringLiteralExpression psiElement, @NotNull String contents);
    }

    @NotNull
    @Override
    public String getName() {
        return "return_array";
    }

    private static class MyReturnTargetVisitor implements ReturnVisitor {
        @NotNull
        private final String contents;
        private final Collection<PsiElement> psiElements;

        public MyReturnTargetVisitor(@NotNull String contents, @NotNull Collection<PsiElement> psiElements) {
            this.contents = contents;
            this.psiElements = psiElements;
        }

        @Override
        public void visit(@NotNull Method method, @NotNull StringLiteralExpression psiElement, @NotNull String returnValue) {
            if (contents.equals(returnValue)) {
                psiElements.add(method);
            }
        }
    }

    private static class MyReturnLookupVisitor implements ReturnVisitor {
        @NotNull
        private final JsonProvider provider;
        private final Collection<LookupElement> lookupElements;

        public MyReturnLookupVisitor(@NotNull JsonProvider provider, @NotNull Collection<LookupElement> lookupElements) {
            this.provider = provider;
            this.lookupElements = lookupElements;
        }

        @Override
        public void visit(@NotNull Method method, @NotNull StringLiteralExpression psiElement, @NotNull String contents) {
            LookupElementBuilder decoratedLookupElementBuilder = ReturnSourceUtil.buildLookupElement(method, contents, provider.getDefaults());
            lookupElements.add(decoratedLookupElementBuilder);
        }
    }
}