package p.studio.workspaces.assets.details.palette;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import p.studio.Container;
import p.studio.utilities.i18n.I18n;
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
import p.studio.workspaces.assets.metadata.AssetStudioGlyphMaterialSchema;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

public final class AssetDetailsPaletteWizardDialog {
    private final Stage stage = new Stage();
    private final ComboBox<AssetStudioGlyphMaterialSchema> schemaComboBox = new ComboBox<>();
    private final TextField nameField = new TextField();
    private final ObservableList<String> editableColors = FXCollections.observableArrayList();
    private final ObservableList<String> indexItems = FXCollections.observableArrayList();
    private final ListView<String> indexListView = new ListView<>(indexItems);
    private final ColorPicker colorPicker = new ColorPicker(Color.WHITE);
    private final Label summaryLabel = new Label();
    private final HBox originalStrip = new HBox(4);
    private final HBox editableStrip = new HBox(4);

    private PaletteEditResult result;
    private final String paletteId;
    private final boolean editing;

    private AssetDetailsPaletteWizardDialog(
            Window owner,
            AssetDetailsPaletteOverhaulingCoordinator coordinator,
            AssetWorkspaceBankCompositionFile paletteFile) {
        this.editing = paletteFile != null;
        this.paletteId = editing && paletteFile.metadata().get("paletteId") instanceof String id && !id.isBlank()
                ? id
                : coordinator.nextPaletteId();
        stage.initOwner(owner);
        stage.initModality(Modality.WINDOW_MODAL);
        stage.setTitle(Container.i18n().text(editing
                ? I18n.ASSETS_PALETTE_WIZARD_UPDATE_TITLE
                : I18n.ASSETS_PALETTE_WIZARD_CREATE_TITLE));

        final List<AssetStudioGlyphMaterialSchema> schemas = coordinator.readySchemas();
        schemaComboBox.getItems().setAll(schemas);
        schemaComboBox.setCellFactory(ignored -> new javafx.scene.control.ListCell<>() {
            @Override
            protected void updateItem(AssetStudioGlyphMaterialSchema item, boolean empty) {
                super.updateItem(item, empty);
                setText(empty || item == null ? null : item.schemaId());
            }
        });
        schemaComboBox.setButtonCell(schemaComboBox.getCellFactory().call(null));

        final AssetStudioGlyphMaterialSchema initialSchema = resolveInitialSchema(coordinator, paletteFile, schemas);
        if (initialSchema != null) {
            schemaComboBox.getSelectionModel().select(initialSchema);
        }
        editableColors.setAll(initialEditableColors(paletteFile, initialSchema));
        nameField.setText(initialDisplayName(paletteFile));
        nameField.setPromptText(AssetDetailsPaletteOverhaulingCoordinator.generatedDisplayName(editableColors));
        schemaComboBox.valueProperty().addListener((ignored, previous, current) -> {
            if (current == null) {
                editableColors.clear();
            } else if (!editing || previous == null || !Objects.equals(previous.schemaId(), current.schemaId())) {
                editableColors.setAll(current.materialIndices().entrySet().stream()
                        .sorted(Map.Entry.comparingByKey())
                        .map(Map.Entry::getValue)
                        .toList());
            }
            refreshPreview(coordinator);
        });
        nameField.textProperty().addListener((ignored, previous, current) -> refreshPreview(coordinator));
        indexListView.getSelectionModel().selectedIndexProperty().addListener((ignored, previous, current) -> {
            final int selected = current == null ? -1 : current.intValue();
            if (selected >= 0 && selected < editableColors.size()) {
                colorPicker.setValue(Color.web(editableColors.get(selected)));
            }
        });

        final Button applyColorButton = new Button(Container.i18n().text(I18n.ASSETS_PALETTE_WIZARD_APPLY_COLOR));
        applyColorButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
        applyColorButton.setOnAction(ignored -> {
            final int index = indexListView.getSelectionModel().getSelectedIndex();
            if (index < 0 || index >= editableColors.size()) {
                return;
            }
            editableColors.set(index, toHtmlColor(colorPicker.getValue()));
            refreshPreview(coordinator);
            indexListView.getSelectionModel().select(index);
        });

        final Button cancelButton = new Button(Container.i18n().text(I18n.FORM_ACTION_CANCEL));
        cancelButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
        cancelButton.setOnAction(ignored -> {
            result = null;
            stage.close();
        });
        final Button applyButton = new Button(Container.i18n().text(I18n.FORM_ACTION_APPLY));
        applyButton.getStyleClass().addAll("studio-button", "studio-button-primary");
        applyButton.setOnAction(ignored -> {
            final AssetStudioGlyphMaterialSchema selectedSchema = schemaComboBox.getSelectionModel().getSelectedItem();
            if (selectedSchema == null || coordinator.duplicatePaletteExists(paletteId, selectedSchema.schemaId(), editableColors)) {
                return;
            }
            result = new PaletteEditResult(paletteId, normalizedDisplayName(), selectedSchema.schemaId(), List.copyOf(editableColors));
            stage.close();
        });
        final Region spacer = new Region();
        HBox.setHgrow(spacer, Priority.ALWAYS);
        final HBox actions = new HBox(8, spacer, cancelButton, applyButton);
        actions.getStyleClass().add("assets-details-palette-wizard-actions");

        final Label schemaLabel = fieldLabel(I18n.ASSETS_PALETTE_WIZARD_SCHEMA);
        final Label nameLabel = fieldLabel(I18n.ASSETS_PALETTE_WIZARD_NAME);
        final Label originalLabel = fieldLabel(I18n.ASSETS_PALETTE_WIZARD_ORIGINAL);
        final Label editableLabel = fieldLabel(I18n.ASSETS_PALETTE_WIZARD_EDITABLE);
        final VBox colorEditor = new VBox(8, colorPicker, applyColorButton);
        colorEditor.getStyleClass().add("assets-details-palette-wizard-color-editor");
        final HBox editorBody = new HBox(12, indexListView, colorEditor);
        editorBody.getStyleClass().add("assets-details-palette-wizard-editor");
        final VBox root = new VBox(
                12,
                nameLabel,
                nameField,
                schemaLabel,
                schemaComboBox,
                originalLabel,
                originalStrip,
                editableLabel,
                editableStrip,
                editorBody,
                summaryLabel,
                actions);
        root.setPadding(new Insets(16));
        root.getStyleClass().add("assets-details-palette-wizard");
        originalStrip.getStyleClass().add("assets-details-palette-wizard-strip");
        editableStrip.getStyleClass().add("assets-details-palette-wizard-strip");
        summaryLabel.getStyleClass().add("assets-details-palette-wizard-summary");
        HBox.setHgrow(indexListView, Priority.ALWAYS);
        HBox.setHgrow(schemaComboBox, Priority.ALWAYS);
        HBox.setHgrow(nameField, Priority.ALWAYS);
        refreshPreview(coordinator);
        final Scene scene = new Scene(root, 620, 570);
        scene.getStylesheets().add(Container.theme().getDefaultTheme());
        stage.setScene(scene);
        stage.setOnShown(ignored -> stage.centerOnScreen());
    }

    public static Optional<PaletteEditResult> showAndWait(
            Window owner,
            AssetDetailsPaletteOverhaulingCoordinator coordinator,
            AssetWorkspaceBankCompositionFile paletteFile) {
        final AssetDetailsPaletteWizardDialog dialog = new AssetDetailsPaletteWizardDialog(owner, coordinator, paletteFile);
        dialog.stage.showAndWait();
        return Optional.ofNullable(dialog.result);
    }

    private Label fieldLabel(I18n label) {
        final Label node = new Label(Container.i18n().text(label));
        node.getStyleClass().add("assets-details-palette-wizard-label");
        return node;
    }

    private AssetStudioGlyphMaterialSchema resolveInitialSchema(
            AssetDetailsPaletteOverhaulingCoordinator coordinator,
            AssetWorkspaceBankCompositionFile paletteFile,
            List<AssetStudioGlyphMaterialSchema> schemas) {
        if (paletteFile != null && paletteFile.metadata().get("schemaId") instanceof String schemaId) {
            return coordinator.readySchemas().stream()
                    .filter(schema -> schema.schemaId().equals(schemaId))
                    .findFirst()
                    .orElse(null);
        }
        return schemas.isEmpty() ? null : schemas.getFirst();
    }

    private List<String> initialEditableColors(
            AssetWorkspaceBankCompositionFile paletteFile,
            AssetStudioGlyphMaterialSchema initialSchema) {
        if (paletteFile != null) {
            @SuppressWarnings("unchecked")
            final Map<String, Object> palette = (Map<String, Object>) paletteFile.metadata().get("palette");
            final Object original = palette == null ? null : palette.get("originalArgb8888");
            if (original instanceof Iterable<?> iterable) {
                final List<String> colors = new ArrayList<>();
                for (Object value : iterable) {
                    if (value instanceof Number number) {
                        colors.add(String.format("#%06X", number.intValue() & 0x00FFFFFF));
                    }
                }
                return List.copyOf(colors);
            }
        }
        if (initialSchema == null) {
            return List.of();
        }
        return initialSchema.materialIndices().entrySet().stream()
                .sorted(Map.Entry.comparingByKey())
                .map(Map.Entry::getValue)
                .toList();
    }

    private String initialDisplayName(AssetWorkspaceBankCompositionFile paletteFile) {
        if (paletteFile == null) {
            return "";
        }
        final Object explicitName = paletteFile.metadata().get("explicitPaletteDisplayName");
        return explicitName instanceof String value ? value : "";
    }

    private void refreshPreview(AssetDetailsPaletteOverhaulingCoordinator coordinator) {
        final AssetStudioGlyphMaterialSchema schema = schemaComboBox.getSelectionModel().getSelectedItem();
        final List<String> originalColors = schema == null
                ? List.of()
                : schema.materialIndices().entrySet().stream()
                        .sorted(Map.Entry.comparingByKey())
                        .map(Map.Entry::getValue)
                        .toList();
        originalStrip.getChildren().setAll(originalColors.stream().map(this::swatch).toList());
        editableStrip.getChildren().setAll(editableColors.stream().map(this::swatch).toList());
        nameField.setPromptText(AssetDetailsPaletteOverhaulingCoordinator.generatedDisplayName(editableColors));
        indexItems.setAll(java.util.stream.IntStream.range(0, editableColors.size())
                .mapToObj(index -> (index + 1) + " -> " + editableColors.get(index))
                .toList());
        final boolean duplicate = schema != null && coordinator.duplicatePaletteExists(paletteId, schema.schemaId(), editableColors);
        summaryLabel.setText(duplicate
                ? Container.i18n().text(I18n.ASSETS_PALETTE_WIZARD_DUPLICATE)
                : Container.i18n().text(I18n.ASSETS_PALETTE_WIZARD_READY));
    }

    private String normalizedDisplayName() {
        final String value = nameField.getText();
        return value == null || value.trim().isBlank()
                ? AssetDetailsPaletteOverhaulingCoordinator.generatedDisplayName(editableColors)
                : value.trim();
    }

    private Region swatch(String color) {
        final Region swatch = new Region();
        swatch.setMinSize(22, 22);
        swatch.setPrefSize(22, 22);
        swatch.setMaxSize(22, 22);
        swatch.getStyleClass().add("assets-details-palette-wizard-swatch");
        swatch.setStyle("-fx-background-color: " + color + ";");
        return swatch;
    }

    private String toHtmlColor(Color color) {
        return String.format(
                "#%02X%02X%02X",
                (int) Math.round(color.getRed() * 255),
                (int) Math.round(color.getGreen() * 255),
                (int) Math.round(color.getBlue() * 255));
    }

    public record PaletteEditResult(
            String paletteId,
            String displayName,
            String schemaId,
            List<String> colors) {
    }
}
