// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.devkit.references;

import com.intellij.codeInspection.unused.ImplicitPropertyUsageProvider;
import com.intellij.lang.properties.PropertiesFileType;
import com.intellij.lang.properties.psi.Property;
import com.intellij.lang.properties.psi.impl.PropertyKeyImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.StandardPatterns;
import com.intellij.patterns.VirtualFilePattern;
import com.intellij.psi.*;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.xml.XmlElement;
import com.intellij.util.CommonProcessors;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.devkit.dom.ActionOrGroup;
import org.jetbrains.idea.devkit.dom.index.IdeaPluginRegistrationIndex;
import org.jetbrains.idea.devkit.util.PsiUtil;

import java.util.Arrays;
import java.util.Objects;

import static com.intellij.patterns.PlatformPatterns.virtualFile;

public class DevKitActionOrGroupIdReferenceContributor extends PsiReferenceContributor {
  @NonNls private static final String ACTION = "action.";
  @NonNls private static final String GROUP = "group.";
  @NonNls private static final String TEXT = ".text";
  @NonNls private static final String DESC = ".description";
  @NonNls private static final String BUNDLE_PROPERTIES = "Bundle.properties";
  
  public static final PsiElementResolveResult[] EMPTY_RESOLVE_RESULT = new PsiElementResolveResult[0];

  @Override
  public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
    registrar.registerReferenceProvider(
      PlatformPatterns.psiElement(PropertyKeyImpl.class).inVirtualFile(bundleFile()),
      new PsiReferenceProvider() {
        @NotNull
        @Override
        public PsiReference[] getReferencesByElement(@NotNull PsiElement element,
                                                     @NotNull ProcessingContext context) {
          if (!(element instanceof PropertyKeyImpl)) return PsiReference.EMPTY_ARRAY;
          if (!PsiUtil.isPluginProject(element.getProject())) return PsiReference.EMPTY_ARRAY;

          String text = ((PropertyKeyImpl)element).getText();
          return JBIterable.of(
            createRef(element, text, ACTION, TEXT),
            createRef(element, text, ACTION, DESC),
            createRef(element, text, GROUP, TEXT),
            createRef(element, text, GROUP, DESC)).filter(Objects::nonNull).toArray(PsiReference.EMPTY_ARRAY);
        }

        @Nullable
        private PsiReference createRef(@NotNull PsiElement element, String text, String prefix, String suffix) {
          if (!text.startsWith(prefix) || !text.endsWith(suffix)) return null;

          String id = text.replace(prefix, "").replace(suffix, "");
          return new DevKitActionReference(id, prefix, element);
        }
      });
  }

  private static VirtualFilePattern bundleFile() {
    return virtualFile().ofType(PropertiesFileType.INSTANCE).withName(StandardPatterns.string().endsWith(BUNDLE_PROPERTIES));
  }

  private static class DevKitActionReference extends PsiPolyVariantReferenceBase<PsiElement> {
    private final String myId;
    private final boolean myIsAction;

    private DevKitActionReference(String id, String prefix, @NotNull PsiElement element) {
      super(element, TextRange.allOf(id).shiftRight(prefix.length()));
      myIsAction = prefix.equals(ACTION);
      myId = id;
    }

    @NotNull
    @Override
    public ResolveResult[] multiResolve(boolean incompleteCode) {
      Project project = getElement().getProject();

      CommonProcessors.CollectUniquesProcessor<ActionOrGroup> processor = new CommonProcessors.CollectUniquesProcessor<>();
      if (myIsAction) {
        IdeaPluginRegistrationIndex.processAction(project, myId, ProjectScope.getContentScope(project), processor);
      }
      else {
        IdeaPluginRegistrationIndex.processGroup(project, myId, ProjectScope.getContentScope(project), processor);
      }

      return JBIterable
        .from(processor.getResults())
        .map(a -> {
          XmlElement xml = a.getId().getXmlAttributeValue();
          return xml == null ? null : new PsiElementResolveResult(xml);
        }).filter(Objects::nonNull).toArray(EMPTY_RESOLVE_RESULT);
    }
  }

  public static class ImplicitUsageProvider extends ImplicitPropertyUsageProvider {
    @Override
    protected boolean isUsed(@NotNull Property property) {
      PsiFile file = property.getContainingFile();
      String fileName = file.getName();
      if (!fileName.endsWith(BUNDLE_PROPERTIES)) return false;
      String name = property.getName();
      if (name == null) return false;
      if ((name.startsWith(ACTION) || name.startsWith(GROUP)) && 
          (name.endsWith(TEXT) || name.endsWith(DESC))) {
        PsiElement key = property.getFirstChild();
        PsiReference[] references = key == null ? PsiReference.EMPTY_ARRAY : key.getReferences();
        return Arrays.stream(references).anyMatch(reference -> reference.resolve() != null);
      }
      return false;
    }
  }
}

@@ -654,16 +654,63 @@
 attern;%0A
+import com.intellij.pom.references.PomService;%0A
 import c
@@ -728,16 +728,16 @@
 .psi.*;%0A
-
 import c
@@ -777,48 +777,8 @@
 pe;%0A
-import com.intellij.psi.xml.XmlElement;%0A
 impo
@@ -904,24 +904,64 @@
 JBIterable;%0A
+import com.intellij.util.xml.DomTarget;%0A
 import org.j
@@ -1454,16 +1454,17 @@
 butor %7B%0A
+%0A
   @NonNl
@@ -1767,11 +1767,9 @@
 s%22;%0A
-  
 %0A
+
   pu
@@ -3883,16 +3883,26 @@
 veResult
+ @NotNull 
 %5B%5D multi
@@ -4472,16 +4472,28 @@
   .map(a
+ctionOrGroup
  -%3E %7B%0A  
@@ -4504,56 +4504,66 @@
     
-XmlElement xml = a.getId().getXmlAttributeValue(
+final DomTarget target = DomTarget.getTarget(actionOrGroup
 );%0A 
@@ -4578,19 +4578,22 @@
  return 
-xml
+target
  == null
@@ -4630,19 +4630,56 @@
 eResult(
-xml
+PomService.convertToPsi(project, target)
 );%0A     
@@ -5136,24 +5136,24 @@
 turn false;%0A
+
       if ((n
@@ -5204,17 +5204,16 @@
 OUP)) &&
- 
 %0A       

resolve to Action, not XML attribute