package com.jetbrains.edu.kotlin;

import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationsConfiguration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.util.text.VersionComparatorUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtilsKt;
import org.jetbrains.kotlin.idea.configuration.KotlinJavaModuleConfigurator;
import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator;
import org.jetbrains.kotlin.idea.configuration.KotlinWithLibraryConfigurator;
import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;

public class EduKotlinLibConfigurator {
  private static final Logger LOG = Logger.getInstance(EduKotlinLibConfigurator.class);

  public static void configureLib(@NotNull Project project) {
    if (tryConfigureSilently(project)) {
      return;
    }
    if (!tryOldAPI(project)) {
      LOG.warn("Failed to configure kotlin lib for edu project");
    }
  }

    //bad way to configure kotlin lib
    private static boolean tryOldAPI(Project project) {

        String settingName = "Configure Kotlin: info notification";
        NotificationsConfiguration.getNotificationsConfiguration().changeSettings(settingName,
                NotificationDisplayType.NONE, true, false);
        KotlinProjectConfigurator configuratorByName = ConfigureKotlinInProjectUtilsKt.getConfiguratorByName("java");
        if (configuratorByName == null) {
            LOG.info("Failed to find configurator");
            return false;
        }
        Class<?> confClass = configuratorByName.getClass();
        while (confClass != KotlinWithLibraryConfigurator.class) {
            confClass = confClass.getSuperclass();
        }
        String lib = FileUIUtils.createRelativePath(project, project.getBaseDir(), "lib");
        //collector arg was added in Kotlin plugin 1.0.1

        IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.jetbrains.kotlin"));
        if (plugin == null) {
            return false;
        }

        if (VersionComparatorUtil.compare(plugin.getVersion(), "1.0.1") > 0) {
            // TODO: uncomment according Kotlin plugin release
//            configureWithCollector(project, confClass, configuratorByName, lib);
        } else {
            if (!configureWithoutCollector(project, confClass, configuratorByName, lib)) {
                configuratorByName.configure(project, Collections.emptyList());
            }
        }
        NotificationsConfiguration.getNotificationsConfiguration().changeSettings(settingName,
                NotificationDisplayType.STICKY_BALLOON, true, false);
        return true;
    }

//    private boolean configureWithCollector(Project project, Class<?> confClass, KotlinProjectConfigurator configurator, String lib) {
//        try {
//            Method method = confClass.getDeclaredMethod("configureModuleWithLibrary", Module.class, String.class, String.class, NotificationMessageCollector.class);
//            method.setAccessible(true);
//            NotificationMessageCollector collector = NotificationMessageCollectorKt.createConfigureKotlinNotificationCollector(project);
//            for (Module module : ModuleManager.getInstance(project).getModules()) {
//                method.invoke(configurator, module, lib, null, collector);
//            }
//            return true;
//        } catch (Exception e) {
//            return false;
//        }
//    }

    private static boolean configureWithoutCollector(Project project, Class<?> confClass, KotlinProjectConfigurator configurator, String lib) {
        try {
            Method method = confClass.getDeclaredMethod("configureModuleWithLibrary", Module.class, String.class, String.class);
            method.setAccessible(true);
            for (Module module : ModuleManager.getInstance(project).getModules()) {
                method.invoke(configurator, module, lib, null);
            }
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    //should work since Kotlin 1.0.2
    private static boolean tryConfigureSilently(Project project) {
        //TODO: uncomment this when nobody use kotlin plugin versions < 1.0.2
//        KotlinJavaModuleConfigurator.getInstance().configureSilently(project);

        Class<KotlinJavaModuleConfigurator> configuratorClass = KotlinJavaModuleConfigurator.class;
        try {
            Method instanceMethod = configuratorClass.getDeclaredMethod("getInstance");
            KotlinJavaModuleConfigurator configurator = (KotlinJavaModuleConfigurator) instanceMethod.invoke(null);
            Class<?> confClass = configurator.getClass();
            while (confClass != KotlinWithLibraryConfigurator.class) {
                confClass = confClass.getSuperclass();
            }
            Method configureSilently = confClass.getDeclaredMethod("configureSilently", Project.class);
            configureSilently.invoke(configurator, project);

            return true;
        } catch (NoSuchMethodException e) {
            return false;
        } catch (InvocationTargetException e) {
            return false;
        } catch (IllegalAccessException e) {
            return false;
        }
    }
}

@@ -628,268 +628,9 @@
 ion.
-ConfigureKotlinInProjectUtilsKt;%0Aimport org.jetbrains.kotlin.idea.configuration.KotlinJavaModuleConfigurator;%0Aimport org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator;%0Aimport org.jetbrains.kotlin.idea.configuration.KotlinWithLibraryConfigurator
+*
 ;%0Aim
@@ -2332,73 +2332,12 @@
     
-// TODO: uncomment according Kotlin plugin release%0A//            
+if (
 conf
@@ -2399,17 +2399,60 @@
 me, lib)
-;
+) %7B%0A              return true;%0A            %7D
 %0A       
@@ -2849,18 +2849,16 @@
 %0A    %7D%0A%0A
-//
     priv
@@ -2861,16 +2861,23 @@
 private 
+static 
 boolean 
@@ -2986,26 +2986,24 @@
 ring lib) %7B%0A
-//
         try 
@@ -3004,18 +3004,16 @@
   try %7B%0A
-//
         
@@ -3169,18 +3169,16 @@
 class);%0A
-//
         
@@ -3209,18 +3209,16 @@
 (true);%0A
-//
         
@@ -3346,18 +3346,16 @@
 oject);%0A
-//
         
@@ -3426,26 +3426,24 @@
 odules()) %7B%0A
-//
             
@@ -3505,18 +3505,16 @@
 ector);%0A
-//
         
@@ -3515,26 +3515,24 @@
           %7D%0A
-//
             
@@ -3544,18 +3544,16 @@
 n true;%0A
-//
         
@@ -3576,18 +3576,16 @@
 on e) %7B%0A
-//
         
@@ -3602,18 +3602,16 @@
  false;%0A
-//
         
@@ -3612,18 +3612,16 @@
       %7D%0A
-//
     %7D%0A%0A 

fix kotlin silent adding