/** @module components/FolderSelector */

import classNames from "classnames";
import $ from "jquery";
import { Observer, Provider, inject, observer } from "mobx-react";
import { Component, Fragment } from "react";
import { createRoot } from "react-dom/client";
import rmlOpts from "rmlopts";

import { Spin, Tree } from "@devowl-wp/react-folder-tree";

import { Modal } from ".//index.js";
import Breadcrumb from "./Breadcrumb.js";
import { BasicStore, createAllNode, createUnorganizedNode } from "../store/index.js";
import { i18n, resolveIcon } from "../util/index.js";

@inject("tree")
@observer
class FolderSelector extends Component {
    static defaultProps = {
        fetch: true, // Set to false if you want to set the selectable tree by yourself (store)
        nullable: false,
        editable: true,
        onSelect: undefined,
        onNodeInit: undefined,
        onFetchTree: undefined,
        ignoreChildNodes: false,
        before: undefined,
        after: undefined,
        title: undefined,
        selected: undefined, // Use this only without input
        input: undefined, // Input is optional, use selected for initial selected id
        disabled: "", // comma seperated list of types which are disabled
    };

    constructor({ input, disabled, selected }) {
        super(...arguments);

        this.inputObject = input || document.createElement("input");

        // Initial selection
        selected !== undefined && (this.inputObject.value = selected);

        this.state = {
            isTreeBusy: false,
            visible: false,
            disabled: (disabled ? `${disabled}`.split(",") : []).map((i) => +i),
        };
    }

    /**
     * Set the selected id to the input field so it can be updated through the properties.
     */
    componentDidUpdate(prevProps) {
        const { input, selected } = this.props;
        if (!input && selected !== prevProps.selected) {
            this.handleSelect(prevProps.selected);
        }
    }

    /**
     * Initially set the value of the input field and breadcrumb path.
     *
     * @internal
     */
    async componentDidMount() {
        const val = this.getInputValue();
        const { tree, onFetchTree } = this.props;
        if (!this.props.nullable && (!val || val < 0)) {
            this.inputObject.value = -1;
        }

        this.setState({ isTreeBusy: true });
        if (this.props.fetch) {
            try {
                // Check if global rml store holds already a tree
                const mainStore = this.getMainStore();
                if (mainStore) {
                    tree.setTree(mainStore.getTreeSnapshot(), false, mainStore.slugs);
                } else {
                    await tree.fetchTree();
                }
                onFetchTree && onFetchTree(tree);
            } catch (e) {
                console.log(e);
            }
        }

        // Disable nodes
        const { disabled } = this.state;
        if (disabled.length) {
            const fnDisabled = (node) => {
                if (disabled.indexOf(node.properties && +node.properties.type) > -1) {
                    node.setter((n) => (n.className = "rml-deactivated"));
                }
            };
            tree.nodes(fnDisabled);
            tree.nodes(fnDisabled, true);
        }

        // Modify this tree
        this.setState({ isTreeBusy: false });

        // Preselect
        const item = this.handleSelect(val);
        this.props.onNodeInit && this.props.onNodeInit(item, this.inputObject);
    }

    getInputValue = () => {
        const val = this.inputObject.value;
        return val === "" || val === "all" ? "" : +val;
    };

    getMainStore = () => /* global rml */ rml.store.tree.length && rml.store;

    /**
     * A node item should be an observer (mobx).
     */
    onTreeNodeRender = (createTreeNode, TreeNode, node) => {
        return <Observer key={node.id}>{() => createTreeNode(node)}</Observer>;
    };

    /**
     * A node item icon is present as string.
     */
    onTreeNodeRenderIcon = (icon) => resolveIcon(icon);

    handleSelect = (id) => {
        let item;
        const previousValue = this.getInputValue();
        if (id === "") {
            // nullable
            item = this.props.tree.selected;
            item && item.setter((node) => (node.selected = false));
            this.props.tree.setter((t) => (t.selectedId = undefined)); // Reset manually cause it can not listen when importing from snapshot
            item = null;
        } else {
            item = this.props.tree.getTreeItemById(id, false);

            if (!item) {
                return this.handleSelect(+rmlOpts.others.rootId);
            } else {
                item.setter((node) => (node.selected = true));
            }
        }

        this.inputObject.value = id;
        $(this.inputObject).data("node", item).trigger("folderSelected");
        this.setState({ visible: false });
        previousValue !== id && this.props.onSelect && this.props.onSelect(item, this.inputObject);
        return item;
    };

    handleVisibleChange = () => {
        let visible = !this.state.visible;
        if (this._nullableNextVisibleChange) {
            visible = false;
        }
        this.setState({ visible });
        this._nullableNextVisibleChange = false;
    };

    handleSelectNull = () => {
        this._nullableNextVisibleChange = true;
        this.handleSelect("");
    };

    render() {
        const { isTreeBusy, visible } = this.state;
        const {
            className,
            style,
            editable,
            title,
            nullable,
            tree: { breadcrumb, selected, staticTree, tree },
            ignoreChildNodes,
            children,
            before,
            after,
        } = this.props;
        return (
            <Fragment>
                <Modal
                    key="modal"
                    visible={visible && editable}
                    wrapClassName="rml-folder-selector"
                    okText={i18n("ok")}
                    cancelText={i18n("cancel")}
                    title={title}
                    onCancel={this.handleVisibleChange}
                >
                    <Fragment>
                        <Tree
                            rootId={+rmlOpts.others.rootId}
                            staticTree={staticTree.filter(({ id }) => id !== "all")}
                            tree={tree.length > 0 ? tree : []}
                            isResizable={false}
                            isFullWidth
                            creatable={{ buttons: {} }}
                            toolbar={{ buttons: {} }}
                            onSelect={this.handleSelect}
                            renderItem={this.onTreeNodeRender}
                            renderIcon={this.onTreeNodeRenderIcon}
                            headline={<span style={{ paddingRight: 5 }}>{i18n("folders")}</span>}
                            noFoldersTitle={i18n("noFoldersTitle")}
                            ignoreChildNodes={ignoreChildNodes}
                            noFoldersDescription=""
                            noSearchResult={i18n("noSearchResult")}
                            theme="wordpress"
                            autoFocusSearchInput
                            {...this.state}
                        />
                        {children}
                    </Fragment>
                </Modal>
                {before}
                <div
                    key="breadcrumb"
                    className={classNames("rml-folder-edit", className, {
                        "rml-deactivated": !editable,
                    })}
                    style={style}
                    onClick={this.handleVisibleChange}
                >
                    <Spin spinning={isTreeBusy}>
                        <Breadcrumb
                            path={breadcrumb}
                            closeable={selected && editable && nullable}
                            onClose={this.handleSelectNull}
                        />
                    </Spin>
                </div>
                {after}
            </Fragment>
        );
    }
}

export { FolderSelector };

/**
 * Create the folder selector in a given target.
 *
 * @param {HTMLElement} target
 * @param {HTMLElement} input An input where the selected id is stored (usually input[type="hidden"])
 * @param {object} props The properties for FolderSelector control
 * @param {object} store
 * @returns {object} The created store
 */
export default function createFolderSelector(target, input, props, useStore) {
    const store =
        useStore ||
        BasicStore.create({
            staticTree: [{ ...createAllNode(), $visible: false }, createUnorganizedNode()],
        });

    createRoot(target).render(
        <Provider tree={store}>
            <FolderSelector input={input} {...props} />
        </Provider>,
    );

    return store;
}
