/** @module others/renderSortMenu */
/* global rmlOpts */

import $ from "jquery";
import { Provider, inject, observer } from "mobx-react";
import { getSnapshot } from "mobx-state-tree";
import React from "react";

import { Menu, message } from "@devowl-wp/react-folder-tree";

import FolderBox, { FolderBoxHeader } from "../components/FolderBox.js";
import { FolderSelector } from "../components/FolderSelector.js";
import { DashIcon, Modal } from "../components/index.js";
import { BasicStore, createUnorganizedNode } from "../store/index.js";
import { IS_DARKMODE, getMediaDialogNextZIndex, hooks, i18n, textDots } from "../util/index.js";

const { Item, SubMenu, Divider, ItemGroup } = Menu;

if (process.env.PLUGIN_CTX === "pro") {
    /* onlypro:start */
    hooks
        .register("folder/children/order/name_asc", (children, setChildren) => {
            setChildren(children.sort((a, b) => a.title.localeCompare(b.title)));
        })
        .register("folder/children/order/name_desc", (children, setChildren) => {
            setChildren(children.sort((a, b) => a.title.localeCompare(b.title) * -1));
        })
        .register("folder/children/order/id_asc folder/children/order/original", (children, setChildren) => {
            setChildren(children.sort((a, b) => (a.id > b.id ? 1 : b.id > a.id ? -1 : 0)));
        })
        .register("folder/children/order/id_desc", (children, setChildren) => {
            setChildren(children.sort((a, b) => (a.id > b.id ? -1 : b.id > a.id ? 1 : 0)));
        });
    /* onlypro:end */
}

/**
 * An element rendering sortables for the popup menu.
 *
 * @returns React.Element[]
 */
const createSortables = (sortables, select, selectText) =>
    Object.keys(sortables).map((key) => (
        <Item key={key}>
            {sortables[key]} {select === key && <strong>({selectText})</strong>}
        </Item>
    ));

/**
 * Apply an order to a tree node object.
 *
 * @this AppTree
 */
async function applyOrder(selected, key, automatically) {
    if (!rmlOpts.others.isPro && rmlOpts.others.showProHints) {
        this.setState({ showProFeature: "order-subfolders" });
    } else {
        const hide = message.loading(i18n("sortLoadingText", { name: selected.title }));
        await selected.applyChildrenOrder(key, automatically);
        hide();
    }
}

/**
 * When clicking on a menu item in the order menu popup.
 *
 * @this AppTree
 */
async function handleClick({ key, keyPath }) {
    const path = keyPath.reverse();
    const { selectedId, selected } = this.props.store;
    const _applyOrder = applyOrder.bind(this);
    if (path[0] === "manual") {
        this.setState({ rearrangeBoxId: selectedId });
    } else if (path[0] === "applyOnce") {
        _applyOrder(selected, key);
    } else if (path[0] === "applyAutomatically") {
        _applyOrder(selected, key, true);
    } else if (key === "reset") {
        _applyOrder(selected, "original");
    } else if (key === "resetAutomatically") {
        _applyOrder(selected, "deactivate");
    } else if (key === "applyReindex") {
        _applyOrder(selected, "reindex");
    }
}

/**
 * Render the order menu.
 *
 * @type React.Element
 */
export default function () {
    const { store } = this.props;
    const { selectedId, selected } = store;
    const sortables = store.treeSortables;
    const isFolder = selectedId > 0;

    return isFolder ? (
        <Menu onClick={handleClick.bind(this)} theme={IS_DARKMODE ? "dark" : "light"}>
            <Item key="manual">{i18n("sortByManual", { name: textDots(selected.title) })}</Item>
            <Divider />
            <ItemGroup
                title={
                    <span>
                        {i18n("subfolders")} ({textDots(selected.title)})
                    </span>
                }
            >
                {!!selected.lastSubOrderBy && <Item key="reset">{i18n("resetOrder")}</Item>}
                {!selected.subOrderAutomatically ? (
                    <SubMenu key="applyOnce" title={i18n("applyOrderOnce")}>
                        {sortables && createSortables(sortables, selected.lastSubOrderBy, i18n("last"))}
                    </SubMenu>
                ) : (
                    <Item key="resetAutomatically">{i18n("deactivateOrderAutomatically")}</Item>
                )}
                <SubMenu key="applyAutomatically" title={i18n("applyOrderAutomatically")}>
                    {sortables &&
                        createSortables(
                            sortables,
                            selected.subOrderAutomatically && selected.lastSubOrderBy,
                            i18n("latest"),
                        )}
                </SubMenu>
                {!!selected.lastSubOrderBy && <Item key="applyReindex">{i18n("reindexOrder")}</Item>}
            </ItemGroup>
        </Menu>
    ) : (
        <span />
    );
}

/**
 * Show a rearrange box for the selected folder id.
 *
 * @property {int} id The id of the folder
 * @extends React.Component
 */
@inject("store")
@observer
class RearrangeBox extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            nextId: undefined,
            loading: false,
        };

        this.parentStore = BasicStore.create({
            staticTree: [createUnorganizedNode()],
        });

        // Create sibling store
        this.siblingStore = BasicStore.create({
            staticTree: [createUnorganizedNode()],
        });
    }

    /**
     * Create the sibling tree.
     */
    handleSelect = (parent) => {
        const { siblingStore } = this;
        const tree = $.extend(true, [], getSnapshot(parent ? parent.childNodes : this.props.store.tree));
        tree.forEach((n) => (n.childNodes = []));
        siblingStore.setTree(tree);
        const { selected } = siblingStore;
        selected && selected.setter((n) => (n.selected = false)); // Reset selection
    };

    handleSave = async () => {
        this.setState({ loading: true });

        try {
            const { store } = this.props;
            const { id } = this.item;
            const { parentStore, siblingStore } = this;
            const oldItem = store.getTreeItemById(id);
            const props = {
                id,
                oldIndex: oldItem.parentArray.indexOf(oldItem),
                parentFromId: oldItem.parentId,
                parentToId: parentStore.selected ? parentStore.selectedId : store.rootId,
                nextId: siblingStore.selectedId || NaN,
                doFinally: false,
            };

            // Get newIndex
            const parentTo = store.getTreeItemById(props.parentToId);
            const newIndexRelative = parentTo ? parentTo.childNodes : store.tree;
            const newIndexA = newIndexRelative
                .filter(({ id }) => id !== oldItem.id)
                .map(({ id }, idx) => (id === props.nextId ? `${idx}` : null))
                .filter(Boolean)
                .map((i) => +i);
            props.newIndex = !isNaN(props.nextId) && newIndexA.length ? newIndexA[0] : newIndexRelative.length;

            // Do the sorting
            this.props.onSort(props);
        } finally {
            this.setState({ loading: false });
        }
    };

    /**
     * Save the editing item to the instance and prepare the first sibling tree.
     */
    handleNodeInit = (itemId) => {
        const { nextSibling, parentArray } = (this.item = this.parentStore.getTreeItemById(itemId, false));
        const id = nextSibling ? nextSibling.id : undefined;
        this.setState({ nextId: id }, () => {
            const tree = $.extend(true, [], getSnapshot(parentArray));
            const { siblingStore, parentStore } = this;
            tree.forEach((n) => (n.childNodes = []));
            siblingStore.setTree(tree);
            parentStore.getTreeItemById(this.item.id).setter((n) => (n.$visible = false));
            siblingStore.getTreeItemById(this.item.id).setter((n) => (n.$visible = false));
            id !== undefined && siblingStore.getTreeItemById(id).setter((n) => (n.selected = true));
        });
    };

    render() {
        let modalContent;
        let title;
        const selected = this.props.id && this.props.store.getTreeItemById(this.props.id, false);
        if (selected) {
            const selectedId = selected.parentId > 0 ? selected.parentId : "";
            title = <FolderBoxHeader icon={<DashIcon name="sort" />} folder={selected} />;
            modalContent = (
                <FolderBox folder={selected} header={false}>
                    <form>
                        <h3>{i18n("parent")}</h3>
                        <Provider tree={this.parentStore}>
                            <FolderSelector
                                selected={selectedId}
                                nullable
                                disabled="4"
                                onSelect={this.handleSelect}
                                onNodeInit={(...args) => this.handleNodeInit(this.props.id, ...args)}
                            />
                        </Provider>
                        <h3>{i18n("beforeThisNode")}</h3>
                        <Provider tree={this.siblingStore}>
                            <FolderSelector fetch={false} nullable disabled="4" ignoreChildNodes />
                        </Provider>
                        <p className="description">{i18n("beforeThisNodeInfo")}</p>
                    </form>
                </FolderBox>
            );
        }

        return (
            <Modal
                visible={!!modalContent}
                title={title}
                onOk={this.handleSave}
                onCancel={this.props.onClose}
                cancelText={i18n("cancel")}
                okText={i18n("save")}
                zIndex={getMediaDialogNextZIndex()}
                confirmLoading={this.state.loading}
            >
                {modalContent}
            </Modal>
        );
    }
}

export { RearrangeBox };
