/** @module others/mediaViews */
/* global rmlOpts */

import T from "i18n-react";
import $ from "jquery";
import React from "react";
import wp from "wp";

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

import { ID_NONE } from "./defaultFolder.js";
import { filter } from "./filter.js";
import { latestQueriedFolder } from "../components/AppTree.js";
import createFolderSelector from "../components/FolderSelector.js";
import { isAttachmentsGalleryEdit } from "../hooks/modal.js";
import store from "../store/index.js";
import { draggable } from "../util/dragdrop.js";
import { findDeep, hooks, i18n, inViewPort } from "../util/index.js";

/**
 * The attachments browser selectors.hooks
 */
export const BROWSER_SELECTOR = ".attachments-browser";

/**
 * This deferred promise is resolved when the first attachments browser'
 * toolbar is created with the RML filter.
 */
export const firstCreatedToolbar = $.Deferred();

/**
 * Restores the selected id from the _rmlFolder property of the given context.
 *
 * @param {module:AppTree~AppTree} tree The tree instance
 * @param {object} _rmlFolder The _rmlFolder property
 * @param {function} [callback] Optional callback resolved with the selected node
 */
export function restoreMediaViewSelection(tree, _rmlFolder, callback) {
    if (_rmlFolder !== undefined && tree) {
        // Detect, if the folder is available, if yes, select it if not yet
        const { store } = tree.props;
        const node = store.getTreeItemById(_rmlFolder);
        if (node && node.visible && store.selectedId !== _rmlFolder) {
            node.setter((node) => {
                node.selected = true;
            });
        }

        // If the folder is no longer available, force 'All' files to be selected
        if (!node || !node.$visible) {
            tree.handleSelect("all");
        } else {
            callback && callback(node);
        }
    }
}

/**
 * Modify the media-views.js components (Backbone) in a way
 * to make them compatible with the AIOT component.
 */
export default function () {
    if (!findDeep(window, "wp.media.view.Attachment.Library")) {
        return false;
    }

    // Create filter
    const RMLFilter = (wp.media.view.AttachmentFilters.RML = wp.media.view.AttachmentFilters.extend(filter));

    // Hold selectedId to select previously selected id
    if (process.env.PLUGIN_CTX === "pro") {
        /* onlypro:start */
        const { Modal } = wp.media.view;
        wp.media.view.Modal = wp.media.view.Modal.extend({
            className: "rml-modal",

            // Add class and backboneView so we can find it via DOM, see getModalControllerOf in modal.js
            initialize() {
                Modal.prototype.initialize.apply(this, arguments);
                this.$el.data("backboneView", this);
            },

            close() {
                Modal.prototype.close.apply(this, arguments);
                const { $RmlAppTree } = this.controller;
                this._rmlFolder = $RmlAppTree ? $RmlAppTree.props.store.selectedId : undefined;
            },

            open() {
                const { $RmlAppTree } = this.controller;
                $RmlAppTree && restoreMediaViewSelection($RmlAppTree, this._rmlFolder);
                Modal.prototype.open.apply(this, arguments);
            },
        });
        /* onlypro:end */
    }

    // Allow rml orderby
    wp.media.model.Query.orderby.allowed.push("rml");

    const createComporator = (attachments, collection, fallbackComparator) =>
        function (a, b) {
            const { $RmlAppTree } = attachments.controller;
            let selectedId;
            if (
                $RmlAppTree &&
                $RmlAppTree.props &&
                (selectedId = $RmlAppTree.getSelectedId()) &&
                selectedId === "all"
            ) {
                return fallbackComparator.apply(this, arguments);
            }

            let aO;
            let bO;
            if (
                collection.props.get("orderby") === "rml" &&
                (aO = a.attributes.rmlGalleryOrder) &&
                (bO = b.attributes.rmlGalleryOrder) &&
                aO !== -1 &&
                bO !== -1
            ) {
                if (aO < bO) {
                    return -1;
                } else if (aO > bO) {
                    return 1;
                }
                return 0;
            } else if (typeof fallbackComparator === "function") {
                return fallbackComparator.apply(this, arguments);
            }
        };

    // Create rml orderby comparator
    const { Attachments } = wp.media.view;
    wp.media.view.Attachments = wp.media.view.Attachments.extend({
        initialize() {
            Attachments.prototype.initialize.apply(this, arguments);

            const that = this;
            const { collection } = this;
            const { comparator } = collection;

            // Disable comporator in gallery-edit mode (dialog)
            if (isAttachmentsGalleryEdit(this)) {
                collection.comparator = undefined;
                this.initSortable();
                return;
            }

            /* onlypro:start */
            collection.comparator = createComporator(that, collection, comparator);
            /* onlypro:end */

            // Initially load a folder from the AppTree initialSelectedId state
            const oldMore = collection.more;
            that._rmlInitialSetted = false;
            collection.more = function () {
                if (
                    !that.views.parent ||
                    (that.controller.acf &&
                        [
                            that.controller.acf.mode,
                            // legacy ACF version
                            that.controller.acf.data?.mode,
                        ].indexOf("edit") > -1)
                ) {
                    return oldMore.apply(this, arguments);
                }
                const { $RmlAppTree } = that.controller;
                const { toolbar } = that.views.parent;
                const { model } = toolbar.get("rml_folder");

                let initialSelectedId;
                if ($RmlAppTree && $RmlAppTree.props && (initialSelectedId = $RmlAppTree.initialSelectedId)) {
                    if (!that._rmlInitialSetted && initialSelectedId !== ID_NONE) {
                        model.set(
                            { rml_folder: initialSelectedId === "all" ? "" : initialSelectedId },
                            { silent: false },
                        );
                        that._rmlInitialSetted = true;
                    }
                }

                if (model.get("rml_folder") !== undefined) {
                    return oldMore.apply(this, arguments);
                }
                return $.Deferred().resolveWith(that).promise();
            };
        },

        /**
         * Override the default behavior when scrolling is relative to the document
         * height: upload.php. Instead of calculating with the scroll bottom use
         * the view-port approach.
         *
         * @internal
         */
        scroll() {
            let el = this.options.scrollElement;
            let overrideDefault = el === document && !isAttachmentsGalleryEdit(this);

            // The scroll event occurs on the document, but the element
            // that should be checked is the last grid item
            if (overrideDefault && !this.$el.hasClass("rml-loading")) {
                el = this.$el.children(":last");
                if (!$(el).is(":visible") || !this.collection.hasMore()) {
                    return;
                }

                if (inViewPort(el, true)) {
                    this.$el.addClass("rml-loading");
                    this.collection.more().done(() => {
                        this.$el.removeClass("rml-loading");
                        this.scroll();
                    });
                }
            } else {
                Attachments.prototype.scroll.apply(this, arguments);
            }
        },
    });

    // Call a grid render for all new generated items
    const oldRender = wp.media.view.Attachment.Library.prototype.render;
    wp.media.view.Attachment.Library.prototype.render = function () {
        oldRender.apply(this, arguments);

        // Disable in gallery edit view
        if (isAttachmentsGalleryEdit(this.views.parent)) {
            return;
        }

        const { $RmlAppTree } = this.controller;
        /**
         * Fired when an attachments browser item is rendered.
         *
         * @event module:util/hooks#attachmentsBrowser/item/rendered
         * @param {jQuery} $el The element
         * @param {object} model The backbone model
         * @param {object} appTree The app tree instance
         * @this wp.media.view.Attachment.Library
         */
        hooks.call("attachmentsBrowser/item/rendered", [this.$el, this.model, $RmlAppTree], this);
    };

    // Modify attachments browser
    let timeoutReloadCount;
    const { AttachmentsBrowser } = wp.media.view;
    wp.media.view.AttachmentsBrowser = wp.media.view.AttachmentsBrowser.extend({
        initialize() {
            AttachmentsBrowser.prototype.initialize.apply(this, arguments);

            // Disable in gallery edit view
            if (isAttachmentsGalleryEdit(this)) {
                return;
            }

            // Events for attachments browsers collections
            let timeout;
            this.collection.on("change reset add remove", () => {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    // Merged collection change
                    const { $RmlAppTree } = this.controller;
                    if ($RmlAppTree) {
                        draggable($RmlAppTree);

                        /**
                         * Fired when the collection of attachments browser changes.
                         *
                         * @event module:util/hooks#attachmentsBrowser/collection/change
                         * @param {object} appTree The app tree instance
                         * @this wp.media.view.AttachmentsBrowser
                         */
                        hooks.call("attachmentsBrowser/collection/change", [$RmlAppTree], this);
                    }
                }, 50);
            });

            this.collection.on("remove", (...args) => {
                /**
                 * Fired when an attachments browser item gets removed.
                 *
                 * @event module:util/hooks#attachmentsBrowser/item/removed
                 * @param {mixed} args... The event arguments
                 * @this wp.media.view.AttachmentsBrowser
                 */
                hooks.call("attachmentsBrowser/item/removed", [this.controller.$RmlAppTree, ...args], this);
            });

            // Listen to the ajax complete to refresh the folder counts
            $(document).ajaxComplete((e, xhs, req) => {
                try {
                    if (req.data.indexOf("action=delete-post") > -1) {
                        const { $RmlAppTree } = this.controller;
                        clearTimeout(timeoutReloadCount);
                        $RmlAppTree && (timeoutReloadCount = setTimeout(() => $RmlAppTree.fetchCounts(), 1800));
                    }
                } catch (e) {
                    // Silence is golden.
                }
            });
        },

        createToolbar() {
            AttachmentsBrowser.prototype.createToolbar.call(this);

            // Disable in gallery edit view
            if (isAttachmentsGalleryEdit(this)) {
                return;
            }

            this.$el.data("backboneView", this);

            // Add new toolbar
            const obj = new RMLFilter({
                controller: this.controller,
                model: this.collection.props,
                priority: -81, // see media-views.js#7295
            }).render();
            this.toolbar.set("rml_folder", obj);

            const { modal } = this.controller.options;
            if (modal) {
                if (process.env.PLUGIN_CTX === "pro") {
                    /* onlypro:start */
                    /**
                     * Fired, when a new modal window is created.
                     *
                     * @event module:util/hooks#attachmentsBrowser/modal
                     * @this wp.media.view.AttachmentsBrowser
                     */
                    hooks.call("attachmentsBrowser/modal", [], this);
                    /* onlypro:end */
                } else {
                    // In lite version, create container for the simple FolderSelector
                    const folderSelectorContainer = new wp.media.View({
                        className: "rml-attachment-filter-folder-selector",
                    });
                    const proTexts = rmlOpts.others.lang.proFeatures["insert-media-tree-view"];

                    this.toolbar.secondary.views.add(folderSelectorContainer, { at: 1 });
                    createFolderSelector(
                        folderSelectorContainer.el,
                        undefined,
                        {
                            title: i18n("selectFolder"),
                            input: obj.$el.get(0),
                            nullable: true,
                            before: (
                                <button
                                    className="button"
                                    style={{ margin: "1px 10px 0 0" }}
                                    onClick={() => this.collection.props.set({ ignore: +new Date() })}
                                >
                                    <Icon type="reload" />
                                </button>
                            ),
                            onFetchTree: ({ slugs }) => {
                                obj.createFilters(slugs);

                                // Automatically select previously selected folder or fallback
                                const useId = store.selected ? store.selectedId : "all";
                                latestQueriedFolder.node = store.selected
                                    ? store.selected
                                    : store.getTreeItemById("all", false);
                                obj.$el.val(useId).change();
                            },
                            onSelect: (item) => {
                                const useId = item === null ? "all" : item.id;
                                latestQueriedFolder.node = item === null ? store.getTreeItemById("all", false) : item;
                                obj.$el.val(useId).change();

                                // Check if folder needs refresh
                                if (store.foldersNeedsRefresh.indexOf(useId) > -1) {
                                    store.removeFoldersNeedsRefresh(useId);
                                    this.collection.props.set({ ignore: +new Date() });
                                }
                            },
                            children: (
                                <div
                                    style={{
                                        padding: "0px 20px 10px",
                                        background: "#f9f9f9",
                                        margin: "10px -5px -5px",
                                    }}
                                >
                                    <strong
                                        style={{
                                            padding: "10px 0px",
                                            display: "block",
                                            fontSize: 17,
                                        }}
                                    >
                                        {proTexts.title}
                                    </strong>
                                    <img
                                        src={`https://assets.devowl.io/in-app/wp-real-media-library/${proTexts.image}`}
                                        style={{ width: "100%", height: "auto" }}
                                    />
                                    <T.p text={proTexts.description} />
                                    <Button
                                        href={`${rmlOpts.others.proUrl}&feature=insert-media-tree-view`}
                                        target="_blank"
                                        type="primary"
                                        style={{ textDecoration: "none", float: "right" }}
                                    >
                                        {i18n("proBoxOk")}
                                    </Button>
                                    <div className="clear" />
                                </div>
                            ),
                        },
                        store,
                    );
                }
            } else {
                firstCreatedToolbar.resolve(this);
            }
        },

        /**
         * Create the view for "Select folder".
         */
        createAttachments() {
            AttachmentsBrowser.prototype.createAttachments.apply(this, arguments);
            this.attachmentsSelectFolder = new wp.media.View({
                controller: this.controller,
                tagName: "p",
            });
            const { $el } = this.attachmentsSelectFolder;

            if (+rmlOpts.defaultFolder === ID_NONE) {
                $el.addClass("hidden no-media").html(rmlOpts.others.lang.defaultFolderNoneLabel);
                this.views.add(this.attachmentsSelectFolder);
            }

            if (!this.controller.isModeActive("grid")) {
                $el.css({
                    color: "#666",
                    "font-size": "18px",
                    padding: "30px 0 0 20px",
                });
            }
        },

        /**
         * Update handler to show created label for "Select folder" or "No media found".
         */
        updateContent() {
            AttachmentsBrowser.prototype.updateContent.apply(this, arguments);
            const noItemsView = this.controller.isModeActive("grid") ? this.attachmentsNoResults : this.uploader;
            this.dfd &&
                this.dfd.done(() => {
                    const { attributes } = this.collection.props;
                    if (attributes && attributes.rml_folder === undefined) {
                        noItemsView.$el.addClass("hidden");
                        this.attachmentsSelectFolder.$el.removeClass("hidden");
                    } else {
                        this.attachmentsSelectFolder.$el.addClass("hidden");
                    }
                });
        },

        remove() {
            const { $RmlAppTree } = this.controller;
            $RmlAppTree && $RmlAppTree.handleDestroy();
            AttachmentsBrowser.prototype.remove.apply(this, arguments);
        },
    });

    return true;
}

/**
 * Enhanced Media Library compatibility and layout adjustment.
 */
hooks.register("ready", () => {
    if ($("body").hasClass("eml-grid")) {
        const mediaGrid = $("#wp-media-grid");
        const offsetTop = mediaGrid.offset().top;
        const fnResize = () => {
            mediaGrid.css("height", $(window).height() - $("#wpadminbar").height() - 10);
        };
        const fnScroll = () => {
            const scrollTop = $(window).scrollTop();
            mediaGrid[0].style.top = `${scrollTop > offsetTop ? scrollTop : 0}px`;
            //mediaGrid.css("top", scrollTop > offsetTop ? scrollTop - offsetTop : 0);
        };

        // Centerize container
        $(window).on("resize", fnResize);
        fnResize();

        // Scroll container
        $(window).on("scroll", fnScroll);
        fnScroll();
    }
});
