/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jetbrains.plugins.gradle.integrations.maven;

import com.intellij.CommonBundle;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.impl.NotificationsConfigurationImpl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationManager;
import com.intellij.openapi.externalSystem.service.notification.NotificationCategory;
import com.intellij.openapi.externalSystem.service.notification.NotificationData;
import com.intellij.openapi.externalSystem.service.notification.NotificationSource;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.maven.indices.MavenIndex;
import org.jetbrains.idea.maven.indices.MavenIndicesManager;
import org.jetbrains.idea.maven.model.MavenRemoteRepository;
import org.jetbrains.plugins.gradle.util.GradleBundle;
import org.jetbrains.plugins.gradle.util.GradleConstants;

import javax.swing.event.HyperlinkEvent;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static org.jetbrains.idea.maven.indices.MavenIndicesManager.IndexUpdatingState.IDLE;

/**
 * @author Vladislav.Soroka
 * @since 10/28/13
 */
public class MavenRepositoriesHolder extends AbstractProjectComponent implements Disposable {
  private static final String UNINDEXED_MAVEN_REPOSITORIES_NOTIFICATION_GROUP = "Unindexed maven repositories gradle detection";
  private static final Key<String> NOTIFICATION_KEY = Key.create(UNINDEXED_MAVEN_REPOSITORIES_NOTIFICATION_GROUP);

  private volatile Set<MavenRemoteRepository> myRemoteRepositories;
  private volatile Set<String> myNotIndexedUrls;

  public MavenRepositoriesHolder(Project project) {
    super(project);
    myRemoteRepositories = Collections.emptySet();
    myNotIndexedUrls = Collections.emptySet();
  }

  public void updateNotIndexedUrls(List<String> repositories) {
    if (repositories.isEmpty()) {
      myNotIndexedUrls = Collections.emptySet();
      ExternalSystemNotificationManager.getInstance(myProject).clearNotifications(
        UNINDEXED_MAVEN_REPOSITORIES_NOTIFICATION_GROUP, NotificationSource.PROJECT_SYNC, GradleConstants.SYSTEM_ID);
    }
    else {
      myNotIndexedUrls = ContainerUtil.newHashSet(repositories);
    }
  }

  public void checkNotIndexedRepositories() {
    ExternalSystemNotificationManager notificationManager = ExternalSystemNotificationManager.getInstance(myProject);
    if (ContainerUtil.isEmpty(myNotIndexedUrls)) {
      return;
    }

    if (notificationManager.isNotificationActive(NOTIFICATION_KEY)) return;

    final MavenIndicesManager indicesManager = MavenIndicesManager.getInstance();
    for (MavenIndex index : indicesManager.getIndices()) {
      if (indicesManager.getUpdatingState(index) != IDLE) return;
    }

    final NotificationData notificationData = new NotificationData(
      GradleBundle.message("gradle.integrations.maven.notification.not_updated_repository.title"),
      "\n<br>" + GradleBundle.message("gradle.integrations.maven.notification.not_updated_repository.text"),
      NotificationCategory.INFO,
      NotificationSource.PROJECT_SYNC);
    notificationData.setBalloonNotification(true);
    notificationData.setBalloonGroup(UNINDEXED_MAVEN_REPOSITORIES_NOTIFICATION_GROUP);
    notificationData.setListener("#update", new NotificationListener.Adapter() {
      @Override
      protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) {
        List<MavenIndex> notIndexed = indicesManager.getIndices().stream()
          .filter(index -> isNotIndexed(index.getRepositoryPathOrUrl()))
          .collect(Collectors.toList());
        indicesManager.scheduleUpdate(myProject, notIndexed).done(aVoid -> {
          if (myNotIndexedUrls.isEmpty()) return;
          for (MavenIndex index : notIndexed) {
            if (index.getUpdateTimestamp() != -1 || index.getFailureMessage() != null) {
              myNotIndexedUrls.remove(index.getRepositoryPathOrUrl());
            }
          }
        });
        notification.hideBalloon();
      }
    });

    notificationData.setListener("#disable", new NotificationListener.Adapter() {
      @Override
      protected void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e) {
        final int result =
          Messages.showYesNoDialog(myProject,
                                   "Notification will be disabled for all projects.\n\n" +
                                   "Settings | Appearance & Behavior | Notifications | " +
                                   UNINDEXED_MAVEN_REPOSITORIES_NOTIFICATION_GROUP +
                                   "\ncan be used to configure the notification.",
                                   "Unindexed Maven Repositories Gradle Detection",
                                   "Disable Notification", CommonBundle.getCancelButtonText(),
                                   Messages.getWarningIcon());
        if (result == Messages.YES) {
          NotificationsConfigurationImpl.getInstanceImpl().changeSettings(UNINDEXED_MAVEN_REPOSITORIES_NOTIFICATION_GROUP,
                                                                          NotificationDisplayType.NONE, false, false);

          notification.hideBalloon();
        }
      }
    });

    notificationManager.showNotification(GradleConstants.SYSTEM_ID, notificationData, NOTIFICATION_KEY);
  }

  @Override
  public void dispose() {
    ExternalSystemNotificationManager.getInstance(myProject).clearNotifications(
      UNINDEXED_MAVEN_REPOSITORIES_NOTIFICATION_GROUP, NotificationSource.PROJECT_SYNC, GradleConstants.SYSTEM_ID);
  }


  public static MavenRepositoriesHolder getInstance(Project p) {
    return p.getComponent(MavenRepositoriesHolder.class);
  }

  public void update(Set<MavenRemoteRepository> remoteRepositories) {
    myRemoteRepositories = ContainerUtil.newHashSet(remoteRepositories);
  }

  public Set<MavenRemoteRepository> getRemoteRepositories() {
    return Collections.unmodifiableSet(myRemoteRepositories);
  }

  public boolean contains(String url) {
    final String pathOrUrl = MavenIndex.normalizePathOrUrl(url);
    for (MavenRemoteRepository repository : myRemoteRepositories) {
      if (MavenIndex.normalizePathOrUrl(repository.getUrl()).equals(pathOrUrl)) return true;
    }
    return false;
  }

  public boolean isNotIndexed(String url) {
    final String pathOrUrl = MavenIndex.normalizePathOrUrl(url);
    for (String repository : myNotIndexedUrls) {
      if (MavenIndex.normalizePathOrUrl(repository).equals(pathOrUrl)) return true;
    }
    return false;
  }
}

@@ -952,24 +952,84 @@
 Disposable;%0A
+import com.intellij.openapi.application.ApplicationManager;%0A
 import com.i
@@ -2930,24 +2930,95 @@
 sitories) %7B%0A
+    if (ApplicationManager.getApplication().isUnitTestMode()) return;%0A%0A
     if (repo
@@ -3424,24 +3424,95 @@
 itories() %7B%0A
+    if (ApplicationManager.getApplication().isUnitTestMode()) return;%0A%0A
     External
@@ -6642,24 +6642,91 @@
 dispose() %7B%0A
+    if (!ApplicationManager.getApplication().isUnitTestMode()) %7B%0A  
     External
@@ -6790,24 +6790,26 @@
 ifications(%0A
+  
       UNINDE
@@ -6914,18 +6914,23 @@
 _ID);%0A  
+  %7D%0A  
 %7D
-%0A
 %0A%0A  publ

fix project leakage in test