import $ from "jquery";
import { createRoot } from "react-dom/client";

import { getSidebarCustomize } from "./getSidebarCustomize.js";
import { ControlFieldset } from "../components/controlFieldset.js";
import { CustomControlContextFactory } from "../contexts/customControlContextFactory.js";

import type { FC } from "react";

/**
 * Compatibility with "Customizr" theme as they are using icheck which is not compatible
 * with React. And the below class is the class which Customizr already excludes.
 *
 * @see https://github.com/dargullin/icheck
 */
const CUSTOM_CONTROL_INPUT_CHECKBOX_CLASS_NAME = "nimblecheck-input";

/**
 * Dynamically create a control for each registered custom control. This view should not be
 * used by you directly, instead use `customControl` function.
 *
 * Unfortunately we can not a lot of TypeScript because WordPress does not provide typings... :-/
 */
function customControlBackboneView(Component: FC) {
    const customize = getSidebarCustomize();
    const { Control } = customize;

    return Control.extend({
        initialize(...args: any) {
            // Bind method contexts correctly
            this.update = this.update.bind(this);
            this.initializeNotifications = this.initializeNotifications.bind(this);
            this.handleRemoved = this.handleRemoved.bind(this);

            Control.prototype.initialize.apply(this, args);

            customize.control.bind("removed", this.handleRemoved);
        },

        /**
         * Render content when control is ready.
         */
        ready() {
            this.setting.bind(this.renderContent.bind(this));
        },

        /**
         * Render React node.
         */
        renderContent() {
            const Context = CustomControlContextFactory.Context();

            const form = (
                <Context.Provider
                    value={{
                        backboneView: this,
                        params: this.params,
                        initializeNotifications: this.initializeNotifications,
                        value: this.setting.get(),
                        setValue: this.update,
                    }}
                >
                    <ControlFieldset>
                        <Component />
                    </ControlFieldset>
                </Context.Provider>
            );

            const root = createRoot(this.container[0]);
            root.render(form);
            this.reactRoot = root;
        },

        /**
         * Update value of control.
         */
        update(value: any) {
            if (typeof value === "object" && !Array.isArray(value)) {
                // Objects can be patched instead of completely overwritten
                this.setting.set({
                    ...this.setting.get(),
                    ...value,
                });
            } else {
                // Primitives
                this.setting.set(value);
            }
        },

        /**
         * Initialize notifications container for this control, so error messages can be shown correctly.
         */
        initializeNotifications(element: HTMLElement) {
            const { notifications } = this;
            notifications.container = $(element);
            notifications.render();
        },

        /**
         * Remove UI container completely before destroy.
         *
         * @param removedControl
         */
        handleRemoved(removedControl: any) {
            if (removedControl === this) {
                this.destroy();
                this.container.remove();
                customize.control.unbind("removed", this.handleRemoved);
            }
        },

        /**
         * Destroy UI container completely (unmount React node).
         */
        destroy() {
            // Garbage collection: undo mounting that was done in the embed/renderContent method.
            this.reactRoot?.unmount();

            // Call destroy method in parent if it exists (as of #31334).
            if (Control.prototype.destroy) {
                Control.prototype.destroy.call(this);
            }
        },
    });
}

export { customControlBackboneView, CUSTOM_CONTROL_INPUT_CHECKBOX_CLASS_NAME };
