package pers.fw.tplugin.router;

import pers.fw.tplugin.beans.ArrayKeyVisitor;
import pers.fw.tplugin.beans.LaravelIcons;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.lang.Language;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
import com.intellij.util.indexing.FileBasedIndex;
import com.jetbrains.php.lang.PhpFileType;
import com.jetbrains.php.lang.PhpLanguage;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import pers.fw.tplugin.config.CollectProjectUniqueKeys;
import pers.fw.tplugin.inter.GotoCompletionContributor;
import pers.fw.tplugin.inter.GotoCompletionLanguageRegistrar;
import pers.fw.tplugin.inter.GotoCompletionProvider;
import pers.fw.tplugin.inter.GotoCompletionRegistrarParameter;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import pers.fw.tplugin.model.ModelStubIndex;
import pers.fw.tplugin.util.MethodMatcher;
import pers.fw.tplugin.util.PsiElementUtil;
import pers.fw.tplugin.util.Util;

import java.util.*;

public class RouterReference implements GotoCompletionLanguageRegistrar {

    private static MethodMatcher.CallToSignature[] Router = new MethodMatcher.CallToSignature[]{
            new MethodMatcher.CallToSignature("\\think\\Route", "get"),
            new MethodMatcher.CallToSignature("\\think\\Route", "any"),
            new MethodMatcher.CallToSignature("\\think\\Route", "post"),
            new MethodMatcher.CallToSignature("\\think\\Route", "put"),
            new MethodMatcher.CallToSignature("\\think\\Route", "delete"),
            new MethodMatcher.CallToSignature("\\think\\Route", "resource"),
            new MethodMatcher.CallToSignature("\\think\\Route", "rule"),
            new MethodMatcher.CallToSignature("\\think\\Route", "controller"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "get"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "any"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "post"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "put"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "delete"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "resource"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "rule"),
            new MethodMatcher.CallToSignature("\\think\\facade\\Route", "controller"),
    };


    @Override
    public void register(GotoCompletionRegistrarParameter registrar) {
        registrar.register(PlatformPatterns.psiElement(), new GotoCompletionContributor() {
            @Nullable
            @Override
            public GotoCompletionProvider getProvider(@Nullable PsiElement psiElement) {
                if (psiElement == null) {// || !LaravelProjectComponent.isEnabled(psiElement)) {
                    return null;
                }
                //string
                PsiElement parent = psiElement.getParent();
                PsiFile containingFile = psiElement.getContainingFile();
                containingFile.getName();
                if (parent != null &&
                        (Util.isHintMethod(parent, Router, 1, true)
                                || PsiElementUtil.isFunctionReference(parent, "route", 1)
                                || (RouteUtil.isRouteFile(containingFile) && RouteUtil.isRoutePosition(parent)))) {
                    return new RouteProvider(parent);
                }

                return null;
            }
        });
    }

    @Override
    public boolean support(@NotNull Language language) {
        return PhpLanguage.INSTANCE == language;
    }

    private static class RouteProvider extends GotoCompletionProvider {

        public RouteProvider(PsiElement element) {
            super(element);
        }

        /**
         * 获取提示信息
         *
         * @return 返回提示集合
         */
        @NotNull
        @Override
        public Collection<LookupElement> getLookupElements() {

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

            CollectProjectUniqueKeys ymlProjectProcessor = new CollectProjectUniqueKeys(getProject(), RouteValStubIndex.KEY);
            //扫描文件获取key, 放入ymlProjectProcessor
            FileBasedIndex.getInstance().processAllKeys(RouteValStubIndex.KEY, ymlProjectProcessor, getProject());
            for (String key : ymlProjectProcessor.getResult()) {    //从ymlProjectProcessor中获取结果
                lookupElements.add(LookupElementBuilder.create(key).withIcon(LaravelIcons.ROUTE));
            }

            return lookupElements;
        }

        @NotNull
        @Override
        public Collection<PsiElement> getPsiTargets(StringLiteralExpression element) {

            final Set<PsiElement> targets = new HashSet<>();

            String contents = element.getContents();
            if (StringUtils.isBlank(contents)) {
                return targets;
            }

            //忽略大小写
            Collection<String> allKeys = FileBasedIndex.getInstance().getAllKeys(RouteValStubIndex.KEY, getElement().getProject());

            final String contents1 = Util.getKeyWithCase(allKeys, contents);

            FileBasedIndex.getInstance().getFilesWithKey(RouteValStubIndex.KEY, new HashSet<>(Collections.singletonList(contents1)), new Processor<VirtualFile>() {
                @Override
                public boolean process(VirtualFile virtualFile) {
                    PsiFile psiFileTarget = PsiManager.getInstance(RouteProvider.this.getProject()).findFile(virtualFile);
                    if (psiFileTarget == null) {
                        return true;
                    }
                    String[] split = contents.split("/");
                    if (split.length == 2)
                        targets.add(psiFileTarget);
                    psiFileTarget.acceptChildren(new PhpControllerVisitor(RouteUtil.matchControllerFile(RouteProvider.this.getProject(), virtualFile).getKeyPrefix(),
                            new ArrayKeyVisitor() {
                                @Override
                                public void visit(String key, PsiElement psiKey, boolean isRootElement) {
                                    if (!isRootElement && key.equals(contents1)) {
                                        targets.add(psiKey);
                                    }
                                }
                            }));

                    return true;
                }
            }, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(getProject()), PhpFileType.INSTANCE));

            return targets;
        }
    }
}