package org.jabref.gui.preferences.external;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.control.ButtonType;
import javafx.scene.control.DialogPane;

import org.jabref.gui.DialogService;
import org.jabref.gui.frame.ExternalApplicationsPreferences;
import org.jabref.gui.preferences.PreferenceTabViewModel;
import org.jabref.gui.push.GuiPushToApplication;
import org.jabref.gui.push.GuiPushToApplicationSettings;
import org.jabref.gui.push.GuiPushToApplications;
import org.jabref.gui.push.GuiPushToEmacs;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.FilePreferences;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.push.CitationCommandString;
import org.jabref.logic.push.PushToApplicationPreferences;
import org.jabref.logic.util.strings.StringUtil;

import de.saxsys.mvvmfx.utils.validation.CompositeValidator;
import de.saxsys.mvvmfx.utils.validation.FunctionBasedValidator;
import de.saxsys.mvvmfx.utils.validation.ValidationMessage;
import de.saxsys.mvvmfx.utils.validation.ValidationStatus;
import de.saxsys.mvvmfx.utils.validation.Validator;

public class ExternalTabViewModel implements PreferenceTabViewModel {

    private final StringProperty eMailReferenceSubjectProperty = new SimpleStringProperty("");
    private final BooleanProperty autoOpenAttachedFoldersProperty = new SimpleBooleanProperty();
    private final ListProperty<GuiPushToApplication> pushToApplicationsListProperty = new SimpleListProperty<>();
    private final ObjectProperty<GuiPushToApplication> selectedPushToApplicationProperty = new SimpleObjectProperty<>();
    private final StringProperty citeCommandProperty = new SimpleStringProperty("");
    private final BooleanProperty useCustomTerminalProperty = new SimpleBooleanProperty();
    private final StringProperty customTerminalCommandProperty = new SimpleStringProperty("");
    private final BooleanProperty useCustomFileBrowserProperty = new SimpleBooleanProperty();
    private final StringProperty customFileBrowserCommandProperty = new SimpleStringProperty("");
    private final StringProperty kindleEmailProperty = new SimpleStringProperty("");

    private final Validator terminalCommandValidator;
    private final Validator fileBrowserCommandValidator;
    private final Validator citeCommandValidator;

    private final DialogService dialogService;
    private final FilePreferences filePreferences;

    private final FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder().build();

    private final ExternalApplicationsPreferences initialExternalApplicationPreferences;
    private final PushToApplicationPreferences initialPushToApplicationPreferences;
    private final PushToApplicationPreferences workingPushToApplicationPreferences;

    public ExternalTabViewModel(DialogService dialogService,
                                ExternalApplicationsPreferences externalApplicationsPreferences,
                                PushToApplicationPreferences pushToApplicationPreferences,
                                FilePreferences filePreferences) {
        this.dialogService = dialogService;
        this.filePreferences = filePreferences;
        this.initialExternalApplicationPreferences = externalApplicationsPreferences;
        this.initialPushToApplicationPreferences = pushToApplicationPreferences;
        this.workingPushToApplicationPreferences = PushToApplicationPreferences.getDefault();
        copyPushPreferences(workingPushToApplicationPreferences, initialPushToApplicationPreferences);

        terminalCommandValidator = new FunctionBasedValidator<>(
                customTerminalCommandProperty,
                input -> !StringUtil.isNullOrEmpty(input),
                ValidationMessage.error("%s > %s %n %n %s".formatted(
                        Localization.lang("External programs"),
                        Localization.lang("Custom applications"),
                        Localization.lang("Please specify a terminal application."))));

        fileBrowserCommandValidator = new FunctionBasedValidator<>(
                customFileBrowserCommandProperty,
                input -> !StringUtil.isNullOrEmpty(input),
                ValidationMessage.error("%s > %s %n %n %s".formatted(
                        Localization.lang("External programs"),
                        Localization.lang("Custom applications"),
                        Localization.lang("Please specify a file browser."))));

        citeCommandValidator = new FunctionBasedValidator<>(
                citeCommandProperty,
                input -> {
                    int indexKey1 = input.indexOf(CitationCommandString.CITE_KEY1);
                    int indexKey2 = input.indexOf(CitationCommandString.CITE_KEY2);
                    return indexKey1 >= 0 && indexKey2 >= 0 && indexKey2 >= (indexKey1 + CitationCommandString.CITE_KEY1.length());
                },
                ValidationMessage.warning(Localization.lang("The cite command should contain '%0' and '%1'.", CitationCommandString.CITE_KEY1, CitationCommandString.CITE_KEY2)));
    }

    @Override
    public void setValues() {
        copyPushPreferences(workingPushToApplicationPreferences, initialPushToApplicationPreferences);

        eMailReferenceSubjectProperty.setValue(initialExternalApplicationPreferences.getEmailSubject());
        autoOpenAttachedFoldersProperty.setValue(initialExternalApplicationPreferences.shouldAutoOpenEmailAttachmentsFolder());

        pushToApplicationsListProperty.setValue(
                FXCollections.observableArrayList(GuiPushToApplications.getAllGUIApplications(dialogService, initialPushToApplicationPreferences)));
        selectedPushToApplicationProperty.setValue(
                GuiPushToApplications.getGUIApplicationByName(initialPushToApplicationPreferences.getActiveApplicationName(), dialogService, initialPushToApplicationPreferences)
                                     .orElseGet(() -> new GuiPushToEmacs(dialogService, initialPushToApplicationPreferences)));

        citeCommandProperty.setValue(initialPushToApplicationPreferences.getCiteCommand().toString());

        useCustomTerminalProperty.setValue(initialExternalApplicationPreferences.useCustomTerminal());
        customTerminalCommandProperty.setValue(initialExternalApplicationPreferences.getCustomTerminalCommand());
        useCustomFileBrowserProperty.setValue(initialExternalApplicationPreferences.useCustomFileBrowser());
        customFileBrowserCommandProperty.setValue(initialExternalApplicationPreferences.getCustomFileBrowserCommand());
        kindleEmailProperty.setValue(initialExternalApplicationPreferences.getKindleEmail());
    }

    @Override
    public void storeSettings() {
        initialExternalApplicationPreferences.setEMailSubject(eMailReferenceSubjectProperty.getValue());
        initialExternalApplicationPreferences.setAutoOpenEmailAttachmentsFolder(autoOpenAttachedFoldersProperty.getValue());
        initialExternalApplicationPreferences.setUseCustomTerminal(useCustomTerminalProperty.getValue());
        initialExternalApplicationPreferences.setCustomTerminalCommand(customTerminalCommandProperty.getValue());
        initialExternalApplicationPreferences.setUseCustomFileBrowser(useCustomFileBrowserProperty.getValue());
        initialExternalApplicationPreferences.setCustomFileBrowserCommand(customFileBrowserCommandProperty.getValue());
        initialExternalApplicationPreferences.setKindleEmail(kindleEmailProperty.getValue());

        initialPushToApplicationPreferences.setActiveApplicationName(selectedPushToApplicationProperty.getValue().getDisplayName());
        initialPushToApplicationPreferences.setCommandPaths(workingPushToApplicationPreferences.getCommandPaths());
        initialPushToApplicationPreferences.setEmacsArguments(workingPushToApplicationPreferences.getEmacsArguments());
        initialPushToApplicationPreferences.setVimServer(workingPushToApplicationPreferences.getVimServer());
        initialPushToApplicationPreferences.setCiteCommand(CitationCommandString.from(citeCommandProperty.getValue()));
    }

    /// Copies all push-to-application settings from `source` into the existing `target` instance.
    private static void copyPushPreferences(PushToApplicationPreferences target, PushToApplicationPreferences source) {
        target.setActiveApplicationName(source.getActiveApplicationName());
        target.setCommandPaths(source.getCommandPaths());
        target.setEmacsArguments(source.getEmacsArguments());
        target.setVimServer(source.getVimServer());
        target.setCiteCommand(source.getCiteCommand());
    }

    public ValidationStatus terminalCommandValidationStatus() {
        return terminalCommandValidator.getValidationStatus();
    }

    public ValidationStatus fileBrowserCommandValidationStatus() {
        return fileBrowserCommandValidator.getValidationStatus();
    }

    public ValidationStatus citeCommandValidationStatus() {
        return citeCommandValidator.getValidationStatus();
    }

    @Override
    public boolean validateSettings() {
        CompositeValidator validator = new CompositeValidator();

        if (useCustomTerminalProperty.getValue()) {
            validator.addValidators(terminalCommandValidator);
        }

        if (useCustomFileBrowserProperty.getValue()) {
            validator.addValidators(fileBrowserCommandValidator);
        }

        validator.addValidators(citeCommandValidator);

        ValidationStatus validationStatus = validator.getValidationStatus();
        if (!validationStatus.isValid()) {
            validationStatus.getHighestMessage().ifPresent(message ->
                    dialogService.showErrorDialogAndWait(message.getMessage()));
            return false;
        }
        return true;
    }

    public void pushToApplicationSettings() {
        GuiPushToApplication selectedApplication = selectedPushToApplicationProperty.getValue();
        GuiPushToApplicationSettings settings = selectedApplication.getSettings(
                selectedApplication,
                dialogService,
                filePreferences,
                workingPushToApplicationPreferences);

        DialogPane dialogPane = new DialogPane();
        dialogPane.setContent(settings.getSettingsPane());

        dialogService.showCustomDialogAndWait(
                             Localization.lang("Application settings"),
                             dialogPane,
                             ButtonType.OK, ButtonType.CANCEL)
                     .ifPresent(btn -> {
                                 if (btn == ButtonType.OK) {
                                     settings.storeSettings();
                                 }
                             }
                     );
    }

    public void customTerminalBrowse() {
        dialogService.showFileOpenDialog(fileDialogConfiguration)
                     .ifPresent(file -> customTerminalCommandProperty.setValue(file.toAbsolutePath().toString()));
    }

    public void customFileBrowserBrowse() {
        dialogService.showFileOpenDialog(fileDialogConfiguration)
                     .ifPresent(file -> customFileBrowserCommandProperty.setValue(file.toAbsolutePath().toString()));
    }

    // EMail

    public StringProperty eMailReferenceSubjectProperty() {
        return this.eMailReferenceSubjectProperty;
    }

    public StringProperty kindleEmailProperty() {
        return this.kindleEmailProperty;
    }

    public BooleanProperty autoOpenAttachedFoldersProperty() {
        return this.autoOpenAttachedFoldersProperty;
    }

    // Push-To-Application

    public ListProperty<GuiPushToApplication> pushToApplicationsListProperty() {
        return this.pushToApplicationsListProperty;
    }

    public ObjectProperty<GuiPushToApplication> selectedPushToApplication() {
        return this.selectedPushToApplicationProperty;
    }

    public StringProperty citeCommandProperty() {
        return this.citeCommandProperty;
    }

    public BooleanProperty useCustomTerminalProperty() {
        return this.useCustomTerminalProperty;
    }

    public StringProperty customTerminalCommandProperty() {
        return this.customTerminalCommandProperty;
    }

    // Open File Browser

    public BooleanProperty useCustomFileBrowserProperty() {
        return this.useCustomFileBrowserProperty;
    }

    public StringProperty customFileBrowserCommandProperty() {
        return this.customFileBrowserCommandProperty;
    }

    public void resetCiteCommandToDefault() {
        this.citeCommandProperty.setValue(PushToApplicationPreferences.getDefault().getCiteCommand().toString());
    }
}
