// 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.java.codeInsight.completion;

import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.JavaProjectCodeInsightSettings;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.JavaPsiClassReferenceElement;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.impl.NonBlockingReadActionImpl;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.augment.PsiAugmentProvider;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
import com.intellij.psi.impl.light.LightMethodBuilder;
import com.intellij.psi.impl.source.PsiExtensibleClass;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.NeedsIndex;
import com.intellij.testFramework.ServiceContainerUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.siyeh.ig.style.UnqualifiedFieldAccessInspection;
import one.util.streamex.MoreCollectors;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;

public class NormalCompletionTest extends NormalCompletionTestCase {

  @NotNull
  @Override
  protected LightProjectDescriptor getProjectDescriptor() {
    return JAVA_21;
  }

  public void testSimple() throws Exception {
    configureByFile("Simple.java");
    assertStringItems("_local1", "_local2", "_field", "_baseField", "_method", "_baseMethod");
  }

  public void testCastToPrimitive1() {
    configureByFile("CastToPrimitive1.java");

    for (final LookupElement item : myItems) {
      if (item.getLookupString().equals("int")) return;
    }
    fail();
  }

  public void testCastToPrimitive2() {
    configureByFile("CastToPrimitive2.java");

    for (final LookupElement item : myItems) {
      if (item.getLookupString().equals("int")) return;
    }
    fail();
  }

  public void testCastToPrimitive3() {
    configureByFile("CastToPrimitive3.java");

    for (final LookupElement item : myItems) {
      if (item.getLookupString().equals("int")) return;
    }
    fail();
  }

  public void testWriteInInvokeLater() {
    configureByFile("WriteInInvokeLater.java");
  }

  public void testQualifiedNew1() {
    configure();
    assertStringItems("IInner", "Inner");
  }

  public void testQualifiedNew2() {
    configure();
    assertStringItems("AnInner", "Inner");
  }

  public void testKeywordsInName() {
    doTest("a\n");
  }

  @NeedsIndex.ForStandardLibrary
  public void testSimpleVariable() { doTest("\n"); }

  public void testTypeParameterItemPresentation() {
    configure();
    LookupElementPresentation presentation = renderElement(myItems[0]);
    assertEquals("Param", presentation.getItemText());
    assertEquals(" type parameter of Foo", presentation.getTailText());
    assertNull(presentation.getTypeText());
    assertNull(presentation.getIcon());
    assertFalse(presentation.isItemTextBold());

    presentation = renderElement(myItems[1]);
    assertEquals("Param2", presentation.getItemText());
    assertEquals(" type parameter of goo", presentation.getTailText());
  }

  public void testDisplayDefaultValueInAnnotationMethods() {
    configure();
    LookupElementPresentation presentation = renderElement(myItems[0]);
    assertEquals("myInt", presentation.getItemText());
    assertEquals(" default 42", presentation.getTailText());
    assertTrue(presentation.getTailFragments().get(0).isGrayed());
    assertNull(presentation.getTypeText());
    assertFalse(presentation.isItemTextBold());

    presentation = renderElement(myItems[1]);
    assertEquals("myString", presentation.getItemText());
    assertEquals(" default \"unknown\"", presentation.getTailText());
  }

  @NeedsIndex.ForStandardLibrary
  public void testMethodItemPresentation() {
    configure();
    LookupElementPresentation presentation = renderElement(myItems[0]);
    assertEquals("equals", presentation.getItemText());
    assertEquals("(Object anObject)", presentation.getTailText());
    assertEquals("boolean", presentation.getTypeText());

    assertFalse(ContainerUtil.exists(presentation.getTailFragments(), LookupElementPresentation.TextFragment::isGrayed));
    assertTrue(presentation.isItemTextBold());
  }

  public void testFieldItemPresentationGenerics() {
    configure();
    LookupElementPresentation presentation = renderElement(myItems[0]);
    assertEquals("target", presentation.getItemText());
    assertNull(presentation.getTailText());
    assertEquals("String", presentation.getTypeText());
  }

  @NeedsIndex.ForStandardLibrary
  public void testMethodItemPresentationGenerics() {
    configure();
    LookupElementPresentation presentation = renderElement(myItems[1]);
    assertEquals("add", presentation.getItemText());
    assertEquals("(int index, String element)", presentation.getTailText());
    assertEquals("void", presentation.getTypeText());

    presentation = renderElement(myItems[0]);
    assertEquals("(String e)", presentation.getTailText());
    assertEquals("boolean", presentation.getTypeText());

    assertFalse(ContainerUtil.exists(presentation.getTailFragments(), LookupElementPresentation.TextFragment::isGrayed));
    assertTrue(presentation.isItemTextBold());
  }

  public void testPreferLongerNamesOption() {
    configureByFile("PreferLongerNamesOption.java");

    assertEquals(3, myItems.length);
    assertEquals("abcdEfghIjk", myItems[0].getLookupString());
    assertEquals("efghIjk", myItems[1].getLookupString());
    assertEquals("ijk", myItems[2].getLookupString());

    LookupManager.getInstance(getProject()).hideActiveLookup();

    JavaCodeStyleSettings.getInstance(getProject()).PREFER_LONGER_NAMES = false;
    configureByFile("PreferLongerNamesOption.java");

    assertEquals(3, myItems.length);
    assertEquals("ijk", myItems[0].getLookupString());
    assertEquals("efghIjk", myItems[1].getLookupString());
    assertEquals("abcdEfghIjk", myItems[2].getLookupString());
  }

  public void testSCR7208() {
    configureByFile("SCR7208.java");
  }

  @NeedsIndex.ForStandardLibrary
  public void testProtectedFromSuper() {
    configureByFile("ProtectedFromSuper.java");
    Arrays.sort(myItems);
    assertTrue("Exception not found", ContainerUtil.indexOf(Arrays.asList(myItems), le -> le.getLookupString().equals("xxx")) > 0);
  }

  @NeedsIndex.ForStandardLibrary
  public void testBeforeInitialization() {
    configureByFile("BeforeInitialization.java");
    assertNotNull(myItems);
    assertTrue(myItems.length > 0);
  }

  @NeedsIndex.ForStandardLibrary
  public void testProtectedFromSuper2() {
    configureByFile("ProtectedFromSuper.java");
    Arrays.sort(myItems);
    assertTrue("Exception not found", ContainerUtil.indexOf(Arrays.asList(myItems), le -> le.getLookupString().equals("xxx")) > 0);
  }

  @NeedsIndex.ForStandardLibrary
  public void testClassLiteralInArrayAnnoInitializer() { doTest("\n"); }

  @NeedsIndex.ForStandardLibrary
  public void testClassLiteralInArrayAnnoInitializer2() { doTest("\n"); }

  public void testReferenceParameters() {
    configureByFile("ReferenceParameters.java");
    assertNotNull(myItems);
    myFixture.assertPreferredCompletionItems(0, "AAAA", "AAAB");
  }

  @Override
  protected void tearDown() throws Exception {
    try {
      CodeInsightSettings.getInstance().AUTOCOMPLETE_ON_CODE_COMPLETION = true;
      CodeInsightSettings.getInstance().setCompletionCaseSensitive(CodeInsightSettings.FIRST_LETTER);
      CodeInsightSettings.getInstance().setSelectAutopopupSuggestionsByChars(false);
      CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET = true;
    }
    catch (Throwable e) {
      addSuppressedException(e);
    }
    finally {
      super.tearDown();
    }
  }

  public void testConstructorName1() {
    CodeInsightSettings.getInstance().AUTOCOMPLETE_ON_CODE_COMPLETION = false;
    configure();
    assertTrue(myFixture.getLookupElementStrings().contains("ABCDE"));
  }

  public void testConstructorName2() {
    CodeInsightSettings.getInstance().AUTOCOMPLETE_ON_CODE_COMPLETION = false;
    configure();
    assertTrue(myFixture.getLookupElementStrings().contains("ABCDE"));
  }

  @NeedsIndex.ForStandardLibrary
  public void testObjectsInThrowsBlock() {
    configureByFile("InThrowsCompletion.java");
    assertEquals("C", myFixture.getLookupElementStrings().get(0));
    assertTrue(myFixture.getLookupElementStrings().contains("B"));
  }

  public void testAnnoParameterValue() {
    configure();
    List<String> strings = myFixture.getLookupElementStrings();
    assertTrue(strings.contains("AssertionError"));
    assertFalse(strings.contains("enum"));
    assertFalse(strings.contains("final"));
    assertFalse(strings.contains("equals"));
    assertFalse(strings.contains("new"));
    assertFalse(strings.contains("null"));
    assertFalse(strings.contains("public"));
    assertFalse(strings.contains("super"));
    assertFalse(strings.contains("null"));
  }

  public void testAfterInstanceof() {
    configureByFile("AfterInstanceof.java");
    assertTrue(myFixture.getLookupElementStrings().contains("A"));
  }

  public void testAfterCast1() {
    configureByFile("AfterCast1.java");

    assertNotNull(myItems);
    assertEquals(2, myItems.length);
  }

  public void testAfterCast2() {
    configureByFile("AfterCast2.java");
    checkResultByFile("AfterCast2-result.java");
  }

  public void testMethodCallForTwoLevelSelection() {
    configureByFile("MethodLookup.java");
    assertEquals(2, myItems.length);
  }

  public void testMethodCallBeforeAnotherStatementWithParen() {
    configureByFile("MethodLookup2.java");
    checkResultByFile("MethodLookup2_After.java");
  }

  public void testMethodCallBeforeAnotherStatementWithParen2() {
    getCodeStyleSettings().METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE = true;
    configureByFile("MethodLookup2.java");
    checkResultByFile("MethodLookup2_After2.java");
  }

  public void testSwitchEnumLabel() {
    configureByFile("SwitchEnumLabel.java");
    //first B is enum Field, second B is class. They have different presentations and handlers
    assertEquals("[A, B, C, null, B, Object]", ContainerUtil.map(myItems, LookupElement::getLookupString).toString());
  }

  public void testSwitchCaseWithEnumConstant() { doTest(); }

  public void testSecondSwitchCaseWithEnumConstant() { doTest(); }

  @NeedsIndex.ForStandardLibrary
  public void testInsideSwitchCaseWithEnumConstant() {
    configure();
    myFixture.assertPreferredCompletionItems(0, "compareTo", "describeConstable", "equals");
  }

  public void testMethodInAnnotation() {
    configureByFile("Annotation.java");
    checkResultByFile("Annotation_after.java");
  }

  public void testMethodInAnnotation2() {
    configureByFile("Annotation2.java");
    checkResultByFile("Annotation2_after.java");
  }

  public void testMethodInAnnotation3() {
    configureByFile("Annotation3.java");
    checkResultByFile("Annotation3_after.java");
  }

  public void testMethodInAnnotation5() {
    configureByFile("Annotation5.java");
    checkResultByFile("Annotation5_after.java");
  }

  public void testMethodInAnnotation7() {
    configureByFile("Annotation7.java");
    selectItem(myItems[0]);
    checkResultByFile("Annotation7_after.java");
  }

  public void testAnnotationAttrBeforeExisting() {
    doTest("\n");
  }

  public void testAnnotationAttrBeforeExistingBool() {
    doTest("\n");
  }

  public void testEnumInAnnotation() {
    configureByFile("Annotation4.java");
    checkResultByFile("Annotation4_after.java");
  }

  public void testEnumInTypeAnnotation() { doTest(); }

  public void testSecondAttribute() {
    configureByFile("Annotation6.java");
    checkResultByFile("Annotation6_after.java");
  }

  public void testIDEADEV6408() {
    configureByFile("IDEADEV6408.java");
    assertFirstStringItems("boolean", "byte");
  }

  public void testMethodWithLeftParTailType() {
    configureByFile("MethodWithLeftParTailType.java");
    type('(');
    checkResultByFile("MethodWithLeftParTailType_after.java");

    configureByFile("MethodWithLeftParTailType2.java");
    type('(');
    checkResultByFile("MethodWithLeftParTailType2_after.java");
  }

  public void testSuperErasure() {
    configureByFile("SuperErasure.java");
    checkResultByFile("SuperErasure_after.java");
  }

  public void testMethodWithLeftParTailTypeNoPairBrace() {
    CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET = false;
    doTest("(");
  }

  public void testMethodWithLeftParTailTypeNoPairBrace2() {
    CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET = false;

    //no tail type should work the normal way
    configureByFile("MethodWithLeftParTailTypeNoPairBrace.java");
    selectItem(myItems[0]);
    checkResultByFile("MethodWithLeftParTailTypeNoPairBrace_after2.java");
  }

  public void testMethodNoPairBrace() {
    CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET = false;
    doTest("\n");
  }

  public void testExcessSpaceInTypeCast() {
    configure();
    selectItem(myItems[0]);
    checkResult();
  }

  public void testFieldType() { doTest(); }

  public void testFieldOfLocalClass() {
    configure();
    assertEquals("field", renderElement(myItems[0]).getItemText());
    type('\t');
    checkResult();
  }

  public void testPackageInAnnoParam() {
    doTest();
  }

  public void testAnonymousTypeParameter() { doTest(); }

  @NeedsIndex.ForStandardLibrary
  public void testClassLiteralInAnnoParam() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testNoForceBraces() {
    getCodeStyleSettings().IF_BRACE_FORCE = CommonCodeStyleSettings.FORCE_BRACES_ALWAYS;
    doTest("\n");
  }

  public void testExcludeStringBuffer() {
    JavaProjectCodeInsightSettings.setExcludedNames(getProject(), myFixture.getTestRootDisposable(), StringBuffer.class.getName());
    configure();
    assertFalse(myFixture.getLookupElementStrings().contains("StringBuffer"));
  }

  @NeedsIndex.Full
  public void testExcludeInstanceInnerClasses() {
    JavaProjectCodeInsightSettings.setExcludedNames(getProject(), myFixture.getTestRootDisposable(), "foo");
    myFixture.addClass("package foo; public class Outer { public class Inner {} }");
    myFixture.addClass("package bar; public class Inner {}");
    configure();
    assertEquals("bar.Inner", ((JavaPsiClassReferenceElement)myFixture.getLookupElements()[0]).getQualifiedName());
    assertEquals(myFixture.getLookupElementStrings(), List.of("Inner"));
  }

  @NeedsIndex.Full
  public void testExcludedInstanceInnerClassCreation() {
    JavaProjectCodeInsightSettings.setExcludedNames(getProject(), myFixture.getTestRootDisposable(), "foo");
    myFixture.addClass("package foo; public class Outer { public class Inner {} }");
    myFixture.addClass("package bar; public class Inner {}");
    configure();
    assertEquals("foo.Outer.Inner", ((JavaPsiClassReferenceElement)myFixture.getLookupElements()[0]).getQualifiedName());
    assertEquals(myFixture.getLookupElementStrings(), List.of("Inner"));
  }

  @NeedsIndex.Full
  public void testExcludedInstanceInnerClassQualifiedReference() {
    JavaProjectCodeInsightSettings.setExcludedNames(getProject(), myFixture.getTestRootDisposable(), "foo");
    myFixture.addClass("package foo; public class Outer { public class Inner {} }");
    myFixture.addClass("package bar; public class Inner {}");
    configure();
    assertEquals("foo.Outer.Inner", ((JavaPsiClassReferenceElement)myFixture.getLookupElements()[0]).getQualifiedName());
    assertEquals(myFixture.getLookupElementStrings(), List.of("Inner"));
  }

  @NeedsIndex.Full
  public void testStaticMethodOfExcludedClass() {
    JavaProjectCodeInsightSettings.setExcludedNames(getProject(), myFixture.getTestRootDisposable(), "foo");
    myFixture.addClass("package foo; public class Outer { public static void method() {} }");
    configure();
    assertEquals(myFixture.getLookupElementStrings(), List.of("method"));
  }

  @NeedsIndex.Full
  public void testExcludeWildcards() {
    JavaProjectCodeInsightSettings.setExcludedNames(getProject(), myFixture.getTestRootDisposable(), "foo.Outer.*1*");
    myFixture.addClass("""
    package foo;
    public class Outer {
      public static void method1() { }

      public static void method2() { }

      public static void method12() { }

      public static void method42() { }
    }
    """);
    myFixture.configureByText("a.java", "class C {{ foo.Outer.m<caret> }}");
    myFixture.completeBasic();
    assertEquals(myFixture.getLookupElementStrings(), List.of("method2", "method42"));
  }

  @NeedsIndex.SmartMode(reason = "JavaGenerateMemberCompletionContributor.fillCompletionVariants works in smart mode only (for int hashCode lookup preventing autocompletion and additional \n)")
  public void testAtUnderClass() {
    doTest("\n");
  }

  public void testLocalClassName() { doTest(); }

  public void testAssigningFieldForTheFirstTime() { doTest(); }

  public void testClassTypeParameters() {
    configure();
    assertTrue(myFixture.getLookupElementStrings().contains("K"));
  }

  public void testClassTypeParametersGenericBounds() {
    configure();
    assertTrue(myFixture.getLookupElementStrings().contains("K"));
  }

  @NeedsIndex.ForStandardLibrary
  public void testLocalClassTwice() {
    configure();
    assertOrderedEquals(myFixture.getLookupElementStrings(),
                        "Zoooz", "Zooooo", "ZoneRulesException", "ZoneRulesProvider", "ZipOutputStream");
  }

  @NeedsIndex.ForStandardLibrary
  public void testLocalTopLevelConflict() {
    configure();
    assertOrderedEquals(myFixture.getLookupElementStrings(),
                        "Zoooz", "Zooooo", "ZoneRulesException", "ZoneRulesProvider", "ZipOutputStream");
  }

  @NeedsIndex.ForStandardLibrary
  public void testFinalBeforeMethodCall() {
    configure();
    assertStringItems("final", "finalize");
  }

  public void testMethodCallAfterFinally() { doTest(); }

  public void testPrivateInAnonymous() { doTest(); }

  public void testStaticMethodFromOuterClass() {
    configure();
    assertStringItems("foo", "A.foo", "for");
    assertEquals("A.foo", renderElement(myItems[1]).getItemText());
    selectItem(myItems[1]);
    checkResult();
  }

  public void testInstanceMethodFromOuterClass() {
    configure();
    assertStringItems("foo", "A.this.foo", "for");
    assertEquals("A.this.foo", renderElement(myItems[1]).getItemText());
    selectItem(myItems[1]);
    checkResult();
  }

  public void testMethodParenthesesSpaces() {
    getCodeStyleSettings().SPACE_BEFORE_METHOD_CALL_PARENTHESES = true;
    getCodeStyleSettings().SPACE_WITHIN_METHOD_CALL_PARENTHESES = true;
    doTest();
  }

  public void testMethodParenthesesSpacesArgs() {
    getCodeStyleSettings().SPACE_BEFORE_METHOD_CALL_PARENTHESES = true;
    getCodeStyleSettings().SPACE_WITHIN_METHOD_CALL_PARENTHESES = true;
    doTest();
  }

  public void testAtUnderClassNoModifiers() {
    doTest();
  }

  public void testBreakInIfCondition() { doTest(); }

  public void testAccessStaticViaInstance() { doTest(); }

  public void testIfConditionLt() {
    configure();
    myFixture.assertPreferredCompletionItems(0, "getAnnotationsAreaOffset");
  }

  @NeedsIndex.ForStandardLibrary(reason = "On emptly indices 'foo' is the only item, so is not filtered out in  JavaCompletionProcessor.dispreferStaticAfterInstance")
  public void testAccessStaticViaInstanceSecond() {
    configure();
    assertFalse(myFixture.getLookupElementStrings().contains("foo"));
    myFixture.complete(CompletionType.BASIC, 2);
    myFixture.assertPreferredCompletionItems(0, "foo");
    myFixture.type('\n');
    checkResult();
  }

  public void testAccessInstanceFromStaticSecond() {
    configure();
    myFixture.complete(CompletionType.BASIC, 2);
    checkResult();
  }

  public void testBreakLabel() {
    myFixture.configureByText("a.java", """
      class a {{
        foo: while (true) break <caret>
      }}""");
    complete();
    assertEquals(myFixture.getLookupElementStrings(), List.of("foo"));
  }

  public void testContinueLabel() {
    myFixture.configureByText("a.java", """
      class a {{
        foo: while (true) continue <caret>
      }}""");
    complete();
    assertEquals(myFixture.getLookupElementStrings(), List.of("foo"));
  }

  public void testContinueLabelTail() {
    myFixture.configureByText("a.java", """
      class a {{
        foo: while (true) con<caret>
      }}""");
    complete();
    myFixture.checkResult("""
                            class a {{
                              foo: while (true) continue <caret>
                            }}""");
  }

  @NeedsIndex.SmartMode(reason = "For now ConstructorInsertHandler.createOverrideRunnable doesn't work in dumb mode")
  public void testAnonymousProcess() {
    myFixture.addClass("package java.lang; public class Process {}");
    myFixture.addClass("""
        import java.util. *;
        public class Process {
        }
        interface Pred<A> {
          boolean predicate(A elem);
        }
        public class ListUtils {
          public static <A> List<A> filter(List<A> list, Pred<A> pred) { }
        }""");
    configure();
    type('\n');
    NonBlockingReadActionImpl.waitForAsyncTaskCompletion();
    checkResult();
  }

  public void testNoThisInComment() { doAntiTest(); }

  public void testIncNull() {
    configure();
    checkResultByFile(getTestName(false) + ".java");
    assertFalse(myFixture.getLookupElementStrings().contains("null"));
  }

  public void testLastExpressionInFor() { doTest(); }

  public void testOnlyKeywordsInsideSwitch() {
    configureByFile(getTestName(false) + ".java");
    List<String> strings = myFixture.getLookupElementStrings();
    assertNotNull(strings);
    assertSameElements(strings, "case", "default");
  }

  @NeedsIndex.ForStandardLibrary
  public void testBooleanLiterals() {
    doTest("\n");
  }

  public void testDoubleBooleanInParameter() {
    configure();
    assertFirstStringItems("boolean", "byte");
  }

  public void testDoubleConstant() {
    configure();
    assertStringItems("Intf.XFOO", "XFOO");
  }

  public void testNotOnlyKeywordsInsideSwitch() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testChainedCallOnNextLine() {
    configureByFile(getTestName(false) + ".java");
    selectItem(myItems[0]);
    checkResult();
  }

  public void testFinishWithDot() {
    configureByFile(getTestName(false) + ".java");
    type('.');
    checkResult();
  }

  public void testEnclosingThis() { doTest(); }

  @NeedsIndex.Full
  public void testSeamlessConstant() {
    configureByFile(getTestName(false) + ".java");
    selectItem(myItems[0]);
    checkResult();
  }

  public void testDefaultAnnoParam() { doTest(); }

  public void testSpaceAfterLookupString() {
    configureByFile(getTestName(false) + ".java");
    type(' ');
    assertNull(getLookup());
    checkResult();
  }

  @NeedsIndex.ForStandardLibrary
  public void testNoSpaceInParensWithoutParams() {
    getCodeStyleSettings().SPACE_WITHIN_METHOD_CALL_PARENTHESES = true;
    try {
      doTest();
    }
    finally {
      getCodeStyleSettings().SPACE_WITHIN_METHOD_CALL_PARENTHESES = false;
    }
  }

  @NeedsIndex.ForStandardLibrary
  public void testTwoSpacesInParensWithParams() {
    getCodeStyleSettings().SPACE_WITHIN_METHOD_CALL_PARENTHESES = true;
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testQualifierAsPackage() {
    configureByFile(getTestName(false) + ".java");
    selectItem(myItems[0]);
    checkResult();
  }

  @NeedsIndex.ForStandardLibrary
  public void testQualifierAsPackage2() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testQualifierAsPackage3() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testPreselectEditorSelection() {
    configure();
    assertNotSame(getLookup().getCurrentItem(), myFixture.getLookupElements()[0]);
    assertEquals("finalize", getLookup().getCurrentItem().getLookupString());
  }

  public void testNoMethodsInNonStaticImports() {
    configure();
    assertStringItems("*");
  }

  @NeedsIndex.ForStandardLibrary
  public void testMembersInStaticImports() { doTest("\n"); }

  @NeedsIndex.ForStandardLibrary
  public void testPackageNamedVariableBeforeAssignment() {
    doTest();
  }

  public void testInnerEnumConstant() { doTest("\n"); }

  public void testNoExpectedReturnTypeDuplication() {
    configure();
    assertEquals(myFixture.getLookupElementStrings(), List.of("boolean", "byte"));
  }

  public void testNoExpectedVoidReturnTypeDuplication() {
    configure();
    assertEquals(myFixture.getLookupElementStrings(), List.of("void"));
  }

  public void testNoExpectedArrayTypeDuplication() {
    configure();
    assertEquals(myFixture.getLookupElementStrings(), List.of("char"));
  }

  public void testShadowedTypeParameter() {
    configure();
    assertEquals(myFixture.getLookupElementStrings(), List.of("MyParam"));
  }

  @NeedsIndex.ForStandardLibrary
  public void testMethodReturnType() { doTest("\n"); }

  public void testMethodReturnTypeNoSpace() {
    configureByFile(getTestName(false) + ".java");
    selectItem(myItems[0]);
    checkResult();
  }

  public void testEnumWithoutConstants() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testDoWhileMethodCall() {
    doTest();
  }

  public void testSecondTypeParameterExtends() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testGetterWithExistingNonEmptyParameterList() {
    doTest();
  }

  public void testNothingAfterNumericLiteral() { doAntiTest(); }

  public void testNothingAfterTypeParameterQualifier() { doAntiTest(); }

  public void testExcludeVariableBeingDeclared() { doAntiTest(); }

  public void testExcludeVariableBeingDeclared2() { doAntiTest(); }

  public void testSpacesAroundEq() { doTest("="); }

  void _testClassBeforeCast() { doTest("\n"); }

  public void testNoAllClassesOnQualifiedReference() {
    doAntiTest();
  }

  public void testFinishClassNameWithDot() {
    doTest(".");
  }

  @NeedsIndex.ForStandardLibrary
  public void testFinishClassNameWithLParen() {
    doTest("(");
  }

  public void testSelectNoParameterSignature() {
    configureByFile(getTestName(false) + ".java");
    final int parametersCount = ((PsiMethod)getLookup().getCurrentItem().getObject()).getParameterList().getParametersCount();
    assertEquals(0, parametersCount);
    type('\n');
    checkResult();
  }

  @NeedsIndex.ForStandardLibrary
  public void testCompletionInsideClassLiteral() {
    configureByFile(getTestName(false) + ".java");
    type('\n');
    checkResult();
  }

  public void testFieldNegation() { doTest("!"); }

  public void testDefaultInSwitch() { doTest(); }

  public void testBreakInSwitch() { doTest(); }

  public void testSuperInConstructor() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testSuperInConstructorWithParams() {
    doTest();
  }

  public void testSuperInMethod() {
    doTest();
  }

  public void testSecondMethodParameterName() {
    doTest();
  }

  public void testAnnotationAsUsualObject() {
    doTest();
  }

  public void testAnnotationAsUsualObjectFromJavadoc() {
    doTest();
  }

  public void testAnnotationAsUsualObjectInsideClass() {
    doTest();
  }

  public void testAnnotationOnNothingParens() {
    doTest();
  }

  public void testMultiResolveQualifier() {
    doTest();
  }

  public void testSecondMethodParameter() { doTest(); }

  public void testReturnInCase() { doTest(); }

  @NeedsIndex.ForStandardLibrary
  public void testUnboxedConstantsInCase() { doTest(); }

  public void testAnnotationWithoutValueMethod() {
    configureByFile(getTestName(false) + ".java");
    assertStringItems("bar", "foo");
  }

  public void testAddExplicitValueInAnnotation() {
    configureByTestName();
    assertStringItems("bar", "goo");
    selectItem(myItems[0]);
    checkResult();
  }

  public void testUnnecessaryMethodMerging() {
    configureByFile(getTestName(false) + ".java");
    assertStringItems("fofoo", "fofoo");
  }

  @NeedsIndex.ForStandardLibrary
  public void testMethodMergingMinimalTail() { doTest(); }

  public void testAnnotationQualifiedName() {
    doTest();
  }

  @NeedsIndex.SmartMode(reason = "For now ConstructorInsertHandler.createOverrideRunnable doesn't work in dumb mode")
  public void testClassNameAnonymous() {
    doTest("\n");
  }

  @NeedsIndex.SmartMode(reason = "For now ConstructorInsertHandler.createOverrideRunnable doesn't work in dumb mode")
  public void testClassNameWithInner() {
    configure();
    assertStringItems("Zzoo", "Zzoo.Impl");
    type('\n');
    NonBlockingReadActionImpl.waitForAsyncTaskCompletion();
    checkResult();
  }

  public void testClassNameWithInner2() { doTest("\n"); }

  public void testClassNameWithInstanceInner() { doTest("\n"); }

  @NeedsIndex.ForStandardLibrary
  public void testDoubleFalse() {
    configureByFile(getTestName(false) + ".java");
    assertFirstStringItems("false", "fefefef", "float", "finalize");
  }

  public void testSameNamedVariableInNestedClasses() {
    configure();
    myFixture.assertPreferredCompletionItems(0, "ffid", "Beda.this.ffid");
    selectItem(myItems[1]);
    checkResult();
  }

  public void testHonorUnderscoreInPrefix() {
    doTest();
  }

  @NeedsIndex.ForStandardLibrary
  public void testNoSemicolonAfterExistingParenthesesEspeciallyIfItsACast() { doTest(); }

  @NeedsIndex.ForStandardLibrary
  public void testNoSemicolonInForUpdate() { doTest("\n"); }

  public void testReturningTypeVariable() { doTest(); }

  public void testReturningTypeVariable2() { doTest(); }

  public void testReturningTypeVariable3() { doTest(); }

  @NeedsIndex.ForStandardLibrary
  public void testImportInGenericType() {
    configure();
    myFixture.complete(CompletionType.BASIC, 2);
    myFixture.type('\n');
    checkResult();
  }

  public void testCaseTailType() {
    IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_11, this::doTest);
  }

  private void doPrimitiveTypeTest() {
    configure();
    checkResultByFile(getTestName(false) + ".java");
    assertTrue(myFixture.getLookupElementStrings().contains("boolean"));
  }

  public void testFinalInForLoop() {
    configure();
    assertStringItems("final");
  }

  @NeedsIndex.ForStandardLibrary
  public void testFinalInForLoop2() {
    configure();
    myFixture.assertPreferredCompletionItems(1, "finalize", "final");
  }

  public void testOnlyClassesInExtends() {
    configure();
    assertStringItems("Inner");
  }

  public void testNoThisClassInExtends() {
    configure();
    assertStringItems("Fooxxxx2");
  }

  public void testPrimitiveTypesInForLoop() { doPrimitiveTypeTest(); }

  public void testPrimitiveTypesInForLoop2() { doPrimitiveTypeTest(); }

  public void testPrimitiveTypesInForLoop3() { doPrimitiveTypeTest(); }

  public void testPrimitiveTypesInForLoop4() { doPrimitiveTypeTest(); }

  public void testPrimitiveTypesInForLoop5() { doPrimitiveTypeTest(); }

  public void testPrimitiveTypesInForLoop6() { doPrimitiveTypeTest(); }

  public void testPrimitiveTypesInForLoopSpace() {
    configure();
    myFixture.type(' ');
    checkResultByFile(getTestName(false) + "_after.java");
  }

  @NeedsIndex.SmartMode(reason = "Ordering requires smart mode")
  public void testSuggestInaccessibleOnSecondInvocation() {
    configure();
    assertStringItems("_bar", "_goo");
    complete();
    assertStringItems("_bar", "_goo", "_foo");
    getLookup().setCurrentItem(getLookup().getItems().get(2));
    selectItem(getLookup().getItems().get(2), Lookup.NORMAL_SELECT_CHAR);
    checkResult();
  }

