package p.studio.window;

import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import p.studio.Container;
import p.studio.projects.KnownProjectsService;
import p.studio.projects.ProjectCatalogService;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;

import java.io.File;
import java.nio.file.Path;
import java.util.Objects;
import java.util.function.Consumer;

public final class ProjectLauncherView extends BorderPane {
    private final KnownProjectsService knownProjectsService;
    private final ProjectCatalogService projectCatalogService;
    private final Consumer<ProjectReference> onOpenProject;
    private final Consumer<ProjectReference> onAddProject;
    private final Consumer<ProjectReference> onCreateProject;
    private final Consumer<ProjectReference> onForgetProject;
    private final ListView<ProjectReference> projectList = new ListView<>();
    private final Label feedbackLabel = new Label();

    public ProjectLauncherView(
            KnownProjectsService knownProjectsService,
            ProjectCatalogService projectCatalogService,
            Consumer<ProjectReference> onOpenProject,
            Consumer<ProjectReference> onAddProject,
            Consumer<ProjectReference> onCreateProject,
            Consumer<ProjectReference> onForgetProject) {
        this.knownProjectsService = Objects.requireNonNull(knownProjectsService, "knownProjectsService");
        this.projectCatalogService = Objects.requireNonNull(projectCatalogService, "projectCatalogService");
        this.onOpenProject = Objects.requireNonNull(onOpenProject, "onOpenProject");
        this.onAddProject = Objects.requireNonNull(onAddProject, "onAddProject");
        this.onCreateProject = Objects.requireNonNull(onCreateProject, "onCreateProject");
        this.onForgetProject = Objects.requireNonNull(onForgetProject, "onForgetProject");

        getStyleClass().add("studio-project-launcher");
        setPadding(new Insets(16));
        setCenter(buildCenteredContent());

        reloadProjects();
    }

    private StackPane buildCenteredContent() {
        final VBox content = buildContent();
        final StackPane wrapper = new StackPane(content);
        wrapper.setPadding(new Insets(8, 12, 8, 12));
        StackPane.setAlignment(content, Pos.CENTER);
        return wrapper;
    }

    private VBox buildContent() {
        final Label title = new Label();
        title.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_TITLE));
        title.getStyleClass().add("studio-launcher-title");

        final Label subtitle = new Label();
        subtitle.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_SUBTITLE));
        subtitle.getStyleClass().add("studio-launcher-subtitle");

        final Label listTitle = new Label();
        listTitle.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_EXISTING_PROJECTS));
        listTitle.getStyleClass().add("studio-launcher-section-title");

        projectList.getStyleClass().add("studio-project-list");
        projectList.setCellFactory(ignored -> new ProjectReferenceListCell());
        projectList.setOnMouseClicked(event -> {
            if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) {
                openSelectedProject();
            }
        });
        VBox.setVgrow(projectList, Priority.ALWAYS);

        final Button openButton = new Button();
        openButton.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_OPEN_PROJECT));
        openButton.getStyleClass().addAll("studio-button", "studio-button-primary");
        openButton.disableProperty().bind(projectList.getSelectionModel().selectedItemProperty().isNull());
        openButton.setOnAction(ignored -> openSelectedProject());

        final Button addButton = new Button();
        addButton.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_ADD_PROJECT));
        addButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
        addButton.setOnAction(ignored -> addExistingProject());

        final Button forgetButton = new Button();
        forgetButton.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_FORGET_PROJECT));
        forgetButton.getStyleClass().addAll("studio-button", "studio-button-warning");
        forgetButton.disableProperty().bind(projectList.getSelectionModel().selectedItemProperty().isNull());
        forgetButton.setOnAction(ignored -> forgetSelectedProject());

        final HBox openRow = new HBox(10, openButton, addButton, forgetButton);
        openRow.setAlignment(Pos.CENTER_LEFT);

        final Label createTitle = new Label();
        createTitle.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_CREATE_PROJECT));
        createTitle.getStyleClass().add("studio-launcher-section-title");

        final Button createButton = new Button();
        createButton.textProperty().bind(Container.i18n().bind(I18n.LAUNCHER_CREATE_BUTTON));
        createButton.getStyleClass().addAll("studio-button", "studio-button-primary");
        createButton.setOnAction(ignored -> openCreateWizard());

        final HBox createRow = new HBox(10, createButton);
        createRow.setAlignment(Pos.CENTER_LEFT);

        feedbackLabel.getStyleClass().add("studio-launcher-feedback");
        feedbackLabel.setWrapText(true);

        final VBox content = new VBox(12,
                title,
                subtitle,
                listTitle,
                projectList,
                openRow,
                createTitle,
                createRow,
                feedbackLabel);
        content.setMaxWidth(520);
        return content;
    }

    public void reloadProjects() {
        projectList.setItems(FXCollections.observableArrayList(knownProjectsService.listKnownProjects()));
        if (!projectList.getItems().isEmpty()) {
            projectList.getSelectionModel().selectFirst();
        }
    }

    private void openSelectedProject() {
        final ProjectReference selected = projectList.getSelectionModel().getSelectedItem();
        if (selected == null) {
            feedbackLabel.textProperty().unbind();
            feedbackLabel.setText("");
            return;
        }

        feedbackLabel.setText("");
        onOpenProject.accept(selected);
    }

    private void forgetSelectedProject() {
        final ProjectReference selected = projectList.getSelectionModel().getSelectedItem();
        if (selected == null) {
            feedbackLabel.setText("");
            return;
        }

        feedbackLabel.textProperty().unbind();
        feedbackLabel.setText("");
        onForgetProject.accept(selected);
    }

    private void openCreateWizard() {
        NewProjectWizard.showAndWait(getScene().getWindow(), projectCatalogService).ifPresent(created -> {
            reloadProjects();
            projectList.getSelectionModel().select(created);
            feedbackLabel.textProperty().unbind();
            feedbackLabel.setText("");
            onCreateProject.accept(created);
        });
    }

    private void addExistingProject() {
        final FileChooser chooser = new FileChooser();
        chooser.setTitle(Container.i18n().text(I18n.LAUNCHER_ADD_PROJECT));
        chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Prometeu Project", "prometeu.json"));
        chooser.setInitialFileName("prometeu.json");
        final File initialDirectory = projectCatalogService.projectsRoot().toFile();
        if (initialDirectory.isDirectory()) {
            chooser.setInitialDirectory(initialDirectory);
        }

        final File selected = chooser.showOpenDialog(getScene().getWindow());
        if (selected == null) {
            return;
        }

        try {
            final ProjectReference project = projectCatalogService.openProjectManifest(Path.of(selected.toURI()));
            onAddProject.accept(project);
            reloadProjects();
            projectList.getSelectionModel().select(project);
            feedbackLabel.textProperty().unbind();
            feedbackLabel.setText("");
        } catch (IllegalArgumentException exception) {
            feedbackLabel.textProperty().unbind();
            feedbackLabel.setText(exception.getMessage());
        }
    }

    private static final class ProjectReferenceListCell extends ListCell<ProjectReference> {
        private final Label nameLabel = new Label();
        private final Label metadataLabel = new Label();
        private final Label pathLabel = new Label();
        private final VBox content = new VBox(3, nameLabel, metadataLabel, pathLabel);

        private ProjectReferenceListCell() {
            nameLabel.getStyleClass().add("studio-project-list-name");
            metadataLabel.getStyleClass().add("studio-project-list-metadata");
            pathLabel.getStyleClass().add("studio-project-list-path");
            metadataLabel.setWrapText(true);
            pathLabel.setWrapText(true);
        }

        @Override
        protected void updateItem(ProjectReference item, boolean empty) {
            super.updateItem(item, empty);
            if (empty || item == null) {
                setText(null);
                setGraphic(null);
                return;
            }

            nameLabel.setText(item.name());
            metadataLabel.setText("v%s  |  %s  |  stdlib %d".formatted(
                    item.version(),
                    item.languageId(),
                    item.stdlibVersion()));
            pathLabel.setText(item.rootPath().toString());
            setText(null);
            setGraphic(content);
        }
    }
}
