package io.xpipe.app.browser.file;

import io.xpipe.app.comp.*;
import io.xpipe.app.hub.comp.*;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.storage.DataStoreEntry;

import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.SetChangeListener;
import javafx.css.PseudoClass;
import javafx.scene.control.Button;
import javafx.scene.layout.Region;

import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Predicate;

public final class BrowserConnectionListComp extends SimpleRegionBuilder {

    private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
    private static final PseudoClass BUSY = PseudoClass.getPseudoClass("busy");
    private final ObservableValue<DataStoreEntry> selected;
    private final Predicate<StoreEntryWrapper> applicable;
    private final BiConsumer<StoreEntryWrapper, BooleanProperty> action;
    private final Property<StoreCategoryWrapper> category;
    private final Property<StoreFilter> filter;

    public BrowserConnectionListComp(
            ObservableValue<DataStoreEntry> selected,
            Predicate<StoreEntryWrapper> applicable,
            BiConsumer<StoreEntryWrapper, BooleanProperty> action,
            Property<StoreCategoryWrapper> category,
            Property<StoreFilter> filter) {
        this.selected = selected;
        this.applicable = applicable;
        this.action = action;
        this.category = category;
        this.filter = filter;
    }

    @Override
    protected Region createSimple() {
        var busyEntries = FXCollections.<StoreSection>observableSet(new HashSet<>());
        BiConsumer<StoreSection, RegionBuilder<Button>> augment = (s, comp) -> {
            comp.disable(Bindings.createBooleanBinding(
                    () -> {
                        return busyEntries.contains(s) || !applicable.test(s.getWrapper());
                    },
                    busyEntries));
            comp.apply(struc -> {
                selected.addListener((observable, oldValue, newValue) -> {
                    PlatformThread.runLaterIfNeeded(() -> {
                        struc.pseudoClassStateChanged(
                                SELECTED,
                                newValue != null
                                        && newValue.equals(s.getWrapper().getEntry()));
                    });
                });
                busyEntries.addListener((SetChangeListener<? super StoreSection>) change -> {
                    PlatformThread.runLaterIfNeeded(() -> {
                        struc.pseudoClassStateChanged(BUSY, change.getSet().contains(s));
                    });
                });
            });
        };

        var section = new StoreSectionMiniComp(
                StoreSection.createTopLevel(
                        StoreViewState.get().getAllEntries(),
                        Set.of(),
                        this::filter,
                        filter,
                        category,
                        StoreViewState.get().getEntriesListVisibilityObservable(),
                        StoreViewState.get().getEntriesListUpdateObservable(),
                        new ReadOnlyBooleanWrapper(true)),
                augment,
                selectedAction -> {
                    BooleanProperty busy = new SimpleBooleanProperty(false);
                    action.accept(selectedAction.getWrapper(), busy);
                    busy.addListener((observable, oldValue, newValue) -> {
                        if (newValue) {
                            busyEntries.add(selectedAction);
                        } else {
                            busyEntries.remove(selectedAction);
                        }
                    });
                },
                false);

        var r = section.vgrow().build();
        r.getStyleClass().add("bookmark-list");
        return r;
    }

    private boolean filter(StoreEntryWrapper w) {
        return applicable.test(w);
    }
}
