(function($) {
    'use strict';

    // Media-modal interactions module. Extends the KookoawpfMediaModal object
    // defined in media-modal.js with the user-interaction surface: the tree
    // context menu, drag & drop of attachments onto folders, keyboard
    // shortcuts, and the breadcrumb bar.
    //
    // Loaded after media-modal.js (enqueue dependency), so the shared object
    // already exists; Object.assign merges these methods onto it. Method
    // bodies are copied verbatim from the original monolithic file — no logic
    // change, only the wrapper.

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

    Object.assign(M, {

        /**
         * Initialize context menu for the modal tree.
         * Delegates to FO._showContextMenu and FO._executeContextAction
         * via a modal context descriptor — no duplicated logic.
         */
        _initModalContextMenu: function($tree, $sidebar, $frame) {
            const self = this;

            // Ensure context menu element exists
            if (!$('#kookoawpf-context-menu').length) {
                $('body').append('<div id="kookoawpf-context-menu" class="kookoawpf-context-menu" style="display:none;"></div>');
            }

            // Refresh helper
            const _refreshModal = function() {
                FO._folderListCache = null;
                self._refreshModalTree($sidebar, $frame);
                if (FO.tree && FO.tree.length) FO.refreshTree();
            };

            // Build modal context descriptor
            const modalCtx = FO._ctxContext({
                source: 'modal',
                tree: $tree,
                postType: 'attachment',
                clipboard: FO._modalClipboard,
                setClipboard: function(val) { FO._modalClipboard = val; },
                refreshFn: _refreshModal,
                $sidebar: $sidebar,
                $frame: $frame
            });

            // Right-click: delegate to unified _showContextMenu
            $tree.on('contextmenu.jstree', '.jstree-anchor', function(e) {
                e.preventDefault();
                e.stopPropagation();
                $tree.find('.kookoawpf-context-active').removeClass('kookoawpf-context-active');
                const $node = $(this).closest('.jstree-node');
                $node.addClass('kookoawpf-context-active');
                const inst = $tree.jstree(true);
                const node = inst ? inst.get_node($node.attr('id')) : null;
                if (!node) return;
                // Update clipboard reference (may have changed since ctx was created)
                modalCtx.clipboard = FO._modalClipboard;
                // Viewport coordinates — the context menu is `position: fixed`.
                FO._showContextMenu(e.clientX, e.clientY, node, modalCtx);
            });

            // Capture-phase close handlers (needed for WP media modal stopPropagation)
            const _closeCtxMenu = function() {
                const menu = document.getElementById('kookoawpf-context-menu');
                if (menu && menu.style.display !== 'none') {
                    menu.style.display = 'none';
                    $(menu).removeData('ctx');
                    $tree.find('.kookoawpf-context-active').removeClass('kookoawpf-context-active');
                }
            };
            document.addEventListener('mousedown', function(e) {
                const menu = document.getElementById('kookoawpf-context-menu');
                if (menu && menu.style.display !== 'none' && !menu.contains(e.target)) {
                    _closeCtxMenu();
                }
            }, true);
            document.addEventListener('keydown', function(e) {
                if (e.keyCode === 27) {
                    const menu = document.getElementById('kookoawpf-context-menu');
                    if (menu && menu.style.display !== 'none') {
                        e.stopPropagation();
                        e.preventDefault();
                        _closeCtxMenu();
                    }
                }
            }, true);

            // Register unified click handler (idempotent)
            FO._bindCtxClickHandler();
        },
        /**
         * Build breadcrumb chain from the modal tree.
         */
        _getModalBreadcrumbChain: function($tree, folderId) {
            const inst = $tree.length ? $tree.jstree(true) : null;
            if (!inst) return [];
            const chain = [];
            let node = inst.get_node('folder_' + folderId);
            while (node && node.id !== '#') {
                const fid = (node.id || '').replace('folder_', '');
                const text = node.text ? node.text.replace(/<[^>]+>/g, '').trim() : '';
                chain.unshift({ id: fid, text: text });
                node = inst.get_node(node.parent);
            }
            return chain;
        },

        /**
         * Update the modal breadcrumb bar.
         */
        _updateModalBreadcrumb: function($sidebar, $frame, folder) {
            const self = this;
            const $nav = $frame.find('.kookoawpf-modal-breadcrumb .kookoawpf-breadcrumb-nav');
            if (!$nav.length) return;

            $nav.empty();
            const S = FO.data.strings;

            // "All Items" root crumb
            const $allCrumb = $('<a href="#" class="kookoawpf-crumb kookoawpf-crumb-root" data-folder="all"></a>')
                .text(S.allItems || 'All Items');
            $nav.append($allCrumb);

            if (!folder || folder === 'all') {
                $allCrumb.addClass('kookoawpf-crumb-current');
                $frame.find('.kookoawpf-modal-breadcrumb').removeClass('kookoawpf-breadcrumb-active');
                return;
            }

            $frame.find('.kookoawpf-modal-breadcrumb').addClass('kookoawpf-breadcrumb-active');

            if (folder === 'uncategorized') {
                $nav.append('<span class="kookoawpf-crumb-sep">/</span>');
                $nav.append($('<span class="kookoawpf-crumb kookoawpf-crumb-current"></span>').text(S.uncategorized || 'Uncategorized'));
                return;
            }

            const $tree = $sidebar.find('#kookoawpf-jstree');
            const chain = self._getModalBreadcrumbChain($tree, folder);
            if (!chain.length) return;

            for (let i = 0; i < chain.length; i++) {
                $nav.append('<span class="kookoawpf-crumb-sep">/</span>');
                const c = chain[i];
                const isLast = (i === chain.length - 1);
                let $crumb;
                if (isLast) {
                    $crumb = $('<span class="kookoawpf-crumb kookoawpf-crumb-current"></span>');
                } else {
                    // c.id comes from jstree node ids (server-controlled
                    // numeric folder IDs) but routing it through .attr()
                    // keeps the dynamic value out of the HTML literal.
                    $crumb = $('<a href="#" class="kookoawpf-crumb"></a>')
                        .attr('data-folder', c.id);
                }
                $crumb.append(document.createTextNode(c.text));
                $nav.append($crumb);
            }

            // Click handlers
            $nav.off('click.kwpfModalBreadcrumb').on('click.kwpfModalBreadcrumb', '.kookoawpf-crumb[data-folder]', function(e) {
                e.preventDefault();
                self._selectModalFolder($(this).data('folder'), $sidebar, $frame);
            });
        },

        /**
         * Register keyboard shortcuts for the media modal.
         * Registered early in init() so the capture-phase listener is added
         * before WP media modal registers its own handlers.
         * Looks up $sidebar/$frame dynamically since they don't exist yet at registration time.
         */
        _initModalShortcuts: function() {
            if (!FO.data || !FO.data.shortcutsEnabled) return;
            const self = this;

            // Use BOTH capture phase AND jQuery bubble for maximum compatibility
            // (WP media modal may block one but not the other)
            const _handleModalKey = function(e) {
                // Find the VISIBLE modal sidebar (filter avoids stale DOM from previous sessions)
                let $sidebar = $('.kookoawpf-modal-sidebar:visible');
                if (!$sidebar.length) return;
                $sidebar = $sidebar.first();
                const $frame = $sidebar.closest('.media-frame');
                if (!$frame.length) return;
                const $tree = $sidebar.find('#kookoawpf-jstree');

                // Skip if a kookoawpf dialog is open (color, icon, delete, etc.)
                // Block tree navigation keys but let Enter/Escape bubble to dialog handlers
                if ($('.kookoawpf-modal:visible').length) {
                    const blocked = [37, 38, 39, 40, 36, 35, 113]; // ← ↑ → ↓ Home End F2
                    if (blocked.indexOf(e.keyCode) !== -1) {
                        e.stopImmediatePropagation();
                    }
                    return;
                }

                // Skip if focus is on input/textarea/select
                const tag = (e.target.tagName || '').toLowerCase();
                if (tag === 'input' || tag === 'textarea' || tag === 'select') return;
                if (e.target.isContentEditable) return;

                // Build context on the fly
                const _refreshModal = function() {
                    self._refreshModalTree($sidebar, $frame);
                    if (FO.tree && FO.tree.length) FO.refreshTree();
                };
                const ctx = FO._ctxContext({
                    source: 'modal', tree: $tree, postType: 'attachment',
                    clipboard: FO._modalClipboard,
                    setClipboard: function(val) { FO._modalClipboard = val; },
                    refreshFn: _refreshModal, $sidebar: $sidebar, $frame: $frame
                });

                // Block F2 inline rename from jsTree
                if (e.keyCode === 113) { e.preventDefault(); e.stopImmediatePropagation(); }

                const inst = $tree.length ? $tree.jstree(true) : null;
                if (!inst) return;

                // Alt+N — New Folder
                if (e.altKey && e.keyCode === 78) { e.preventDefault(); e.stopImmediatePropagation(); self._createModalFolder($sidebar, $frame); return; }
                // Alt+K — Keyboard Shortcuts panel
                if (e.altKey && e.keyCode === 75) { e.preventDefault(); e.stopImmediatePropagation(); FO._ensureDialogInModal($('#kookoawpf-modal-shortcuts'), 'kookoawpf-modal-shortcuts'); $('#kookoawpf-modal-shortcuts').css('z-index', 200000).show(); return; }

                // →← — Expand / Collapse
                if (e.keyCode === 39 || e.keyCode === 37) {
                    const sel = inst.get_selected(); if (!sel.length) return;
                    const nd = inst.get_node(sel[0]); if (!nd || !inst.is_parent(nd)) return;
                    e.preventDefault(); e.stopImmediatePropagation();
                    if (e.keyCode === 39) inst.open_node(nd); else inst.close_node(nd);
                    return;
                }
                // ↑↓ — Navigate folders + nav items (All/Uncategorized)
                if (!e.shiftKey && (e.keyCode === 38 || e.keyCode === 40)) {
                    const sel = inst.get_selected();

                    // No tree selection — on a nav item (All/Uncategorized)
                    if (!sel.length) {
                        e.preventDefault(); e.stopImmediatePropagation();
                        if (e.keyCode === 40) { // ↓
                            if (FO._modalCurrentFolder === 'all') {
                                self._selectModalFolder('uncategorized', $sidebar, $frame);
                            } else if (FO._modalCurrentFolder === 'uncategorized') {
                                const rootChildren = inst.get_node('#').children;
                                if (rootChildren.length) {
                                    inst.select_node(rootChildren[0]);
                                }
                            }
                        } else { // ↑
                            if (FO._modalCurrentFolder === 'uncategorized') {
                                self._selectModalFolder('all', $sidebar, $frame);
                            }
                        }
                        return;
                    }

                    const $cur = inst.get_node(sel[0], true); if (!$cur || !$cur.length) return;
                    if (e.keyCode === 38) { // ↑
                        const $target = inst.get_prev_dom($cur);
                        if ($target && $target.length) {
                            e.preventDefault(); e.stopImmediatePropagation();
                            inst.deselect_all();
                            inst.select_node($target);
                        } else {
                            // First root node — go to Uncategorized
                            e.preventDefault(); e.stopImmediatePropagation();
                            inst.deselect_all(true);
                            self._selectModalFolder('uncategorized', $sidebar, $frame);
                        }
                    } else { // ↓
                        const $target = inst.get_next_dom($cur);
                        if ($target && $target.length) {
                            e.preventDefault(); e.stopImmediatePropagation();
                            inst.deselect_all();
                            inst.select_node($target);
                        }
                    }
                    return;
                }

                // Resolve selected node for remaining shortcuts
                let node = null;
                const sel = inst.get_selected();
                if (sel.length) node = inst.get_node(sel[0]);
                if (!node) return;
                const fid = (node.id || '').replace('folder_', '');
                if (!fid) return;
                ctx.clipboard = FO._modalClipboard;

                // Shift+↑↓ — Reorder
                if (e.shiftKey && (e.keyCode === 38 || e.keyCode === 40)) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction(e.keyCode === 38 ? 'move-up' : 'move-down', fid, node, ctx); return; }
                // F2 — Rename
                if (e.keyCode === 113) { FO._executeContextAction('rename', fid, node, ctx); return; }
                // Del — Delete
                if (e.keyCode === 46 || e.key === 'Delete') { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('delete', fid, node, ctx); return; }
                // Ctrl+C — Copy
                if (e.ctrlKey && e.keyCode === 67) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('copy', fid, node, ctx); return; }
                // Ctrl+X — Cut
                if (e.ctrlKey && e.keyCode === 88) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('cut', fid, node, ctx); return; }
                // Ctrl+V — Paste
                if (e.ctrlKey && e.keyCode === 86) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('paste', fid, node, ctx); return; }
                // Ctrl+D — Duplicate
                if (e.ctrlKey && e.keyCode === 68) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('duplicate', fid, node, ctx, true); return; }
                // Alt+S — Subfolder
                if (e.altKey && e.keyCode === 83) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('subfolder', fid, node, ctx); return; }
                // Alt+M — Move to Folder
                if (e.altKey && e.keyCode === 77) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('move-to-folder', fid, node, ctx); return; }
                // Alt+C — Color
                if (e.altKey && e.keyCode === 67) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('color', fid, node, ctx); return; }
                // Alt+I — Icon
                if (e.altKey && e.keyCode === 73) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('icon', fid, node, ctx); return; }
                // Alt+P — Pin/Unpin
                if (e.altKey && e.keyCode === 80) {
                    e.preventDefault(); e.stopImmediatePropagation();
                    FO._executeContextAction('pin', fid, node, ctx, true); return;
                }
                // Alt+L — Lock/Unlock
                if (e.altKey && e.keyCode === 76) { e.preventDefault(); e.stopImmediatePropagation(); FO._executeContextAction('lock', fid, node, ctx, true); return; }
            };
            // Register on BOTH capture phase AND jQuery bubble phase
            document.addEventListener('keydown', _handleModalKey, true);
            $(document).on('keydown.kookoawpf-modal-shortcuts', _handleModalKey);
        },
        /**
         * Initialize drag & drop in the media modal: make attachments draggable
         * and modal tree nodes droppable so items can be moved into folders.
         */
        _initModalDragDrop: function($tree, $sidebar, $frame) {
            const self = this;
            let _dragActive = false;
            let _lastHoverId = null;

            // Append the drag helper INSIDE the visible .media-modal rather than
            // <body>. Page builders that wrap the media modal in their own
            // stacking context (Divi 5 ships the modal under a positioned
            // overlay) leave a body-appended helper buried behind that overlay,
            // so the drag "ghost" is invisible even though the drag itself
            // works. Anchoring the helper to the modal makes it share the modal
            // stacking context, where its zIndex: 999999 wins. Falls back to
            // <body> when no modal wrapper is found (defensive — should not
            // happen inside _initModalDragDrop). Harmless on Elementor/native
            // WP, where the ghost was already visible.
            const _dragHost = function() {
                const $host = $frame.closest('.media-modal');
                return $host.length ? $host : $('body');
            };

            // Make modal grid attachments draggable (on mouseenter, delegated)
            $frame.on('mouseenter', '.attachments-browser .attachment', function() {
                const $att = $(this);
                if ($att.data('fo-modal-d')) return;
                $att.data('fo-modal-d', true);
                $att.draggable({
                    helper: function() {
                        return FO._createDragHelper($(this), $frame);
                    },
                    appendTo: _dragHost(), cursor: 'move',
                    cursorAt: { left: 10, top: 10 }, distance: 10,
                    revert: false, zIndex: 999999,
                    start: function() {
                        // Smart folders are virtual queries; dragging from them is meaningless.
                        if (FO._modalCurrentFolderIsDynamic) return false;
                        _dragActive = true;
                        $('body').addClass('kookoawpf-dragging');
                    },
                    stop: function() {
                        _dragActive = false; _lastHoverId = null;
                        $('body').removeClass('kookoawpf-dragging');
                        $tree.find('.kookoawpf-drop-hover').removeClass('kookoawpf-drop-hover');
                    }
                });
            });

            // Expose a method to force-init draggable on all current grid attachments.
            // Called after upload refresh to ensure elements recreated by Backbone are draggable.
            self._ensureModalAttachmentsDraggable = function() {
                // Fix missing data-id on attachment elements.
                // WP may re-render the selected attachment view without setting data-id.
                const $missingId = $frame.find('.attachments-browser .attachment:not([data-id])');
                if ($missingId.length) {
                    try {
                        let _lib = null;
                        const _frame = wp.media.frame;
                        try { if (_frame) _lib = _frame.state().get('library'); } catch(e) {}
                        if (!_lib && wp.media.frames) {
                            const _keys = Object.keys(wp.media.frames);
                            for (let k = 0; k < _keys.length; k++) {
                                try {
                                    const _f = wp.media.frames[_keys[k]];
                                    if (_f && _f.state) { _lib = _f.state().get('library'); break; }
                                } catch(e) {}
                            }
                        }
                        if (_lib) {
                            $missingId.each(function() {
                                const $att = $(this);
                                const src = $att.find('img').attr('src') || '';
                                if (!src) return;
                                const srcFile = src.split('/').pop().replace(/-\d+x\d+\./, '.'); // strip thumbnail size suffix
                                _lib.each(function(model) {
                                    if ($att.attr('data-id')) return; // already fixed
                                    const url = model.get('url') || '';
                                    const urlFile = url.split('/').pop();
                                    if (urlFile && srcFile && urlFile === srcFile) {
                                        $att.attr('data-id', model.id);
                                        jQuery.data($att[0], 'id', model.id);
                                    }
                                });
                            });
                        }
                    } catch(e) {}
                }

                $frame.find('.attachments-browser .attachment').each(function() {
                    const $att = $(this);
                    if ($att.data('fo-modal-d')) return;
                    $att.data('fo-modal-d', true);
                    $att.draggable({
                        helper: function() {
                            return FO._createDragHelper($(this), $frame);
                        },
                        appendTo: 'body', cursor: 'move',
                        cursorAt: { left: 10, top: 10 }, distance: 10,
                        revert: false, zIndex: 999999,
                        start: function() {
                            if (FO._modalCurrentFolderIsDynamic) return false;
                            _dragActive = true;
                            $('body').addClass('kookoawpf-dragging');
                        },
                        stop: function() {
                            _dragActive = false; _lastHoverId = null;
                            $('body').removeClass('kookoawpf-dragging');
                            $tree.find('.kookoawpf-drop-hover').removeClass('kookoawpf-drop-hover');
                        }
                    });
                });
            };

            // Hover tracking for drop indicator
            $(document).on('mousemove.kookoawpf-modal-dd', function(e) {
                if (!_dragActive) return;
                const el = document.elementFromPoint(e.clientX, e.clientY);
                if (!el) { if (_lastHoverId) { $tree.find('.kookoawpf-drop-hover').removeClass('kookoawpf-drop-hover'); _lastHoverId = null; } return; }
                const $anchor = $(el).closest('.jstree-anchor');
                const $node = $anchor.length ? $anchor.closest('.jstree-node') : null;
                const nodeId = $node ? $node.attr('id') : null;
                if (nodeId === _lastHoverId) return;
                _lastHoverId = nodeId;
                $tree.find('.kookoawpf-drop-hover').removeClass('kookoawpf-drop-hover');
                if ($node) {
                    const inst = $tree.jstree(true);
                    const treeNode = inst ? inst.get_node(nodeId) : null;
                    if (treeNode && treeNode.data && treeNode.data.is_dynamic) return;
                    $node.addClass('kookoawpf-drop-hover');
                }
            });

            // Make tree nodes droppable
            const dropOpts = {
                accept: '.attachments-browser .attachment',
                tolerance: 'pointer', greedy: true,
                drop: function(event, ui) {
                    $tree.find('.kookoawpf-drop-hover').removeClass('kookoawpf-drop-hover');
                    _dragActive = false; _lastHoverId = null;
                    const inst = $tree.jstree(true);
                    const treeNode = inst ? inst.get_node($(this).attr('id')) : null;
                    if (!treeNode) return;
                    if (treeNode.data && treeNode.data.is_dynamic) {
                        FO.showNotice(FO.data.strings.smartAutoPopulated || 'Smart folders are auto-populated.', 'warning');
                        return;
                    }
                    const targetId = FO.getFolderId(treeNode);
                    // Get dragged post IDs
                    const postIds = [];
                    const sel = $frame.find('.attachment[aria-checked="true"], .attachment.selected');
                    if (sel.length > 1) {
                        sel.each(function() { const pid = $(this).data('id'); if (pid) postIds.push(pid); });
                    } else {
                        const pid = ui.draggable.data('id');
                        if (pid) postIds.push(pid);
                    }
                    if (postIds.length && targetId) {
                        // Hide dragged items immediately for visual feedback
                        ui.draggable.fadeOut(150);
                        FO.ajax('kookoawpf_move_items', {
                            post_ids: postIds,
                            from_folder_id: FO._modalCurrentFolder || 'all',
                            to_folder_id: targetId,
                            post_type: 'attachment'
                        }, function(r) {
                            if (r.success) {
                                FO.showNotice(
                                    postIds.length > 1
                                        ? postIds.length + ' ' + (FO.data.strings.itemsMoved || 'items moved')
                                        : (FO.data.strings.moveTo || 'Item moved'),
                                    'success'
                                );
                                self._afterModalMove($sidebar, $frame);
                            }
                        });
                    }
                }
            };

            // Apply droppable to existing and future tree nodes
            const _makeDroppable = function($node) {
                if ($node.data('fo-modal-dp')) return;
                $node.data('fo-modal-dp', true);
                $node.droppable(dropOpts);
            };
            // Expose for re-init after tree refresh (load_node recreates DOM nodes)
            self._modalMakeDroppable = _makeDroppable;
            $tree.find('.jstree-node').each(function() { _makeDroppable($(this)); });
            $tree.on('mouseenter', '.jstree-node', function() {
                _makeDroppable($(this));
            });

            // Uncategorized droppable
            $sidebar.find('.kookoawpf-nav-uncategorized').droppable({
                accept: '.attachments-browser .attachment',
                hoverClass: 'kookoawpf-drop-hover', tolerance: 'pointer',
                drop: function(e, ui) {
                    const cur = FO._modalCurrentFolder;
                    if (!cur || cur === 'all' || cur === 'uncategorized') return;
                    const postIds = [];
                    const sel = $frame.find('.attachment[aria-checked="true"], .attachment.selected');
                    if (sel.length > 1) {
                        sel.each(function() { const pid = $(this).data('id'); if (pid) postIds.push(pid); });
                    } else {
                        const pid = ui.draggable.data('id');
                        if (pid) postIds.push(pid);
                    }
                    if (postIds.length) {
                        ui.draggable.fadeOut(150);
                        FO.ajax('kookoawpf_unassign_items', {
                            post_ids: postIds,
                            folder_id: cur,
                            post_type: 'attachment'
                        }, function(r) {
                            if (r.success) {
                                FO.showNotice(FO.data.strings.moveTo || 'Item moved', 'success');
                                self._afterModalMove($sidebar, $frame);
                            }
                        });
                    }
                }
            });
        },
    });

})(jQuery);
