package p.studio.workspaces.assets.wizards;

import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import p.packer.messages.AssetReference;
import p.packer.messages.MoveAssetRequest;
import p.packer.messages.MoveAssetResult;
import p.packer.messages.PackerOperationStatus;
import p.studio.Container;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetSummary;

import java.io.File;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

public final class MoveAssetWizard {
    private final ProjectReference projectReference;
    private final AssetReference assetReference;
    private final AssetWorkspaceAssetSummary assetSummary;
    private final Stage stage;
    private final AtomicReference<AssetReference> result = new AtomicReference<>();
    private final Label stepTitle = new Label();
    private final Label stepDescription = new Label();
    private final VBox stepBody = new VBox(12);
    private final Label feedbackLabel = new Label();
    private final Button backButton = new Button();
    private final Button nextButton = new Button();
    private final Button confirmButton = new Button();
    private final TextField destinationParentField = new TextField();
    private final TextField destinationNameField = new TextField();
    private final TextField targetRootField = new TextField();

    private int stepIndex;
    private boolean moving;

    private MoveAssetWizard(
            Window owner,
            ProjectReference projectReference,
            AssetReference assetReference,
            AssetWorkspaceAssetSummary assetSummary) {
        this.projectReference = Objects.requireNonNull(projectReference, "projectReference");
        this.assetReference = Objects.requireNonNull(assetReference, "assetReference");
        this.assetSummary = Objects.requireNonNull(assetSummary, "assetSummary");
        this.stage = new Stage();
        stage.initOwner(owner);
        stage.initModality(Modality.WINDOW_MODAL);
        stage.setTitle(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_TITLE));
        stage.setScene(new Scene(buildRoot(), 760, 460));
        stage.getScene().getStylesheets().add(Container.theme().getDefaultTheme());
        stage.setOnCloseRequest(event -> {
            if (moving) {
                event.consume();
            }
        });

        destinationParentField.setText(initialDestinationParent());
        destinationNameField.setText(assetSummary.assetRoot().getFileName().toString());
        targetRootField.setEditable(false);
        renderStep();
    }

    public static Optional<AssetReference> showAndWait(
            Window owner,
            ProjectReference projectReference,
            AssetReference assetReference,
            AssetWorkspaceAssetSummary assetSummary) {
        final MoveAssetWizard wizard = new MoveAssetWizard(owner, projectReference, assetReference, assetSummary);
        wizard.stage.showAndWait();
        return Optional.ofNullable(wizard.result.get());
    }

    private VBox buildRoot() {
        stepTitle.getStyleClass().add("studio-launcher-section-title");
        stepDescription.getStyleClass().add("studio-launcher-subtitle");
        feedbackLabel.getStyleClass().add("studio-launcher-feedback");
        feedbackLabel.setWrapText(true);

        backButton.textProperty().bind(Container.i18n().bind(I18n.WIZARD_BACK));
        backButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
        backButton.setOnAction(ignored -> goBack());

        nextButton.textProperty().bind(Container.i18n().bind(I18n.WIZARD_NEXT));
        nextButton.getStyleClass().addAll("studio-button", "studio-button-primary");
        nextButton.setOnAction(ignored -> goNext());

        confirmButton.textProperty().bind(Container.i18n().bind(I18n.ASSETS_RELOCATE_WIZARD_BUTTON_CONFIRM));
        confirmButton.getStyleClass().addAll("studio-button", "studio-button-primary");
        confirmButton.setOnAction(ignored -> confirmMove());

        final Button cancelButton = new Button();
        cancelButton.textProperty().bind(Container.i18n().bind(I18n.WIZARD_CANCEL));
        cancelButton.getStyleClass().addAll("studio-button", "studio-button-cancel");
        cancelButton.setOnAction(ignored -> {
            if (!moving) {
                stage.close();
            }
        });

        final HBox actions = new HBox(12, backButton, nextButton, confirmButton, cancelButton);
        actions.setAlignment(Pos.CENTER_RIGHT);

        final VBox root = new VBox(16, stepTitle, stepDescription, stepBody, feedbackLabel, actions);
        root.setPadding(new Insets(24));
        VBox.setVgrow(stepBody, Priority.ALWAYS);
        return root;
    }

    private void renderStep() {
        backButton.setDisable(stepIndex == 0 || moving);
        nextButton.setVisible(stepIndex == 0 && !moving);
        nextButton.setManaged(stepIndex == 0 && !moving);
        confirmButton.setVisible(stepIndex == 1 && !moving);
        confirmButton.setManaged(stepIndex == 1 && !moving);
        if (!moving && stepIndex != 2 && feedbackLabel.getText() == null) {
            feedbackLabel.setText("");
        }

        switch (stepIndex) {
            case 0 -> renderDestinationStep();
            case 1 -> renderSummaryStep();
            case 2 -> renderWaitingStep();
            default -> throw new IllegalStateException("Unknown wizard step: " + stepIndex);
        }
    }

    private void renderDestinationStep() {
        stepTitle.textProperty().unbind();
        stepDescription.textProperty().unbind();
        stepTitle.textProperty().bind(Container.i18n().bind(I18n.ASSETS_RELOCATE_WIZARD_STEP_DESTINATION_TITLE));
        stepDescription.textProperty().bind(Container.i18n().bind(I18n.ASSETS_RELOCATE_WIZARD_STEP_DESTINATION_DESCRIPTION));

        targetRootField.setText(displayTargetRoot());

        final Button browseButton = new Button();
        browseButton.textProperty().bind(Container.i18n().bind(I18n.WIZARD_BROWSE));
        browseButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
        browseButton.setOnAction(ignored -> browseForDestinationParent());
        browseButton.setDisable(moving);

        destinationParentField.setDisable(moving);
        destinationNameField.setDisable(moving);

        final HBox destinationParentRow = new HBox(12, destinationParentField, browseButton);
        HBox.setHgrow(destinationParentField, Priority.ALWAYS);

        final Label note = new Label(Container.i18n().format(
                I18n.ASSETS_RELOCATE_WIZARD_ASSETS_ROOT_HINT,
                assetsRoot().toString()));
        note.getStyleClass().add("studio-launcher-feedback");
        note.setWrapText(true);

        stepBody.getChildren().setAll(
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_CURRENT_ROOT),
                        currentRelativeRoot()),
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_DESTINATION_PARENT),
                        destinationParentRow),
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_DESTINATION_NAME),
                        destinationNameField),
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_TARGET_ROOT),
                        targetRootField),
                note);
    }

    private void renderSummaryStep() {
        stepTitle.textProperty().unbind();
        stepDescription.textProperty().unbind();
        stepTitle.textProperty().bind(Container.i18n().bind(I18n.ASSETS_RELOCATE_WIZARD_STEP_SUMMARY_TITLE));
        stepDescription.textProperty().bind(Container.i18n().bind(I18n.ASSETS_RELOCATE_WIZARD_STEP_SUMMARY_DESCRIPTION));
        stepBody.getChildren().setAll(
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_CURRENT_ROOT),
                        currentRelativeRoot()),
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_DESTINATION_PARENT),
                        displayDestinationParent()),
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_DESTINATION_NAME),
                        normalizedDestinationName()),
                AssetDetailsUiSupport.createKeyValueRow(
                        Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_LABEL_TARGET_ROOT),
                        targetRelativeRoot()),
                AssetDetailsUiSupport.createSectionMessage(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_NOTE)));
    }

    private void renderWaitingStep() {
        stepTitle.textProperty().unbind();
        stepDescription.textProperty().unbind();
        stepTitle.textProperty().bind(Container.i18n().bind(I18n.ASSETS_RELOCATE_WIZARD_STEP_WAITING_TITLE));
        stepDescription.textProperty().bind(Container.i18n().bind(I18n.ASSETS_RELOCATE_WIZARD_STEP_WAITING_DESCRIPTION));
        final ProgressIndicator indicator = new ProgressIndicator();
        final VBox box = new VBox(16, indicator, AssetDetailsUiSupport.createSectionMessage(targetRelativeRoot()));
        box.setAlignment(Pos.CENTER_LEFT);
        stepBody.getChildren().setAll(box);
    }

    private void goBack() {
        if (moving || stepIndex == 0) {
            return;
        }
        stepIndex -= 1;
        renderStep();
    }

    private void goNext() {
        if (moving || !validateDestination(true)) {
            return;
        }
        feedbackLabel.setText("");
        stepIndex = 1;
        renderStep();
    }

    private void confirmMove() {
        if (moving || !validateDestination(true)) {
            return;
        }
        moving = true;
        feedbackLabel.setText("");
        stepIndex = 2;
        renderStep();
        final MoveAssetRequest request = new MoveAssetRequest(
                projectReference.toPackerProjectContext(),
                assetReference,
                targetRelativeRoot());
        Container.backgroundTasks().submit(() -> moveAsset(request));
    }

    private void moveAsset(MoveAssetRequest request) {
        try {
            final MoveAssetResult moveResult = Container.packer().workspaceService().moveAsset(request);
            Platform.runLater(() -> applyMoveResult(moveResult));
        } catch (RuntimeException exception) {
            Platform.runLater(() -> applyMoveFailure(exception));
        }
    }

    private void applyMoveResult(MoveAssetResult moveResult) {
        moving = false;
        if (moveResult.status() == PackerOperationStatus.SUCCESS && moveResult.assetReference() != null) {
            result.set(moveResult.assetReference());
            stage.close();
            return;
        }
        feedbackLabel.setText(Objects.requireNonNullElse(moveResult.summary(), "Unable to move asset."));
        stepIndex = 0;
        renderStep();
    }

    private void applyMoveFailure(RuntimeException exception) {
        moving = false;
        feedbackLabel.setText(exception.getMessage() == null || exception.getMessage().isBlank()
                ? "Unable to move asset."
                : exception.getMessage());
        stepIndex = 0;
        renderStep();
    }

    private void browseForDestinationParent() {
        final DirectoryChooser chooser = new DirectoryChooser();
        chooser.setTitle(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_BROWSE_TITLE));
        chooser.setInitialDirectory(existingInitialBrowseDirectory().toFile());
        final File selected = chooser.showDialog(stage);
        if (selected == null) {
            return;
        }
        final Path selectedPath = selected.toPath().toAbsolutePath().normalize();
        if (!selectedPath.startsWith(assetsRoot())) {
            feedbackLabel.setText(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_ERROR_OUTSIDE_ASSETS));
            return;
        }
        destinationParentField.setText(relativeDestinationParent(selectedPath));
        targetRootField.setText(displayTargetRoot());
    }

    private boolean validateDestination(boolean showFeedback) {
        final String parent = normalizedDestinationParent();
        if (parent == null) {
            if (showFeedback) {
                feedbackLabel.setText(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_ERROR_PARENT));
            }
            return false;
        }
        final String name = normalizedDestinationName();
        if (name == null) {
            if (showFeedback) {
                feedbackLabel.setText(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_ERROR_NAME));
            }
            return false;
        }
        final String targetRoot = targetRelativeRoot();
        if (targetRoot == null) {
            if (showFeedback) {
                feedbackLabel.setText(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_ERROR_OUTSIDE_ASSETS));
            }
            return false;
        }
        if (targetRoot.equals(currentRelativeRoot())) {
            if (showFeedback) {
                feedbackLabel.setText(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_ERROR_TARGET_SAME));
            }
            return false;
        }
        if (targetManifestPath().toFile().isFile()) {
            if (showFeedback) {
                feedbackLabel.setText(Container.i18n().text(I18n.ASSETS_RELOCATE_WIZARD_ERROR_TARGET_ALREADY_ASSET));
            }
            return false;
        }
        if (showFeedback) {
            feedbackLabel.setText("");
        }
        targetRootField.setText(displayTargetRoot());
        return true;
    }

    private String initialDestinationParent() {
        return relativeDestinationParent(assetSummary.assetRoot().getParent());
    }

    private String relativeDestinationParent(Path parentPath) {
        final Path normalized = Objects.requireNonNull(parentPath, "parentPath").toAbsolutePath().normalize();
        if (normalized.equals(assetsRoot())) {
            return "";
        }
        return assetsRoot().relativize(normalized).toString().replace('\\', '/');
    }

    private Path existingInitialBrowseDirectory() {
        final String normalizedParent = normalizedDestinationParent();
        if (normalizedParent == null || normalizedParent.isBlank()) {
            return assetsRoot();
        }
        final Path candidate = assetsRoot().resolve(normalizedParent).toAbsolutePath().normalize();
        return candidate.startsWith(assetsRoot()) && candidate.toFile().isDirectory()
                ? candidate
                : assetsRoot();
    }

    private String normalizedDestinationParent() {
        final String raw = Objects.requireNonNullElse(destinationParentField.getText(), "").trim().replace('\\', '/');
        if (raw.isBlank()) {
            return "";
        }
        final Path normalized = Path.of(raw).normalize();
        if (normalized.isAbsolute() || normalized.startsWith("..")) {
            return null;
        }
        return normalized.toString().replace('\\', '/');
    }

    private String normalizedDestinationName() {
        final String raw = Objects.requireNonNullElse(destinationNameField.getText(), "").trim();
        if (raw.isBlank()) {
            return null;
        }
        final Path normalized = Path.of(raw).normalize();
        if (normalized.isAbsolute() || normalized.startsWith("..") || normalized.getNameCount() != 1) {
            return null;
        }
        final String value = normalized.toString().replace('\\', '/');
        if (value.isBlank() || value.contains("/")) {
            return null;
        }
        return value;
    }

    private String targetRelativeRoot() {
        final String parent = normalizedDestinationParent();
        final String name = normalizedDestinationName();
        if (parent == null || name == null) {
            return null;
        }
        return parent.isBlank() ? name : parent + "/" + name;
    }

    private String displayDestinationParent() {
        final String parent = normalizedDestinationParent();
        return parent == null || parent.isBlank() ? "." : parent;
    }

    private String displayTargetRoot() {
        final String targetRoot = targetRelativeRoot();
        return targetRoot == null ? "" : targetRoot;
    }

    private Path targetManifestPath() {
        final String targetRoot = Objects.requireNonNullElse(targetRelativeRoot(), "");
        return assetsRoot().resolve(targetRoot).resolve("asset.json").toAbsolutePath().normalize();
    }

    private String currentRelativeRoot() {
        return assetsRoot().relativize(assetSummary.assetRoot()).toString().replace('\\', '/');
    }

    private Path assetsRoot() {
        return projectReference.rootPath().resolve("assets").toAbsolutePath().normalize();
    }
}
