package org.jabref.gui.exporter;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import org.jabref.gui.DialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.clipboard.ClipBoardManager;
import org.jabref.logic.FilePreferences;
import org.jabref.logic.LibraryPreferences;
import org.jabref.logic.exporter.Exporter;
import org.jabref.logic.exporter.SelfContainedSaveConfiguration;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.preferences.CliPreferences;
import org.jabref.logic.util.CurrentThreadTaskExecutor;
import org.jabref.logic.util.StandardFileType;
import org.jabref.logic.util.TaskExecutor;
import org.jabref.logic.xmp.XmpPreferences;
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.MetaData;
import org.jabref.model.metadata.SaveOrder;

import org.jspecify.annotations.NonNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Answers;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class ExportToClipboardActionTest {

    private ExportToClipboardAction exportToClipboardAction;
    private final DialogService dialogService = spy(DialogService.class);
    private final ClipBoardManager clipBoardManager = mock(ClipBoardManager.class);
    private final BibDatabaseContext databaseContext = mock(BibDatabaseContext.class);
    private final CliPreferences preferences = mock(CliPreferences.class, Answers.RETURNS_DEEP_STUBS);
    private final StateManager stateManager = mock(StateManager.class);

    private TaskExecutor taskExecutor;
    private ObservableList<BibEntry> selectedEntries;

    @BeforeEach
    void setUp() {
        BibEntry entry = new BibEntry(StandardEntryType.Misc)
                .withField(StandardField.AUTHOR, "Souti Chattopadhyay and Nicholas Nelson and Audrey Au and Natalia Morales and Christopher Sanchez and Rahul Pandita and Anita Sarma")
                .withField(StandardField.TITLE, "A tale from the trenches")
                .withField(StandardField.YEAR, "2020")
                .withField(StandardField.DOI, "10.1145/3377811.3380330")
                .withField(StandardField.SUBTITLE, "cognitive biases and software development");

        selectedEntries = FXCollections.observableArrayList(entry);
        when(stateManager.getSelectedEntries()).thenReturn(selectedEntries);

        taskExecutor = new CurrentThreadTaskExecutor();
        when(preferences.getExportPreferences().getCustomExporters()).thenReturn(FXCollections.observableList(List.of()));
        when(preferences.getSelfContainedExportConfiguration()).thenReturn(mock(SelfContainedSaveConfiguration.class));
        when(preferences.getXmpPreferences()).thenReturn(mock(XmpPreferences.class));
        exportToClipboardAction = new ExportToClipboardAction(dialogService, stateManager, clipBoardManager, taskExecutor, preferences);
    }

    @Test
    void executeIfNoSelectedEntries() {
        when(stateManager.getSelectedEntries()).thenReturn(FXCollections.emptyObservableList());

        exportToClipboardAction.execute();
        verify(dialogService, times(1)).notify(Localization.lang("This operation requires one or more entries to be selected."));
    }

    @Test
    void executeOnSuccess() {
        Exporter selectedExporter = new Exporter("html", "HTML", StandardFileType.HTML) {
            @Override
            public void export(@NonNull BibDatabaseContext databaseContext,
                               Path file,
                               @NonNull List<BibEntry> entries) {
            }
        };

        LibraryPreferences libraryPreferences = mock(LibraryPreferences.class, Answers.RETURNS_DEEP_STUBS);
        FilePreferences filePreferences = mock(FilePreferences.class, Answers.RETURNS_DEEP_STUBS);
        when(preferences.getFilePreferences()).thenReturn(filePreferences);
        when(preferences.getLibraryPreferences()).thenReturn(libraryPreferences);
        when(preferences.getExportPreferences().getLastExportExtension()).thenReturn("HTML");
        when(preferences.getSelfContainedExportConfiguration().getSaveOrder()).thenReturn(SaveOrder.getDefaultSaveOrder());
        when(stateManager.getSelectedEntries()).thenReturn(selectedEntries);
        when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext));
        // noinspection ConstantConditions since databaseContext is mocked
        when(databaseContext.getFileDirectories(preferences.getFilePreferences())).thenReturn(new ArrayList<>(List.of(Path.of("path"))));
        when(databaseContext.getMetaData()).thenReturn(new MetaData());
        when(dialogService.showChoiceDialogAndWait(
                eq(Localization.lang("Export")),
                eq(Localization.lang("Select export format")),
                eq(Localization.lang("Export")),
                any(Exporter.class),
                anyCollection())
        ).thenReturn(Optional.of(selectedExporter));

        exportToClipboardAction.execute();
        verify(dialogService, times(1)).showChoiceDialogAndWait(
                eq(Localization.lang("Export")), eq(Localization.lang("Select export format")),
                eq(Localization.lang("Export")), any(Exporter.class), anyCollection());
        verify(dialogService, times(1)).notify(Localization.lang("Entries exported to clipboard") + ": " + selectedEntries.size());
    }
}
