package org.jabref.logic.importer.fileformat;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.jabref.logic.util.StandardFileType;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.Month;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.field.UnknownField;
import org.jabref.model.entry.types.StandardEntryType;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class IsiImporterTest {

    private static final String FILE_ENDING = ".isi";
    private final IsiImporter importer = new IsiImporter();

    private static Stream<String> fileNames() throws IOException {
        Predicate<String> fileName = name -> name.startsWith("IsiImporterTest")
                && !name.contains("Empty")
                && name.endsWith(FILE_ENDING);
        return ImporterTestEngine.getTestFiles(fileName).stream();
    }

    private static Stream<String> invalidFileNames() throws IOException {
        Predicate<String> fileName = name -> !name.startsWith("IsiImporterTest");
        return ImporterTestEngine.getTestFiles(fileName).stream();
    }

    @Test
    public void parseMonthException() {
        IsiImporter.parseMonth("20l06 06-07");
    }

    @Test
    public void getFormatName() {
        assertEquals("ISI", importer.getName());
    }

    @Test
    public void getCLIId() {
        assertEquals("isi", importer.getId());
    }

    @Test
    public void sGetExtensions() {
        assertEquals(StandardFileType.ISI, importer.getFileType());
    }

    @Test
    public void getDescription() {
        assertEquals("Importer for the ISI Web of Science, INSPEC and Medline format.",
                importer.getDescription());
    }

    @ParameterizedTest
    @MethodSource("fileNames")
    public void isRecognizedFormatAccepted(String fileName) throws IOException {
        ImporterTestEngine.testIsRecognizedFormat(importer, fileName);
    }

    @ParameterizedTest
    @MethodSource("invalidFileNames")
    public void isRecognizedFormatRejected(String fileName) throws IOException {
        ImporterTestEngine.testIsNotRecognizedFormat(importer, fileName);
    }

    @Test
    public void processSubSup() {
        HashMap<Field, String> subs = new HashMap<>();

        subs.put(StandardField.TITLE, "/sub 3/");
        IsiImporter.processSubSup(subs);
        assertEquals("$_3$", subs.get(StandardField.TITLE));

        subs.put(StandardField.TITLE, "/sub   3   /");
        IsiImporter.processSubSup(subs);
        assertEquals("$_3$", subs.get(StandardField.TITLE));

        subs.put(StandardField.TITLE, "/sub 31/");
        IsiImporter.processSubSup(subs);
        assertEquals("$_{31}$", subs.get(StandardField.TITLE));

        subs.put(StandardField.ABSTRACT, "/sub 3/");
        IsiImporter.processSubSup(subs);
        assertEquals("$_3$", subs.get(StandardField.ABSTRACT));

        subs.put(StandardField.COMMENT, "/sub 31/");
        IsiImporter.processSubSup(subs);
        assertEquals("$_{31}$", subs.get(StandardField.COMMENT));

        subs.put(StandardField.TITLE, "/sup 3/");
        IsiImporter.processSubSup(subs);
        assertEquals("$^3$", subs.get(StandardField.TITLE));

        subs.put(StandardField.TITLE, "/sup 31/");
        IsiImporter.processSubSup(subs);
        assertEquals("$^{31}$", subs.get(StandardField.TITLE));

        subs.put(StandardField.ABSTRACT, "/sup 3/");
        IsiImporter.processSubSup(subs);
        assertEquals("$^3$", subs.get(StandardField.ABSTRACT));

        subs.put(StandardField.COMMENT, "/sup 31/");
        IsiImporter.processSubSup(subs);
        assertEquals("$^{31}$", subs.get(StandardField.COMMENT));

        subs.put(StandardField.TITLE, "/sub $Hello/");
        IsiImporter.processSubSup(subs);
        assertEquals("$_{\\$Hello}$", subs.get(StandardField.TITLE));
    }

    @Test
    public void importEntries1() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IsiImporterTest1.isi").toURI());
        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();
        BibEntry entry = entries.getFirst();

        assertEquals(1, entries.size());
        assertEquals(Optional.of("Optical properties of MgO doped LiNbO$_3$ single crystals"),
                entry.getField(StandardField.TITLE));
        assertEquals(
                Optional.of(
                        "James Brown and James Marc Brown and Brown, J. M. and Brown, J. and Brown, J. M. and Brown, J."),
                entry.getField(StandardField.AUTHOR));
        assertEquals(StandardEntryType.Article, entry.getType());
        assertEquals(Optional.of("Optical Materials"), entry.getField(StandardField.JOURNAL));
        assertEquals(Optional.of("2006"), entry.getField(StandardField.YEAR));
        assertEquals(Optional.of("28"), entry.getField(StandardField.VOLUME));
        assertEquals(Optional.of("5"), entry.getField(StandardField.NUMBER));
        assertEquals(Optional.of("467--72"), entry.getField(StandardField.PAGES));
    }

    @Test
    public void importEntries2() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IsiImporterTest2.isi").toURI());
        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();
        BibEntry entry = entries.getFirst();

        assertEquals(3, entries.size());
        assertEquals(Optional.of("Optical properties of MgO doped LiNbO$_3$ single crystals"),
                entry.getField(StandardField.TITLE));
        assertEquals(StandardEntryType.Misc, entry.getType());
        assertEquals(Optional.of("Optical Materials"), entry.getField(StandardField.JOURNAL));
        assertEquals(Optional.of("2006"), entry.getField(StandardField.YEAR));
        assertEquals(Optional.of("28"), entry.getField(StandardField.VOLUME));
        assertEquals(Optional.of("5"), entry.getField(StandardField.NUMBER));
        assertEquals(Optional.of("467-72"), entry.getField(StandardField.PAGES));
    }

    @Test
    public void importEntriesINSPEC() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IsiImporterTestInspec.isi").toURI());
        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();

        BibEntry first = entries.getFirst();
        BibEntry second = entries.get(1);

        if (first.getField(StandardField.TITLE).equals(
                Optional.of("Optical and photoelectric spectroscopy of photorefractive Sn$_2$P$_2$S$_6$ crystals"))) {
            BibEntry tmp = first;
            first = second;
            second = tmp;
        }

        assertEquals(2, entries.size());
        assertEquals(
                Optional.of(
                        "Second harmonic generation of continuous wave ultraviolet light and production of beta -BaB$_2$O$_4$ optical waveguides"),
                first.getField(StandardField.TITLE));
        assertEquals(StandardEntryType.Article, first.getType());

        assertEquals(Optional.of("Degl'Innocenti, R. and Guarino, A. and Poberaj, G. and Gunter, P."),
                first.getField(StandardField.AUTHOR));
        assertEquals(Optional.of("Applied Physics Letters"), first.getField(StandardField.JOURNAL));
        assertEquals(Optional.of("2006"), first.getField(StandardField.YEAR));
        assertEquals(Optional.of(Month.JULY), first.getMonth());
        assertEquals(Optional.of("89"), first.getField(StandardField.VOLUME));
        assertEquals(Optional.of("4"), first.getField(StandardField.NUMBER));
        assertEquals(Optional.of("Lorem ipsum abstract"), first.getField(StandardField.ABSTRACT));
        assertEquals(Optional.of("Aip"), first.getField(StandardField.PUBLISHER));
        assertEquals(
                Optional.of("Optical and photoelectric spectroscopy of photorefractive Sn$_2$P$_2$S$_6$ crystals"),
                second.getField(StandardField.TITLE));
        assertEquals(StandardEntryType.Article, second.getType());
    }

    @Test
    public void importEntriesWOS() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IsiImporterTestWOS.isi").toURI());
        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();

        BibEntry first = entries.getFirst();
        BibEntry second = entries.get(1);

        assertEquals(2, entries.size());

        assertEquals(Optional.of("Optical and photoelectric spectroscopy of photorefractive Sn2P2S6 crystals"),
                first.getField(StandardField.TITLE));
        assertEquals(Optional.of("Optical waveguides in Sn2P2S6 by low fluence MeV He+ ion implantation"),
                second.getField(StandardField.TITLE));

        assertEquals(Optional.of("Journal of Physics-Condensed Matter"), first.getField(StandardField.JOURNAL));
    }

    @Test
    public void isiAuthorsConvert() {
        assertEquals(
                "James Brown and James Marc Brown and Brown, J. M. and Brown, J. and Brown, J. M. and Brown, J.",
                IsiImporter.isiAuthorsConvert(
                        "James Brown and James Marc Brown and Brown, J.M. and Brown, J. and Brown, J.M. and Brown, J."));

        assertEquals(
                "Joffe, Hadine and Hall, Janet E. and Gruber, Staci and Sarmiento, Ingrid A. and Cohen, Lee S. and Yurgelun-Todd, Deborah and Martin, Kathryn A.",
                IsiImporter.isiAuthorsConvert(
                        "Joffe, Hadine; Hall, Janet E; Gruber, Staci; Sarmiento, Ingrid A; Cohen, Lee S; Yurgelun-Todd, Deborah; Martin, Kathryn A"));
    }

    @Test
    public void monthConvert() {
        assertEquals("#jun#", IsiImporter.parseMonth("06"));
        assertEquals("#jun#", IsiImporter.parseMonth("JUN"));
        assertEquals("#jun#", IsiImporter.parseMonth("jUn"));
        assertEquals("#may#", IsiImporter.parseMonth("MAY-JUN"));
        assertEquals("#jun#", IsiImporter.parseMonth("2006 06"));
        assertEquals("#jun#", IsiImporter.parseMonth("2006 06-07"));
        assertEquals("#jul#", IsiImporter.parseMonth("2006 07 03"));
        assertEquals("#may#", IsiImporter.parseMonth("2006 May-Jun"));
    }

    @Test
    public void isiAuthorConvert() {
        assertEquals("James Brown", IsiImporter.isiAuthorConvert("James Brown"));
        assertEquals("James Marc Brown", IsiImporter.isiAuthorConvert("James Marc Brown"));
        assertEquals("Brown, J. M.", IsiImporter.isiAuthorConvert("Brown, J.M."));
        assertEquals("Brown, J.", IsiImporter.isiAuthorConvert("Brown, J."));
        assertEquals("Brown, J. M.", IsiImporter.isiAuthorConvert("Brown, JM"));
        assertEquals("Brown, J.", IsiImporter.isiAuthorConvert("Brown, J"));
        assertEquals("Brown, James", IsiImporter.isiAuthorConvert("Brown, James"));
        assertEquals("Hall, Janet E.", IsiImporter.isiAuthorConvert("Hall, Janet E"));
        assertEquals("", IsiImporter.isiAuthorConvert(""));
    }

    @Test
    public void importIEEEExport() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IEEEImport1.txt").toURI());
        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();
        BibEntry entry = entries.getFirst();

        assertEquals(1, entries.size());
        assertEquals(StandardEntryType.Article, entry.getType());
        assertEquals(Optional.of("Geoscience and Remote Sensing Letters, IEEE"), entry.getField(StandardField.JOURNAL));
        assertEquals(Optional.of("Improving Urban Road Extraction in High-Resolution "
                        + "Images Exploiting Directional Filtering, Perceptual " + "Grouping, and Simple Topological Concepts"),
                entry.getField(StandardField.TITLE));
        assertEquals(Optional.of("4"), entry.getField(StandardField.VOLUME));
        assertEquals(Optional.of("3"), entry.getField(StandardField.NUMBER));
        assertEquals(Optional.of("1545-598X"), entry.getField(new UnknownField("SN")));
        assertEquals(Optional.of("387--391"), entry.getField(StandardField.PAGES));
        assertEquals(Optional.of("Gamba, P. and Dell'Acqua, F. and Lisini, G."), entry.getField(StandardField.AUTHOR));
        assertEquals(Optional.of("2006"), entry.getField(StandardField.YEAR));
        assertEquals(Optional.of("Perceptual grouping, street extraction, urban remote sensing"),
                entry.getField(StandardField.KEYWORDS));
        assertEquals(Optional.of("Lorem ipsum abstract"), entry.getField(StandardField.ABSTRACT));
    }

    @Test
    public void iEEEImport() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IEEEImport1.txt").toURI());
        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();
        BibEntry entry = entries.getFirst();

        assertEquals(1, entries.size());
        assertEquals(StandardEntryType.Article, entry.getType());
        assertEquals(Optional.of("Geoscience and Remote Sensing Letters, IEEE"), entry.getField(StandardField.JOURNAL));
        assertEquals(
                Optional.of(
                        "Improving Urban Road Extraction in High-Resolution Images Exploiting Directional Filtering, Perceptual Grouping, and Simple Topological Concepts"),
                entry.getField(StandardField.TITLE));
        assertEquals(Optional.of("4"), entry.getField(StandardField.VOLUME));
        assertEquals(Optional.of("3"), entry.getField(StandardField.NUMBER));
        assertEquals(Optional.of("1545-598X"), entry.getField(new UnknownField("SN")));
        assertEquals(Optional.of("387--391"), entry.getField(StandardField.PAGES));
        assertEquals(Optional.of("Gamba, P. and Dell'Acqua, F. and Lisini, G."), entry.getField(StandardField.AUTHOR));
        assertEquals(Optional.of("2006"), entry.getField(StandardField.YEAR));
        assertEquals(Optional.of("Perceptual grouping, street extraction, urban remote sensing"),
                entry.getField(StandardField.KEYWORDS));
        assertEquals(Optional.of("Lorem ipsum abstract"), entry.getField(StandardField.ABSTRACT));
    }

    @Test
    public void importEntriesMedline() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IsiImporterTestMedline.isi").toURI());
        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();

        BibEntry first = entries.getFirst();
        BibEntry second = entries.get(1);

        assertEquals(2, entries.size());
        assertEquals(
                Optional.of("Effects of modafinil on cognitive performance and alertness during sleep deprivation."),
                first.getField(StandardField.TITLE));
        assertEquals(Optional.of("Wesensten, Nancy J."), first.getField(StandardField.AUTHOR));
        assertEquals(Optional.of("Curr Pharm Des"), first.getField(StandardField.JOURNAL));
        assertEquals(Optional.of("2006"), first.getField(StandardField.YEAR));
        assertEquals(Optional.empty(), first.getField(StandardField.MONTH));
        assertEquals(Optional.of("12"), first.getField(StandardField.VOLUME));
        assertEquals(Optional.of("20"), first.getField(StandardField.NUMBER));
        assertEquals(Optional.of("2457--71"), first.getField(StandardField.PAGES));
        assertEquals(StandardEntryType.Article, first.getType());
        assertEquals(
                Optional.of(
                        "Estrogen therapy selectively enhances prefrontal cognitive processes: a randomized, double-blind, placebo-controlled study with functional magnetic resonance imaging in perimenopausal and recently postmenopausal women."),
                second.getField(StandardField.TITLE));
        assertEquals(
                Optional.of(
                        "Joffe, Hadine and Hall, Janet E. and Gruber, Staci and Sarmiento, Ingrid A. and Cohen, Lee S. and Yurgelun-Todd, Deborah and Martin, Kathryn A."),
                second.getField(StandardField.AUTHOR));
        assertEquals(Optional.of("2006"), second.getField(StandardField.YEAR));
        assertEquals(Optional.of(Month.MAY), second.getMonth());
        assertEquals(Optional.of("13"), second.getField(StandardField.VOLUME));
        assertEquals(Optional.of("3"), second.getField(StandardField.NUMBER));
        assertEquals(Optional.of("411--22"), second.getField(StandardField.PAGES));
        assertEquals(StandardEntryType.Article, second.getType());
    }

    @Test
    public void importEntriesEmpty() throws IOException, URISyntaxException {
        Path file = Path.of(IsiImporterTest.class.getResource("IsiImporterTestEmpty.isi").toURI());

        List<BibEntry> entries = importer.importDatabase(file).getDatabase().getEntries();

        assertEquals(1, entries.size());
    }
}
