/**
 * KooKoa Folders — Folder Bulk Actions (sidebar checkbox mode)
 *
 * Extracted from admin.js (block 10).
 * IIFE that adds methods directly to FO (window.KookoaWPF).
 *
 * Depends on: kookoawpf-admin (FO.data, FO.tree, FO.ajax, FO.showNotice,
 *             FO.refreshTree, FO.goToFolder, FO.getFolderId, FO.applyFolderIcons,
 *             FO.applyActiveFolderColor, FO._hasLockedDescendant, FO._updateNoFolderState)
 *
 * @since 1.0.0
 */
(function($) {
    'use strict';

    const FO = window.KookoaWPF;
    if (!FO) return;

    /** Currently selected folder IDs in bulk mode */
    FO._fbSelected = [];

    /** Whether folder bulk mode is active */
    FO._fbActive = false;

    FO.initFolderBulk = function() {
        const self = this;

        // Master checkbox: toggle bulk mode
        $(document).on('change', '#kookoawpf-fb-master', function() {
            if ($(this).is(':checked')) {
                self._fbEnter();
            } else {
                self._fbExit();
            }
        });

        // Capture-phase click on tree anchors in bulk mode.
        // Must use native addEventListener with capture:true to intercept
        // BEFORE jsTree's own click handler fires.
        document.addEventListener('click', function(e) {
            if (!self._fbActive) return;

            const anchor = e.target.closest('#kookoawpf-jstree .jstree-anchor');
            if (!anchor) return;

            // Don't interfere with +/- toggle
            if (e.target.closest('.jstree-ocl')) return;

            e.preventDefault();
            e.stopImmediatePropagation();

            const $node = $(anchor).closest('.jstree-node');
            const nodeId = $node.attr('id');
            if (!nodeId) return;

            const treeNode = self.tree.jstree('get_node', nodeId);
            if (!treeNode) return;
            const folderId = self.getFolderId(treeNode);
            if (!folderId) return;

            // Locked folders (or folders containing locked descendants) cannot be selected
            if ((treeNode.data && treeNode.data.locked) || self._hasLockedDescendant(treeNode)) return;

            if ($node.hasClass('kookoawpf-fb-selected')) {
                // Deselect: only this node, children stay selected
                $node.removeClass('kookoawpf-fb-selected');
                self._fbSelected = self._fbSelected.filter(function(id) { return id !== folderId; });
            } else {
                // Select: this node + all descendants
                $node.addClass('kookoawpf-fb-selected');
                if (self._fbSelected.indexOf(folderId) === -1) self._fbSelected.push(folderId);

                // Cascade to all descendants
                const inst = self.tree.jstree(true);
                if (inst && treeNode.children_d && treeNode.children_d.length) {
                    for (let ci = 0; ci < treeNode.children_d.length; ci++) {
                        const childNode = inst.get_node(treeNode.children_d[ci]);
                        if (!childNode) continue;
                        const childFid = self.getFolderId(childNode);
                        if (!childFid) continue;
                        // Skip locked folders and folders with locked descendants
                        if ((childNode.data && childNode.data.locked) || self._hasLockedDescendant(childNode)) continue;
                        const $childEl = self.tree.find('#' + childNode.id);
                        if (!$childEl.hasClass('kookoawpf-fb-selected')) {
                            $childEl.addClass('kookoawpf-fb-selected');
                            if (self._fbSelected.indexOf(childFid) === -1) self._fbSelected.push(childFid);
                        }
                    }
                }
            }

            self._fbUpdateUI();
        }, true); // ← capture phase

        // Also intercept select_node in bulk mode
        // (belt-and-suspenders: suppress navigation when in bulk mode)
        // Action buttons
        $(document).on('click', '.kookoawpf-fb-btn[data-action="pin"]', function() {
            if ($(this).is('[disabled]')) return;
            self._fbActionPin();
        });

        $(document).on('click', '.kookoawpf-fb-btn[data-action="lock"]', function() {
            if ($(this).is('[disabled]')) return;
            self._fbActionLock();
        });

        $(document).on('click', '.kookoawpf-fb-btn[data-action="duplicate"]', function() {
            if ($(this).is('[disabled]')) return;
            self._fbActionDuplicate();
        });

        $(document).on('click', '.kookoawpf-fb-btn[data-action="delete"]', function() {
            if ($(this).is('[disabled]')) return;
            self._fbActionDelete();
        });

        // Bulk folder delete modal — confirm button
        $(document).on('click', '#kookoawpf-confirm-bulk-folder-delete', function() {
            self._fbExecuteDelete();
        });
        // Bulk folder delete modal — Enter key to confirm
        $(document).on('keydown', function(e) {
            if (e.keyCode === 13 && $('#kookoawpf-modal-bulk-folder-delete:visible').length) {
                e.preventDefault();
                $('#kookoawpf-confirm-bulk-folder-delete').trigger('click');
            }
        });
    };

    /**
     * Enter folder bulk selection mode.
     */
    FO._fbEnter = function() {
        const self = this;
        self._fbActive = true;
        self._fbSelected = [];
        $('body').addClass('kookoawpf-bulk-folder-mode');

        // Deselect active folder in tree and remove visual highlight
        if (self.tree && self.tree.jstree(true)) {
            self.tree.jstree(true).deselect_all();
            self.applyActiveFolderColor();
        }

        // Mark locked folders (and their ancestors) as non-selectable
        if (self.tree && self.tree.jstree(true)) {
            const inst = self.tree.jstree(true);
            const allNodes = inst.get_json('#', { flat: true });
            for (let i = 0; i < allNodes.length; i++) {
                const n = inst.get_node(allNodes[i].id);
                if (!n) continue;
                const isLocked = n.data && n.data.locked;
                const hasLockedChild = self._hasLockedDescendant(n);
                if (isLocked || hasLockedChild) {
                    const $el = inst.get_node(n.id, true);
                    $el.addClass('kookoawpf-fb-locked');
                    // Ancestor (not locked itself) gets ❌ + tooltip
                    if (!isLocked && hasLockedChild) {
                        $el.addClass('kookoawpf-fb-locked-ancestor');
                        $el.children('.jstree-anchor').attr('title', self.data.strings.lockedSubfolderPresent || 'Contains a locked subfolder');
                    }
                }
            }

            // Disable jsTree DnD while in bulk mode
            if (inst.settings && inst.settings.dnd) {
                self._fbOrigDraggable = inst.settings.dnd.is_draggable;
                inst.settings.dnd.is_draggable = function() { return false; };
            }
        }

        self._fbUpdateUI();
    };

    /**
     * Re-apply locked/ancestor classes after tree redraw (open/close/refresh).
     */
    FO._fbReapplyLocked = function() {
        const self = this;
        if (!self.tree || !self.tree.jstree(true)) return;
        const inst = self.tree.jstree(true);
        const allNodes = inst.get_json('#', { flat: true });
        for (let i = 0; i < allNodes.length; i++) {
            const n = inst.get_node(allNodes[i].id);
            if (!n) continue;
            const $el = inst.get_node(n.id, true);
            if (!$el || !$el.length) continue;
            const isLocked = n.data && n.data.locked;
            const hasLockedChild = self._hasLockedDescendant(n);
            if (isLocked || hasLockedChild) {
                $el.addClass('kookoawpf-fb-locked');
                if (!isLocked && hasLockedChild) {
                    $el.addClass('kookoawpf-fb-locked-ancestor');
                    $el.children('.jstree-anchor').attr('title', self.data.strings.lockedSubfolderPresent || 'Contains a locked subfolder');
                }
            }
            // Also re-apply selected state
            if (self._fbSelected && self._fbSelected.indexOf(self.getFolderId(n)) !== -1) {
                $el.addClass('kookoawpf-fb-selected');
            }
        }
    };

    /**
     * Exit folder bulk selection mode.
     */
    FO._fbExit = function() {
        const self = this;
        self._fbActive = false;
        self._fbSelected = [];
        $('body').removeClass('kookoawpf-bulk-folder-mode');

        // Remove selection and locked classes
        if (self.tree) {
            self.tree.find('.kookoawpf-fb-selected').removeClass('kookoawpf-fb-selected');
            self.tree.find('.kookoawpf-fb-locked-ancestor').each(function() {
                $(this).children('.jstree-anchor').removeAttr('title');
            });
            self.tree.find('.kookoawpf-fb-locked').removeClass('kookoawpf-fb-locked');
            self.tree.find('.kookoawpf-fb-locked-ancestor').removeClass('kookoawpf-fb-locked-ancestor');
        }

        // Restore jsTree DnD
        if (self.tree && self.tree.jstree(true)) {
            const inst = self.tree.jstree(true);
            if (inst.settings && inst.settings.dnd && self._fbOrigDraggable) {
                inst.settings.dnd.is_draggable = self._fbOrigDraggable;
            }
        }

        // Reset master checkbox
        const $master = $('#kookoawpf-fb-master');
        $master.prop('checked', false);
        $master.prop('indeterminate', false);

        self._fbUpdateUI();
    };

    /**
     * Return the subset of _fbSelected that are NOT already pinned.
     * Bulk pin is idempotent "pin all": already-pinned stay pinned, others get pinned.
     */
    FO._fbSelectedNotPinned = function() {
        const self = this;
        const inst = self.tree ? self.tree.jstree(true) : null;
        if (!inst) return [];
        const out = [];
        for (let i = 0; i < self._fbSelected.length; i++) {
            const fid = self._fbSelected[i];
            const node = inst.get_node('folder_' + fid);
            if (node && node.data && !node.data.pinned) out.push(fid);
        }
        return out;
    };

    /**
     * Evaluate the pin button state for the current selection.
     * Pin is fully free, so this only distinguishes:
     *   'ok'   — there is at least one non-pinned folder in the selection
     *   'noop' — everything in the selection is already pinned → disable
     */
    FO._fbPinState = function() {
        return this._fbSelectedNotPinned().length > 0 ? 'ok' : 'noop';
    };

    /**
     * Return the subset of _fbSelected that are NOT already locked.
     * Note: in practice already-locked folders cannot be in the bulk selection
     * (the capture-phase click handler skips them), so this usually == _fbSelected.
     * Kept for consistency with the pin logic and as a safety net.
     */
    FO._fbSelectedNotLocked = function() {
        const self = this;
        const inst = self.tree ? self.tree.jstree(true) : null;
        if (!inst) return [];
        const out = [];
        for (let i = 0; i < self._fbSelected.length; i++) {
            const fid = self._fbSelected[i];
            const node = inst.get_node('folder_' + fid);
            if (node && node.data && !node.data.locked) out.push(fid);
        }
        return out;
    };

    /**
     * Evaluate the lock button state for the current selection.
     * Lock is fully free, so this only returns 'ok' | 'noop' — same as pin.
     */
    FO._fbLockState = function() {
        return this._fbSelectedNotLocked().length > 0 ? 'ok' : 'noop';
    };

    /**
     * Return the "top-level" folders in the current bulk selection —
     * the ones whose ancestors are NOT also in _fbSelected.
     *
     * Why: in bulk mode, clicking a parent cascades the selection to all
     * descendants. When the user then triggers an action whose server
     * handler already cascades (delete, duplicate), iterating on the
     * descendants would either double-process them or fire failing AJAX
     * for items already removed by the parent's cascade.
     *
     * @param {boolean} excludeSmart If true, smart (dynamic) folders are skipped.
     */
    FO._fbTopLevelSelected = function(excludeSmart) {
        const self = this;
        const inst = self.tree ? self.tree.jstree(true) : null;
        if (!inst) return [];
        const selected = self._fbSelected.slice();
        const selSet = {};
        for (let i = 0; i < selected.length; i++) selSet[selected[i]] = true;

        const top = [];
        for (let j = 0; j < selected.length; j++) {
            const fid = selected[j];
            const node = inst.get_node('folder_' + fid);
            if (!node) continue;
            if (excludeSmart && node.data && node.data.is_dynamic) continue;
            // Walk ancestors; if any ancestor is also selected, skip this fid.
            const parents = node.parents || [];
            let skip = false;
            for (let k = 0; k < parents.length; k++) {
                const pId = parents[k];
                if (pId === '#') continue;
                const pNode = inst.get_node(pId);
                if (!pNode) continue;
                const pFid = self.getFolderId(pNode);
                if (pFid && selSet[pFid]) { skip = true; break; }
            }
            if (!skip) top.push(fid);
        }
        return top;
    };

    /**
     * Top-level selected, excluding smart folders (which cannot be duplicated).
     * The server recursively clones the whole subtree in one call, so we must
     * not iterate on descendants.
     */
    FO._fbDuplicateTopLevel = function() {
        return this._fbTopLevelSelected(true);
    };

    /**
     * Evaluate the duplicate button state for the current selection.
     * Smart folders are not duplicable, so:
     *   'ok'   — at least one non-smart folder in the selection
     *   'noop' — every selected folder is a smart folder → disable
     */
    FO._fbDuplicateState = function() {
        return this._fbDuplicateTopLevel().length > 0 ? 'ok' : 'noop';
    };

    /**
     * Apply a tri-state ('ok' | 'noop') to a bulk toolbar button.
     */
    FO._fbApplyBtnState = function($btn, state) {
        if (!$btn || !$btn.length) return;
        $btn.prop('disabled', state !== 'ok');
    };

    /**
     * Update toolbar buttons enabled/disabled + master checkbox state.
     */
    FO._fbUpdateUI = function() {
        const self = this;
        const count = self._fbSelected.length;

        if (count > 0) {
            $('.kookoawpf-fb-btn[data-action="delete"]').prop('disabled', false);
            self._fbApplyBtnState($('.kookoawpf-fb-btn[data-action="duplicate"]'), self._fbDuplicateState());
            self._fbApplyBtnState($('.kookoawpf-fb-btn[data-action="pin"]'), self._fbPinState());
            self._fbApplyBtnState($('.kookoawpf-fb-btn[data-action="lock"]'), self._fbLockState());
        } else {
            $('.kookoawpf-fb-btn').prop('disabled', true);
        }
    };

    /**
     * Get array of selected folder IDs (deduplicated).
     */
    FO._fbGetSelected = function() {
        // Deduplicate just in case
        const unique = [];
        for (let i = 0; i < this._fbSelected.length; i++) {
            if (unique.indexOf(this._fbSelected[i]) === -1) {
                unique.push(this._fbSelected[i]);
            }
        }
        return unique;
    };

    /**
     * Batch Pin to Top action — idempotent "pin all".
     * Only pins folders that are NOT already pinned. Already-pinned are left alone.
     */
    FO._fbActionPin = function() {
        const self = this;

        const ids = self._fbSelectedNotPinned();
        if (!ids.length) return;

        // Check if ALL selected are smart folders
        const inst = self.tree ? self.tree.jstree(true) : null;
        let allDynamic = ids.length > 0;
        if (inst) {
            for (let d = 0; d < ids.length; d++) {
                const dn = inst.get_node('folder_' + ids[d]);
                if (!dn || !dn.data || !dn.data.is_dynamic) { allDynamic = false; break; }
            }
        } else { allDynamic = false; }

        let hadErrors = false;

        // Sequential chain with delay — prevents server overload on cheap hosting.
        if (ids.length >= 5) self.showNotice(self.data.strings.bulkProcessing, 'success', true);
        function next(i) {
            if (i >= ids.length) {
                if (hadErrors) {
                    self.showNotice(self.data.strings.errorGeneric, 'error');
                } else if (allDynamic) {
                    self.showNotice(ids.length === 1 ? self.data.strings.smartPinnedToTop : self.data.strings.smartBulkPinPlural, 'success');
                } else {
                    self.showNotice(self.data.strings.folderBulkPin, 'success');
                }
                self._fbExit();
                self.refreshTree();
                return;
            }
            self.ajax('kookoawpf_toggle_pin', {
                folder_id: ids[i],
                post_type: self.data.postType
            }, function(r) {
                if (!r || !r.success) hadErrors = true;
                setTimeout(function() { next(i + 1); }, 200);
            });
        }
        next(0);
    };

    /**
     * Batch Lock action — idempotent "lock all".
     * Only locks folders that are NOT already locked. Already-locked are left alone.
     */
    FO._fbActionLock = function() {
        const self = this;

        const ids = self._fbSelectedNotLocked();
        if (!ids.length) return;

        // Check if ALL selected are smart folders
        const inst = self.tree ? self.tree.jstree(true) : null;
        let allDynamic = ids.length > 0;
        if (inst) {
            for (let d = 0; d < ids.length; d++) {
                const dn = inst.get_node('folder_' + ids[d]);
                if (!dn || !dn.data || !dn.data.is_dynamic) { allDynamic = false; break; }
            }
        } else { allDynamic = false; }

        let hadErrors = false;

        // Sequential chain with delay — prevents server overload on cheap hosting.
        if (ids.length >= 5) self.showNotice(self.data.strings.bulkProcessing, 'success', true);
        function next(i) {
            if (i >= ids.length) {
                if (hadErrors) {
                    self.showNotice(self.data.strings.errorGeneric, 'error');
                } else if (allDynamic) {
                    self.showNotice(ids.length === 1 ? self.data.strings.smartFolderLocked : self.data.strings.smartBulkLockPlural, 'success');
                } else {
                    self.showNotice(self.data.strings.folderBulkLock, 'success');
                }
                self._fbExit();
                self.refreshTree();
                return;
            }
            self.ajax('kookoawpf_toggle_lock', {
                folder_id: ids[i],
                post_type: self.data.postType
            }, function(r) {
                if (!r || !r.success) hadErrors = true;
                setTimeout(function() { next(i + 1); }, 200);
            });
        }
        next(0);
    };

    /**
     * Batch Duplicate action.
     *
     * Only duplicates the "top-level" folders in the selection — a top-level is
     * one whose ancestors are NOT also selected. This matches the mental model
     * of the context menu: duplicating a parent recursively clones the whole
     * subtree, so iterating on descendants too would duplicate them twice.
     *
     * Calls are sequential to keep the resulting tree order deterministic.
     */
    FO._fbActionDuplicate = function() {
        const self = this;

        const ids = self._fbDuplicateTopLevel();
        if (!ids.length) return;

        const total = ids.length;
        let hadErrors = false;

        // Sequential chain with delay — prevents server overload on cheap hosting.
        if (ids.length >= 5) self.showNotice(self.data.strings.bulkProcessing, 'success', true);
        function next(i) {
            if (i >= ids.length) {
                if (hadErrors) {
                    self.showNotice(self.data.strings.errorGeneric, 'error');
                } else {
                    const n = parseInt(total, 10) || 0;
                    const msg = n === 1
                        ? (self.data.strings.folderDuplicated || 'Folder duplicated.')
                        : (self.data.strings.folderBulkDuplicated || '%d folders duplicated.').replace('%d', n);
                    self.showNotice(msg, 'success');
                }
                self._fbExit();
                self.refreshTree();
                return;
            }
            self.ajax('kookoawpf_duplicate_folder', {
                folder_id: ids[i],
                post_type: self.data.postType
            }, function(r) {
                if (!r || !r.success) hadErrors = true;
                setTimeout(function() { next(i + 1); }, 200);
            });
        }
        next(0);
    };

    /**
     * Batch Delete action — show confirmation modal.
     */
    FO._fbActionDelete = function() {
        const self = this;
        const ids = self._fbGetSelected();
        if (!ids.length) return;

        // Check if ALL selected are smart folders
        const inst = self.tree ? self.tree.jstree(true) : null;
        let allDynamic = ids.length > 0 && inst;
        if (allDynamic) {
            for (let i = 0; i < ids.length; i++) {
                const n = inst.get_node('folder_' + ids[i]);
                if (!n || !n.data || !n.data.is_dynamic) { allDynamic = false; break; }
            }
        }

        // Dynamic title/text: smart folders vs regular folders
        const $modal = $('#kookoawpf-modal-bulk-folder-delete');
        $modal.find('.kookoawpf-modal-header h3').text(
            allDynamic ? (self.data.strings.deleteBulkSmartTitle || 'Delete Smart Folders')
                       : (self.data.strings.deleteBulkTitle || 'Delete Folders')
        );
        $modal.find('.kookoawpf-modal-body p').text(
            allDynamic ? (self.data.strings.deleteBulkSmartMsg || 'Are you sure you want to delete the selected smart folders? The items inside will NOT be deleted.')
                       : (self.data.strings.deleteBulkMsg || 'Are you sure you want to delete the selected folders? All subfolders will also be deleted. The items inside will NOT be deleted.')
        );

        $modal.show();
    };

    /**
     * Execute bulk folder delete after modal confirmation.
     */
    FO._fbExecuteDelete = function() {
        const self = this;
        // All currently selected ids — used by the optimistic UI pass and to
        // detect whether the active folder is being removed.
        const allIds = self._fbGetSelected();
        if (!allIds.length) return;

        // Server-side deletes: only the top-level of the selection. The server
        // already cascades, so firing AJAX for descendants of an already-selected
        // parent would race against the cascade and produce spurious errors.
        const serverIds = self._fbTopLevelSelected(false);

        $('#kookoawpf-modal-bulk-folder-delete').hide();

        // Check if ALL selected are smart folders (before optimistic delete)
        const inst = self.tree.jstree(true);
        let allDynamic = allIds.length > 0;
        if (inst) {
            for (let d = 0; d < allIds.length; d++) {
                const dn = inst.get_node('folder_' + allIds[d]);
                if (!dn || !dn.data || !dn.data.is_dynamic) { allDynamic = false; break; }
            }
        } else { allDynamic = false; }

        // Optimistic: remove nodes from tree immediately. Iterating allIds is
        // safe because jstree's delete_node on a parent removes its descendants;
        // the later get_node() calls for those descendants simply return null.
        $('#kookoawpf-dynamic-tooltip').hide();
        allIds.forEach(function(fid) {
            const nodeId = 'folder_' + fid;
            if (inst && inst.get_node(nodeId)) {
                inst.delete_node(nodeId);
            }
        });
        self.applyFolderIcons();
        if (allIds.indexOf(parseInt(self.currentFolder, 10)) !== -1) {
            self.goToFolder('all');
        }
        self._fbExit();
        self._updateNoFolderState();
        if (allIds.length >= 5) self.showNotice(self.data.strings.bulkProcessing, 'success', true);

        // Sequential chain with delay — prevents server overload on cheap hosting.
        let hadErrors = false;

        function next(i) {
            if (i >= serverIds.length) {
                if (hadErrors) {
                    self.showNotice(self.data.strings.errorGeneric, 'error');
                } else if (allDynamic) {
                    self.showNotice(allIds.length === 1 ? self.data.strings.smartBulkDeleted : self.data.strings.smartBulkDeletedPlural, 'success');
                } else {
                    self.showNotice(self.data.strings.folderBulkDeleted, 'success');
                }
                self.refreshTree();
                return;
            }
            self.ajax('kookoawpf_delete_folder', {
                folder_id: serverIds[i],
                post_type: self.data.postType
            }, function(r) {
                if (!r || !r.success) hadErrors = true;
                setTimeout(function() { next(i + 1); }, 200);
            });
        }
        next(0);
    };

})(jQuery);
