package p.studio.controls.forms;

import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import p.studio.lsp.events.StudioWorkspaceEventBus;

import java.util.Objects;

public abstract class StudioFormSection extends StudioSection {
    private final StudioFormActionBar actionBar = new StudioFormActionBar(
            this::handleBeginEdit,
            this::handleApply,
            this::handleReset,
            this::handleCancel);
    private final VBox contentHost = new VBox();
    private boolean editAllowed = true;
    private VBox mountedSection;

    protected StudioFormSection() {
        super();
        actionBar.getStyleClass().add("studio-form-section-actions");
        contentHost.setFillWidth(true);
        VBox.setVgrow(contentHost, Priority.ALWAYS);
    }

    public static VBox createReadOnlySection(String title, Node content) {
        return StudioSection.createSection(title, content);
    }

    public static VBox createEditableSection(String title, Node headerActions, Node content, StudioFormMode mode) {
        return StudioSection.createSection(title, headerActions, content, true, mode == StudioFormMode.EDITING, true);
    }

    protected final void renderFormSection(Node content) {
        updateActionBarState();
        if (mountedSection == null) {
            mountedSection = createEditableSection(sectionTitle(), actionBar, contentHost, formMode());
            final String sectionStyleClass = sectionStyleClass();
            if (sectionStyleClass != null && !sectionStyleClass.isBlank()) {
                mountedSection.getStyleClass().add(sectionStyleClass);
            }
            getChildren().setAll(mountedSection);
        }
        syncMountedSectionState();
        contentHost.getChildren().setAll(content);
    }

    protected final void clearRenderedSection() {
        contentHost.getChildren().clear();
        mountedSection = null;
        getChildren().clear();
    }

    protected final void updateActionBarState() {
        actionBar.updateState(formMode(), isDirty(), editAllowed);
    }

    protected final void setEditAllowed(boolean editAllowed) {
        this.editAllowed = editAllowed;
        updateActionBarState();
    }

    protected final boolean isEditAllowed() {
        return editAllowed;
    }

    protected abstract String formSectionId();

    protected final void handleEditScopeChanged(StudioFormEditScopeChangedEvent event, String currentScopeKey) {
        if (!Objects.equals(currentScopeKey, event.scopeKey())) {
            setEditAllowed(true);
            return;
        }
        if (event.ownerSectionId() == null && formMode() == StudioFormMode.EDITING) {
            cancel();
            renderSection();
            return;
        }
        setEditAllowed(event.ownerSectionId() == null || formSectionId().equals(event.ownerSectionId()));
    }

    protected final void publishEditScope(StudioWorkspaceEventBus workspaceBus, String scopeKey, String ownerSectionId) {
        setEditAllowed(ownerSectionId == null || formSectionId().equals(ownerSectionId));
        workspaceBus.publish(new StudioFormEditScopeChangedEvent(scopeKey, ownerSectionId));
    }

    protected final void rerenderPreservingScrollPosition() {
        final ScrollPane scrollPane = findAncestorScrollPane();
        final double previousVvalue = scrollPane == null ? 0.0d : scrollPane.getVvalue();
        renderSection();
        if (scrollPane != null) {
            restoreScrollPosition(scrollPane, previousVvalue, 2);
        }
    }

    protected abstract void renderSection();

    protected abstract String sectionTitle();

    protected abstract StudioFormMode formMode();

    protected abstract boolean isDirty();

    protected abstract void beginEdit();

    protected abstract void apply();

    protected abstract void reset();

    protected abstract void cancel();

    protected String sectionStyleClass() {
        return null;
    }

    private void handleBeginEdit() {
        if (!editAllowed) {
            return;
        }
        beginEdit();
        rerenderPreservingScrollPosition();
    }

    private void handleApply() {
        apply();
    }

    private void handleReset() {
        reset();
        rerenderPreservingScrollPosition();
    }

    private void handleCancel() {
        cancel();
        rerenderPreservingScrollPosition();
    }

    private void restoreScrollPosition(ScrollPane scrollPane, double vvalue, int remainingPasses) {
        Platform.runLater(() -> {
            scrollPane.applyCss();
            scrollPane.layout();
            scrollPane.setVvalue(vvalue);
            if (remainingPasses > 0) {
                restoreScrollPosition(scrollPane, vvalue, remainingPasses - 1);
            }
        });
    }

    private ScrollPane findAncestorScrollPane() {
        Parent current = getParent();
        while (current != null) {
            if (current instanceof ScrollPane scrollPane) {
                return scrollPane;
            }
            current = current.getParent();
        }
        return null;
    }

    private void syncMountedSectionState() {
        if (mountedSection == null) {
            return;
        }
        mountedSection.getStyleClass().remove("studio-form-section-editing");
        if (formMode() == StudioFormMode.EDITING) {
            mountedSection.getStyleClass().add("studio-form-section-editing");
        }
    }
}
