// 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.actions;

import com.intellij.JavaTestUtil;
import com.intellij.compiler.CompilerManagerImpl;
import com.intellij.java.testFramework.fixtures.MultiModuleProjectDescriptor;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.DumbServiceImpl;
import com.intellij.openapi.roots.CompilerProjectExtension;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.refactoring.LightMultiFileTestCase;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.NeedsIndex;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.ServiceContainerUtil;
import com.intellij.util.WaitFor;
import kotlinx.coroutines.CoroutineScopeKt;
import kotlinx.coroutines.JobKt;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;

@NeedsIndex.SmartMode(reason = "The module descriptors generator is not supported in the dumb mode")
public class Java9GenerateModuleDescriptorsActionTest extends LightMultiFileTestCase {
  private MultiModuleProjectDescriptor myDescriptor;
  private final AtomicInteger myLock = new AtomicInteger(0);

  @Override
  protected @NotNull LightProjectDescriptor getProjectDescriptor() {
    return myDescriptor == null
           ? myDescriptor = new MultiModuleProjectDescriptor(Paths.get(getTestDataPath() + "/" + getTestName(true)), null, projectModel -> {
      // replace services
      CompilerProjectExtension.getInstance(projectModel.getProject()).setCompilerOutputUrl(projectModel.getOutputUrl());
      ServiceContainerUtil.replaceService(projectModel.getProject(), DumbService.class,
                                          new DumbServiceImpl(projectModel.getProject(), CoroutineScopeKt.CoroutineScope(JobKt.Job(null))) {
                                            @Override
                                            public void smartInvokeLater(@NotNull Runnable runnable) {
                                              myLock.incrementAndGet();
                                              ReadAction.compute(() -> {
                                                runnable.run();
                                                return myLock.decrementAndGet();
                                              });
                                            }
                                          }, projectModel.getProject());
      ServiceContainerUtil.replaceService(projectModel.getProject(), CompilerManager.class, new CompilerManagerImpl(projectModel.getProject()) {
        @Override
        public boolean isUpToDate(@NotNull CompileScope scope) {
          return true;
        }
      }, projectModel.getProject());
    }) : myDescriptor;
  }

  @Override
  protected String getTestDataPath() {
    return JavaTestUtil.getJavaTestDataPath() + "/actions/generateModuleDescriptors";
  }

  public void testSingleModule() throws IOException {
    performReformatAction();
  }

  public void testSingleModuleWithDependency() throws IOException {
    performReformatAction();
  }

  public void testDependentModules() throws IOException {
    performReformatAction();
  }

  public void testTransitiveDependency() throws IOException {
    performReformatAction();
  }

  public void testCommonModuleWithTransitiveDependencies() throws IOException {
    performReformatAction();
  }

  public void testResources() throws IOException {
    performReformatAction();
  }

  public void testAutoGenerated() throws IOException {
    performReformatAction();
  }

  protected void performReformatAction() throws IOException {
    // INIT
    final AnAction action = ActionManager.getInstance().getAction("GenerateModuleDescriptors");
    final AnActionEvent event = AnActionEvent.createFromAnAction(action, null, "", dataId -> {
      if (CommonDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) return Collections.emptyList();
      if (CommonDataKeys.PROJECT.is(dataId)) return getProject();
      return null;
    });

    // EXEC
    action.actionPerformed(event);

    // CHECK
    final MultiModuleProjectDescriptor descriptor = (MultiModuleProjectDescriptor)getProjectDescriptor();

    //noinspection ResultOfObjectAllocationIgnored
    new WaitFor() {
      @Override
      protected boolean condition() {
        return myLock.get() == 0;
      }
    };
    PlatformTestUtil.assertDirectoriesEqual(LocalFileSystem.getInstance().findFileByNioFile(descriptor.getAfterPath()),
                                            LocalFileSystem.getInstance().findFileByNioFile(descriptor.getProjectPath()));
  }
}
