/** @module components/MetaBox */

import $ from "jquery";
import { inject, observer } from "mobx-react";
import { Component, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";

import { Icon } from "@devowl-wp/react-folder-tree";

import FolderBox, { FolderBoxHeader } from "./FolderBox.js";
import { Modal } from "./index.js";
import { MediaLibrarySelector } from "./MediaLibrarySelector.js";
import { getMediaDialogNextZIndex, hooks, i18n, request, rmlOpts } from "../util/index.js";

/**
 * Show a meta box for the selected folder id. It also supports
 * user settings.
 *
 * @property {string|int} id The id of the folder or 'usersettings'
 * @extends Component
 */
@inject("store")
@observer
class MetaBox extends Component {
    constructor(props) {
        super(props);

        this.state = {
            id: 0, // The current visible id
            html: "", // The html
            loading: false,
            errors: [],
        };
    }

    componentDidUpdate() {
        const { id } = this.props;
        if (id !== this.state.id) {
            this.setState({ id, html: "" });

            if (id !== false) {
                request({
                    location: {
                        path: id === "usersettings" ? "/usersettings" : `/folders/${id}/meta`,
                    },
                }).then(
                    ({ html }) => {
                        this.setState({ html });
                    },
                    () => {
                        // An error occured
                        this.setState({
                            html: "",
                        });
                    },
                );
            }
        }
    }

    handleRef = (ref) => {
        this.refSpan = ref;

        /**
         * The MetaBox ref element is ready and created.
         *
         * @event module:util/hooks#folder/meta
         * @param {HTMLElement} ref The reference
         * @param {string|id} id The folder id or 'usersettings'
         * @param {module:store~Store} store The store
         * @this MetaBox instance
         */
        hooks.call("folder/meta", [ref, this.state.id, this.props.store], this);
    };

    handleSave = async () => {
        const form = $(this.refSpan).children("form");
        const serialize = form.serializeArray();
        const data = {};
        $.each(serialize, (key, value) => (data[value.name] = value.value));

        /**
         * The MetaBox is serialized and ready to send.
         *
         * @event module:util/hooks#folder/meta/serialize
         * @param {string|id} id The folder id or 'usersettings'
         * @param {module:store~Store} store The store
         * @param {object} data The data prepared for the server so you can perhaps modify it
         * @param {HTMLElement} form The form container
         * @this MetaBox instance
         */
        hooks.call("folder/meta/serialize", [this.state.id, this.props.store, data, form], this);
        try {
            const response = await request({
                location: {
                    path: this.state.id === "usersettings" ? "/usersettings" : `/folders/${this.state.id}/meta`,
                    method: "PUT",
                },
                request: data,
            });

            /**
             * The MetaBox is saved successfully.
             *
             * @event module:util/hooks#folder/meta/saved
             * @param {string|id} id The folder id or 'usersettings'
             * @param {object} response The server response
             * @param {object} data The data sent to the server
             * @this MetaBox instance
             */
            hooks.call("folder/meta/saved", [this.state.id, response, data], this);

            this.props.onClose(true, response);
        } catch ({ responseJSON: { message } }) {
            this.setState({ errors: message });
        } finally {
            this.setState({ loading: false });
        }
    };

    render() {
        let selected;
        let modalContent;
        let title;
        if (this.props.id === "usersettings") {
            selected = {
                icon: <Icon type="setting" />,
                title: rmlOpts.others.lang.userSettingsToolTipTitle,
            };
        } else {
            selected = this.props.store.getTreeItemById(this.props.id, false);
        }

        const { html, loading, errors } = this.state;
        if (selected) {
            title = <FolderBoxHeader icon={<Icon type="ellipsis" />} folder={selected} />;
            modalContent = (
                <FolderBox busy={!html || loading} folder={selected} errors={errors} header={false}>
                    {html && (
                        <div className="inside">
                            <span
                                dangerouslySetInnerHTML={{ __html: html }}
                                style={{ display: html ? "block" : "none" }}
                                ref={this.handleRef}
                            />
                        </div>
                    )}
                </FolderBox>
            );
        }

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

const CoverImageSelector = ({ input, modal }) => {
    const [attachmentId, setAttachmentId] = useState(input.val());

    useEffect(() => {
        input.val(attachmentId);
    }, [attachmentId]);

    return (
        <MediaLibrarySelector
            attachmentId={attachmentId}
            onChange={(id) => setAttachmentId(id)}
            title={"Select cover image"}
            allowedTypes={["image"]}
            render={({ open, reset, attachmentId, url }) => (
                <>
                    <p style={{ marginTop: 0 }}>
                        <a className="button" onClick={open}>
                            {attachmentId ? "Replace image" : "Select from media library"}
                        </a>
                        &nbsp;
                        {attachmentId && (
                            <a className="button" onClick={reset}>
                                {"Remove image"}
                            </a>
                        )}
                    </p>
                    {url && <img style={{ maxWidth: "100%", height: "auto" }} src={url} />}
                </>
            )}
            ref={(ref) => {
                if (ref?.frame) {
                    // Legacy WordPress versions
                    ref.frame.on("open", () => modal.hide()).on("close", () => modal.show());
                } else if (ref) {
                    // Newer WordPress versions (idk which version, needs more investigation into git blame)
                    const { onOpen, onClose } = ref;
                    ref.onOpen = function () {
                        onOpen.apply(this, arguments);
                        modal.hide();
                    }.bind(ref);
                    ref.onClose = function () {
                        onClose.apply(this, arguments);
                        modal.show();
                    }.bind(ref);
                }
            }}
        />
    );
};

/**
 * Wait for the input field for the cover image and create a media picker.
 *
 * @see https://wordpress.stackexchange.com/questions/190987/how-do-i-create-a-custom-add-media-button-modal
 */
hooks.register("wprfc/metaCoverImage", function () {
    const input = $(this).hide();
    const modal = $(this).parents(".ant-modal-wrap").parent();
    createRoot($("<div />").css("margin-top", "5px").insertAfter($(this)).get(0)).render(
        <CoverImageSelector input={input} modal={modal} />,
    );
});

export default MetaBox;
