package org.jabref.logic.exporter;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.jabref.logic.layout.LayoutFormatterPreferences;
import org.jabref.logic.util.StandardFileType;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.types.StandardEntryType;
import org.jabref.model.metadata.SaveOrder;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.api.parallel.ResourceLock;
import org.mockito.Answers;

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

@Execution(ExecutionMode.SAME_THREAD)
@ResourceLock("exporter")
class HayagrivaYamlExporterTest {

    private static Exporter hayagrivaYamlExporter;
    private static BibDatabaseContext databaseContext;

    @BeforeAll
    static void setUp() {
        hayagrivaYamlExporter = new TemplateExporter(
                "Hayagriva YAML",
                "hayagrivayaml",
                "hayagrivayaml",
                null,
                StandardFileType.YAML,
                mock(LayoutFormatterPreferences.class, Answers.RETURNS_DEEP_STUBS),
                SaveOrder.getDefaultSaveOrder(),
                BlankLineBehaviour.DELETE_BLANKS);

        databaseContext = new BibDatabaseContext();
    }

    @Test
    final void exportForNoEntriesWritesNothing(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        Path file = tempFile.resolve("ThisIsARandomlyNamedFile");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, tempFile, List.of());
        assertEquals(List.of(), Files.readAllLines(file));
    }

    @Test
    final void exportsCorrectContent(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Article)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "Test Author")
                .withField(StandardField.TITLE, "Test Title")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: article",
                "  title: \"Test Title\"",
                "  author:",
                "    - Author, Test",
                "  date: 2020-10-14",
                "  url: http://example.com");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    final void exportsCorrectMultipleAuthors(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Article)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "Test Author and Other One")
                .withField(StandardField.TITLE, "Test Title")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: article",
                "  title: \"Test Title\"",
                "  author:",
                "    - Author, Test",
                "    - One, Other",
                "  date: 2020-10-14",
                "  url: http://example.com");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    final void formatsContentCorrect(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Misc)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "Test Author")
                .withField(StandardField.TITLE, "Test Title")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: Misc",
                "  title: \"Test Title\"",
                "  author:",
                "    - Author, Test",
                "  date: 2020-10-14",
                "  url: http://example.com");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    void passesModifiedCharset(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Article)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "谷崎 潤一郎")
                .withField(StandardField.TITLE, "細雪")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: article",
                "  title: \"細雪\"",
                "  author:",
                "    - 潤一郎, 谷崎",
                "  date: 2020-10-14",
                "  url: http://example.com");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    void passesModifiedCharsetNull(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Article)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "谷崎 潤一郎")
                .withField(StandardField.TITLE, "細雪")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: article",
                "  title: \"細雪\"",
                "  author:",
                "    - 潤一郎, 谷崎",
                "  date: 2020-10-14",
                "  url: http://example.com");
        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    final void exportsCorrectParentField(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Article)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "Test Author")
                .withField(StandardField.TITLE, "Test Title")
                .withField(StandardField.JOURNAL, "Test Publisher")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: article",
                "  title: \"Test Title\"",
                "  author:",
                "    - Author, Test",
                "  date: 2020-10-14",
                "  parent:",
                "    type: periodical",
                "    title: Test Publisher",
                "  url: http://example.com");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    final void exportsMultipleEntriesAsSingleYamlDocument(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry first = new BibEntry(StandardEntryType.Article)
                .withCitationKey("first")
                .withField(StandardField.AUTHOR, "Test Author")
                .withField(StandardField.TITLE, "First Title")
                .withField(StandardField.URL, "http://example.com/1")
                .withField(StandardField.DATE, "2020-10-14");

        BibEntry second = new BibEntry(StandardEntryType.Article)
                .withCitationKey("second")
                .withField(StandardField.AUTHOR, "Other One")
                .withField(StandardField.TITLE, "Second Title")
                .withField(StandardField.URL, "http://example.com/2")
                .withField(StandardField.DATE, "2021-05-03");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(first, second));

        List<String> expected = List.of(
                "first:",
                "  type: article",
                "  title: \"First Title\"",
                "  author:",
                "    - Author, Test",
                "  date: 2020-10-14",
                "  url: http://example.com/1",
                "second:",
                "  type: article",
                "  title: \"Second Title\"",
                "  author:",
                "    - One, Other",
                "  date: 2021-05-03",
                "  url: http://example.com/2");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    final void exportsDoiNestedUnderSerialNumber(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Article)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "Test Author")
                .withField(StandardField.TITLE, "Test Title")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14")
                .withField(StandardField.DOI, "10.1109/EDOC.2018.00030");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: article",
                "  title: \"Test Title\"",
                "  author:",
                "    - Author, Test",
                "  date: 2020-10-14",
                "  url: http://example.com",
                "  serial-number:",
                "    doi: \"10.1109/EDOC.2018.00030\"");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    final void exportsIsbnNestedUnderSerialNumber(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Book)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "Test Author")
                .withField(StandardField.TITLE, "Test Book")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14")
                .withField(StandardField.ISBN, "978-3-16-148410-0");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: book",
                "  title: \"Test Book\"",
                "  author:",
                "    - Author, Test",
                "  date: 2020-10-14",
                "  url: http://example.com",
                "  serial-number:",
                "    isbn: \"978-3-16-148410-0\"");

        assertEquals(expected, Files.readAllLines(file));
    }

    @Test
    final void exportsIssnNestedUnderSerialNumber(@TempDir Path tempFile) throws IOException, SaveException, ParserConfigurationException, TransformerException {
        BibEntry entry = new BibEntry(StandardEntryType.Article)
                .withCitationKey("test")
                .withField(StandardField.AUTHOR, "Test Author")
                .withField(StandardField.TITLE, "Test Article")
                .withField(StandardField.URL, "http://example.com")
                .withField(StandardField.DATE, "2020-10-14")
                .withField(StandardField.ISSN, "0896-3207");

        Path file = tempFile.resolve("RandomFileName");
        Files.createFile(file);
        hayagrivaYamlExporter.export(databaseContext, file, List.of(entry));

        List<String> expected = List.of(
                "test:",
                "  type: article",
                "  title: \"Test Article\"",
                "  author:",
                "    - Author, Test",
                "  date: 2020-10-14",
                "  url: http://example.com",
                "  serial-number:",
                "    issn: \"0896-3207\"");

        assertEquals(expected, Files.readAllLines(file));
    }
}

