package p.studio.workspaces.assets;

import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import p.studio.Container;
import p.studio.projectstate.ProjectLocalStudioState;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.Workspace;
import p.studio.workspaces.WorkspaceId;
import p.studio.workspaces.assets.details.AssetDetailsControl;
import p.studio.workspaces.assets.list.AssetListControl;
import p.studio.workspaces.assets.messages.events.StudioAssetsRefreshRequestedEvent;
import p.studio.workspaces.assets.messages.events.StudioAssetsWorkspaceSelectionRequestedEvent;
import p.studio.workspaces.assets.wizards.AddAssetWizard;
import p.studio.workspaces.assets.wizards.PackWorkspaceWizard;

import java.util.List;

public final class AssetWorkspace extends Workspace {
    private final BorderPane root = new BorderPane();
    private final AssetListControl assetListControl;
    private final AssetDetailsControl detailsControl;
    private final AssetLogsPane logsPane;
    private SplitPane splitPane;
    private Runnable stateChangedAction = () -> { };
    private ProjectLocalStudioState.AssetsLayoutState pendingAssetsLayoutState = ProjectLocalStudioState.AssetsLayoutState.defaults();
    private boolean applyingPendingLayoutState;

    public AssetWorkspace(final ProjectReference projectReference) {
        super(projectReference);
        this.assetListControl = new AssetListControl(this.projectReference, this.workspaceEventBus);
        this.detailsControl = new AssetDetailsControl(this.projectReference, this.workspaceEventBus);
        this.logsPane = new AssetLogsPane(this.workspaceEventBus);
        root.getStyleClass().add("assets-workspace");
        root.setCenter(buildLayout());
        root.setBottom(logsPane.getPane());
    }

    @Override
    public WorkspaceId workspaceId() {
        return WorkspaceId.ASSETS;
    }

    @Override
    public I18n title() {
        return I18n.WORKSPACE_ASSETS;
    }

    @Override
    public Node rootNode() {
        return root;
    }

    @Override
    public void load() {
        reapplyPendingLayoutState();
        schedulePendingLayoutReapply();
        workspaceEventBus.publish(new StudioAssetsRefreshRequestedEvent());
    }

    @Override
    public void unLoad() {
    }

    @Override
    public List<p.studio.controls.lifecycle.StudioControlLifecycle> lifecycleParticipants() {
        return List.of(assetListControl, detailsControl, logsPane);
    }

    public void setStateChangedAction(final Runnable stateChangedAction) {
        this.stateChangedAction = java.util.Objects.requireNonNull(stateChangedAction, "stateChangedAction");
    }

    public void restoreProjectLocalState(final ProjectLocalStudioState projectLocalStudioState) {
        pendingAssetsLayoutState = projectLocalStudioState.shellLayout().assetsLayout();
        reapplyPendingLayoutState();
        schedulePendingLayoutReapply();
    }

    public void reapplyPendingLayoutState() {
        if (splitPane == null || pendingAssetsLayoutState.contentSplitDividers().isEmpty()) {
            return;
        }
        applyingPendingLayoutState = true;
        splitPane.setDividerPositions(pendingAssetsLayoutState.contentSplitDividers().stream()
                .mapToDouble(Double::doubleValue)
                .toArray());
        javafx.application.Platform.runLater(() -> applyingPendingLayoutState = false);
    }

    public ProjectLocalStudioState.AssetsLayoutState captureLayoutState() {
        if (splitPane == null || splitPane.getDividers().isEmpty()) {
            return ProjectLocalStudioState.AssetsLayoutState.defaults();
        }
        return new ProjectLocalStudioState.AssetsLayoutState(
                splitPane.getDividers().stream()
                        .map(SplitPane.Divider::getPosition)
                        .toList());
    }

    private VBox buildLayout() {
        final var topProgress = createTopProgressBox();
        final var actionBar = createActionBar();
        final var splitPane = createAssetSplitPane();
        return new VBox(10, topProgress, actionBar, splitPane);
    }

    private VBox createTopProgressBox() {
        final var topProgress = new VBox(6);
        topProgress.getStyleClass().add("assets-workspace-inline-progress");
        return topProgress;
    }

    private HBox createActionBar() {
        final var refreshButton = createRefreshButton();
        final var addAssetButton = createAddAssetButton();
        final var packButton = createPackButton();
        final var workspaceActionBar = new HBox(8, refreshButton, addAssetButton, packButton);
        workspaceActionBar.setAlignment(Pos.CENTER_LEFT);
        workspaceActionBar.getStyleClass().add("assets-workspace-action-bar");
        return workspaceActionBar;
    }

    private Button createRefreshButton() {
        final var refreshButton = new Button();
        refreshButton.textProperty().bind(Container.i18n().bind(I18n.ASSETS_NAVIGATOR_ACTION_REFRESH));
        refreshButton.getStyleClass().addAll("studio-button", "studio-button-primary", "assets-workspace-action-button");
        refreshButton.setMaxWidth(Double.MAX_VALUE);
        refreshButton.setOnAction(event -> refreshAssets());
        return refreshButton;
    }

    private void refreshAssets() {
        workspaceEventBus.publish(new StudioAssetsRefreshRequestedEvent(null, true));
    }

    private Button createAddAssetButton() {
        final var addAssetButton = new Button();
        addAssetButton.textProperty().bind(Container.i18n().bind(I18n.ASSETS_NAVIGATOR_ACTION_ADD));
        addAssetButton.getStyleClass().addAll(
                "studio-button",
                "studio-button-secondary",
                "studio-button-add-asset",
                "assets-workspace-action-button");
        addAssetButton.setOnAction(event -> openAddAssetWizard());
        return addAssetButton;
    }

    private Button createPackButton() {
        final var packButton = new Button();
        packButton.textProperty().bind(Container.i18n().bind(I18n.ASSETS_NAVIGATOR_ACTION_PACK));
        packButton.getStyleClass().addAll(
                "studio-button",
                "studio-button-secondary",
                "assets-workspace-action-button");
        packButton.setOnAction(event -> openPackWizard());
        return packButton;
    }

    private void openAddAssetWizard() {
        if (root.getScene() == null) {
            return;
        }
        AddAssetWizard.showAndWait(root.getScene().getWindow(), projectReference).ifPresent(assetReference -> {
            workspaceEventBus.publish(new StudioAssetsRefreshRequestedEvent(assetReference));
        });
    }

    private void openPackWizard() {
        if (root.getScene() == null) {
            return;
        }
        PackWorkspaceWizard.showAndWait(root.getScene().getWindow(), projectReference);
    }

    private SplitPane createAssetSplitPane() {
        splitPane = new SplitPane(assetListControl, detailsControl);
        splitPane.setDividerPositions(0.34);
        splitPane.getStyleClass().add("assets-workspace-split");
        splitPane.getDividers().forEach(divider ->
                divider.positionProperty().addListener((ignored, previous, current) -> {
                    if (applyingPendingLayoutState) {
                        return;
                    }
                    pendingAssetsLayoutState = captureLayoutState();
                    stateChangedAction.run();
                }));
        VBox.setVgrow(splitPane, Priority.ALWAYS);
        return splitPane;
    }

    private void schedulePendingLayoutReapply() {
        if (splitPane == null) {
            return;
        }
        if (splitPane.getWidth() > 0) {
            javafx.application.Platform.runLater(this::reapplyPendingLayoutState);
            return;
        }
        final javafx.beans.value.ChangeListener<Number>[] listenerRef = new javafx.beans.value.ChangeListener[1];
        listenerRef[0] = (ignored, previous, current) -> {
            if (current == null || current.doubleValue() <= 0) {
                return;
            }
            splitPane.widthProperty().removeListener(listenerRef[0]);
            javafx.application.Platform.runLater(this::reapplyPendingLayoutState);
        };
        splitPane.widthProperty().addListener(listenerRef[0]);
    }
}
