package io.xpipe.app.browser.menu;

import io.xpipe.app.action.AbstractAction;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.browser.action.BrowserActionProvider;
import io.xpipe.app.browser.action.BrowserActionProviders;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.comp.RegionDescriptor;
import io.xpipe.app.hub.action.StoreAction;
import io.xpipe.app.storage.DataStoreEntryRef;

import javafx.scene.control.Button;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;

import lombok.SneakyThrows;

import java.util.List;

public interface BrowserMenuLeafProvider extends BrowserMenuItemProvider {

    default void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
        createAction(model, entries).executeAsync();
    }

    default Class<? extends BrowserActionProvider> getDelegateActionProvider() {
        return null;
    }

    @SneakyThrows
    default AbstractAction createAction(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
        var c = getDelegateActionProvider() != null
                ? BrowserActionProviders.forClass(getDelegateActionProvider())
                        .getActionClass()
                        .orElseThrow()
                : getActionClass().orElseThrow();
        var bm = c.getDeclaredMethod("builder");
        bm.setAccessible(true);
        var b = bm.invoke(null);

        if (StoreAction.class.isAssignableFrom(c)) {
            var refMethod = b.getClass().getMethod("ref", DataStoreEntryRef.class);
            refMethod.setAccessible(true);
            refMethod.invoke(b, model.getEntry());
        }

        if (BrowserAction.class.isAssignableFrom(c)) {
            var modelMethod = b.getClass().getMethod("model", BrowserFileSystemTabModel.class);
            modelMethod.setAccessible(true);
            modelMethod.invoke(b, model);

            var entriesMethod = b.getClass().getMethod("initEntries", BrowserFileSystemTabModel.class, List.class);
            entriesMethod.setAccessible(true);
            entriesMethod.invoke(b, model, entries);
        }

        var m = b.getClass().getDeclaredMethod("build");
        m.setAccessible(true);
        var defValue = c.cast(m.invoke(b));
        return defValue;
    }

    default Button toButton(Region root, BrowserFileSystemTabModel model, List<BrowserEntry> selected) {
        var name = getName(model, selected);
        var b = new Button();
        b.setOnAction(event -> {
            try {
                execute(model, selected);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            event.consume();
        });
        RegionDescriptor.builder().name(name).shortcut(getShortcut()).build().apply(b);
        var graphic = getIcon();
        if (graphic != null) {
            b.setGraphic(graphic.createGraphicNode());
        }
        b.setMnemonicParsing(false);
        root.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
            if (getShortcut() != null && getShortcut().match(event)) {
                b.fire();
                event.consume();
            }
        });

        b.setDisable(!isActive(model));
        model.getCurrentPath().addListener((observable, oldValue, newValue) -> {
            b.setDisable(!isActive(model));
        });

        return b;
    }

    default MenuItem toMenuItem(BrowserFileSystemTabModel model, List<BrowserEntry> selected) {
        var name = getName(model, selected);
        var mi = new MenuItem();
        mi.textProperty().bind(name);
        mi.setOnAction(event -> {
            try {
                execute(model, selected);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            event.consume();
        });
        if (getShortcut() != null) {
            mi.setAccelerator(getShortcut());
        }
        var graphic = getIcon();
        if (graphic != null) {
            mi.setGraphic(graphic.createGraphicNode());
        }
        mi.setMnemonicParsing(false);
        mi.setDisable(!isActive(model));

        return mi;
    }

    @Override
    default boolean isApplicable(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
        if (getDelegateActionProvider() != null) {
            var provider = BrowserActionProviders.forClass(getDelegateActionProvider());
            return provider.isApplicable(model, entries);
        } else {
            return true;
        }
    }
}
