// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.refactoring.rename.naming;

import com.intellij.codeInsight.TestFrameworks;
import com.intellij.java.refactoring.JavaRefactoringBundle;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.PsiTypeParameter;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.regex.Pattern;


public class AutomaticTestRenamerFactory implements AutomaticRenamerFactory {
  @Override
  public boolean isApplicable(@NotNull PsiElement element) {
    return element instanceof PsiClass &&
           !(element instanceof PsiTypeParameter) &&
           TestFrameworks.detectFramework((PsiClass)element) == null;
  }

  @Override
  public String getOptionName() {
    return JavaRefactoringBundle.message("rename.tests");
  }

  @Override
  public boolean isEnabled() {
    return JavaRefactoringSettings.getInstance().isToRenameTests();
  }

  @Override
  public void setEnabled(boolean enabled) {
    JavaRefactoringSettings.getInstance().setRenameTests(enabled);
  }

  @Override
  public @NotNull AutomaticRenamer createRenamer(PsiElement element, String newName, Collection<UsageInfo> usages) {
    return new TestsRenamer((PsiNamedElement)element, newName, ((PsiNamedElement)element).getName());
  }

  public static class TestsRenamer extends AutomaticRenamer {
    public TestsRenamer(PsiNamedElement aClass, String newClassName, @Nullable @NlsSafe String className) {
      Module module = ModuleUtilCore.findModuleForPsiElement(aClass);
      if (module != null) {
        GlobalSearchScope moduleScope = GlobalSearchScope.moduleWithDependentsScope(module);

        PsiShortNamesCache cache = PsiShortNamesCache.getInstance(aClass.getProject());

        Pattern pattern = Pattern.compile(".*" + className + ".*");

        int count = 0;
        for (String eachName : ContainerUtil.newHashSet(cache.getAllClassNames())) {
          if (pattern.matcher(eachName).matches()) {
            if (count ++ > 1000) break;
            for (PsiClass eachClass : cache.getClassesByName(eachName, moduleScope)) {
              if (TestFrameworks.detectFramework(eachClass) != null) {
                myElements.add(eachClass);
              }
            }
          }
        }

        suggestAllNames(className, newClassName);
      }
    }

    @Override
    public String getDialogTitle() {
      return JavaRefactoringBundle.message("rename.tests.title");
    }

    @Override
    public String getDialogDescription() {
      return JavaRefactoringBundle.message("rename.tests.with.the.following.names.to");
    }

    @Override
    public String entityName() {
      return JavaRefactoringBundle.message("entity.name.test");
    }
  }
}