package org.jabref.logic.importer;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import org.jabref.logic.bibtex.FieldContentFormatterPreferences;
import org.jabref.logic.importer.fetcher.ACMPortalFetcher;
import org.jabref.logic.importer.fetcher.AbstractIsbnFetcher;
import org.jabref.logic.importer.fetcher.GrobidCitationFetcher;
import org.jabref.logic.importer.fetcher.IsbnViaEbookDeFetcher;
import org.jabref.logic.importer.fetcher.IsbnViaOttoBibFetcher;
import org.jabref.logic.importer.fetcher.MrDLibFetcher;

import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class WebFetchersTest {

    private ImportFormatPreferences importFormatPreferences;
    private final ClassGraph classGraph = new ClassGraph().enableAllInfo().whitelistPackages("org.jabref");

    @BeforeEach
    void setUp() throws Exception {
        importFormatPreferences = mock(ImportFormatPreferences.class);
        FieldContentFormatterPreferences fieldContentFormatterPreferences = mock(FieldContentFormatterPreferences.class);
        when(importFormatPreferences.getFieldContentFormatterPreferences()).thenReturn(fieldContentFormatterPreferences);
    }

    @Test
    void getIdBasedFetchersReturnsAllFetcherDerivingFromIdBasedFetcher() throws Exception {
        Set<IdBasedFetcher> idFetchers = WebFetchers.getIdBasedFetchers(importFormatPreferences);

        try (ScanResult scanResult = classGraph.scan()) {

            ClassInfoList controlClasses = scanResult.getClassesImplementing(IdBasedFetcher.class.getCanonicalName());
            Set<Class<?>> expected = new HashSet<>(controlClasses.loadClasses());

            expected.remove(AbstractIsbnFetcher.class);
            expected.remove(IdBasedParserFetcher.class);

            // Remove special ISBN fetcher since we don't want to expose them to the user
            expected.remove(IsbnViaEbookDeFetcher.class);
            expected.remove(IsbnViaOttoBibFetcher.class);

            // Remove ACM, because it doesn't work currently
            expected.remove(ACMPortalFetcher.class);

            assertEquals(expected, getClasses(idFetchers));
        }
    }

    @Test
    void getEntryBasedFetchersReturnsAllFetcherDerivingFromEntryBasedFetcher() throws Exception {
        Set<EntryBasedFetcher> idFetchers = WebFetchers.getEntryBasedFetchers(importFormatPreferences);

        try (ScanResult scanResult = classGraph.scan()) {
            ClassInfoList controlClasses = scanResult.getClassesImplementing(EntryBasedFetcher.class.getCanonicalName());
            Set<Class<?>> expected = new HashSet<>(controlClasses.loadClasses());

            expected.remove(EntryBasedParserFetcher.class);
            expected.remove(MrDLibFetcher.class);
            assertEquals(expected, getClasses(idFetchers));
        }
    }

    @Test
    void getSearchBasedFetchersReturnsAllFetcherDerivingFromSearchBasedFetcher() throws Exception {
        Set<SearchBasedFetcher> searchBasedFetchers = WebFetchers.getSearchBasedFetchers(importFormatPreferences);
        try (ScanResult scanResult = classGraph.scan()) {
            ClassInfoList controlClasses = scanResult.getClassesImplementing(SearchBasedFetcher.class.getCanonicalName());
            Set<Class<?>> expected = new HashSet<>(controlClasses.loadClasses());

            // Remove interfaces
            expected.remove(SearchBasedParserFetcher.class);

            // Remove ACM, because it doesn't work currently
            expected.remove(ACMPortalFetcher.class);

            expected.remove(PagedSearchBasedParserFetcher.class);
            expected.remove(PagedSearchBasedFetcher.class);

            // Remove GROBID, because we don't want to show this to the user
            expected.remove(GrobidCitationFetcher.class);

            assertEquals(expected, getClasses(searchBasedFetchers));
        }
    }

    @Test
    void getFullTextFetchersReturnsAllFetcherDerivingFromFullTextFetcher() throws Exception {
        Set<FulltextFetcher> fullTextFetchers = WebFetchers.getFullTextFetchers(importFormatPreferences);

        try (ScanResult scanResult = classGraph.scan()) {
            ClassInfoList controlClasses = scanResult.getClassesImplementing(FulltextFetcher.class.getCanonicalName());
            Set<Class<?>> expected = new HashSet<>(controlClasses.loadClasses());
            assertEquals(expected, getClasses(fullTextFetchers));
        }
    }

    @Test
    void getIdFetchersReturnsAllFetcherDerivingFromIdFetcher() throws Exception {
        Set<IdFetcher<?>> idFetchers = WebFetchers.getIdFetchers(importFormatPreferences);

        try (ScanResult scanResult = classGraph.scan()) {
            ClassInfoList controlClasses = scanResult.getClassesImplementing(IdFetcher.class.getCanonicalName());
            Set<Class<?>> expected = new HashSet<>(controlClasses.loadClasses());

            expected.remove(IdParserFetcher.class);
            assertEquals(expected, getClasses(idFetchers));
        }
    }

    private Set<? extends Class<?>> getClasses(Collection<?> objects) {
        return objects.stream().map(Object::getClass).collect(Collectors.toSet());
    }
}

@@ -321,32 +321,88 @@
 actIsbnFetcher;%0A
+import org.jabref.logic.importer.fetcher.GoogleScholar;%0A
 import org.jabre
@@ -569,32 +569,87 @@
 OttoBibFetcher;%0A
+import org.jabref.logic.importer.fetcher.JstorFetcher;%0A
 import org.jabre
@@ -2397,35 +2397,45 @@
       // Remove 
-ACM
+the following
 , because it doe
@@ -2420,39 +2420,39 @@
 lowing, because 
-i
 t
+hey
  do
-es
 n't work current
@@ -2440,33 +2440,37 @@
  don't work 
-curr
+at the mom
 ent
-ly
 %0A           
@@ -2502,32 +2502,131 @@
 lFetcher.class);
+%0A            expected.remove(JstorFetcher.class);%0A            expected.remove(GoogleScholar.class);
 %0A%0A            as
@@ -3957,19 +3957,29 @@
  Remove 
-ACM
+the following
 , becaus
@@ -3984,15 +3984,15 @@
 use 
-i
 t
+hey
  do
-es
 n't 
@@ -4000,17 +4000,11 @@
 ork 
-currently
+atm
 %0A   
@@ -4044,32 +4044,131 @@
 lFetcher.class);
+%0A            expected.remove(JstorFetcher.class);%0A            expected.remove(GoogleScholar.class);
 %0A%0A            ex
@@ -4969,24 +4969,243 @@
 Classes());%0A
+%0A            // Remove the following, because they don't work atm%0A            expected.remove(ACMPortalFetcher.class);%0A            expected.remove(JstorFetcher.class);%0A            expected.remove(GoogleScholar.class);%0A%0A
             
@@ -5758,32 +5758,212 @@
 Fetcher.class);%0A
+            // Remove the following, because they don't work at the moment%0A            expected.remove(ACMPortalFetcher.class);%0A            expected.remove(GoogleScholar.class);%0A%0A%0A
             asse

fix fetcher architecture test