/**
 * EmbedPress — YouTube Playlist (Queue + Theatre)
 *
 * Wires up the .ep-yt-playlist shells emitted by
 * EmbedPress\Providers\TemplateLayouts\YoutubeLayout (queue + theatre).
 * Per-layout selectors differ (queue uses .ep-yt-queue__*, theatre uses
 * .ep-yt-theatre__*) but the interaction model is the same:
 *   - click-to-swap: clicking an item/card swaps the iframe src to that vid,
 *     updates the active highlight and the "N / M" position counter
 *   - infinite scroll: when scrolled near the end of the list/strip,
 *     fetches the next page of items via WP REST and appends them
 *   - loop: when ended (postMessage from YT iframe), advance to next or
 *     restart from 1 if loop is on
 *   - shuffle: randomize playback order; toggling off restores natural order
 *   - theatre extras: prev/next arrow buttons scroll the card strip
 *
 * No framework dependency — vanilla DOM. Safe to enqueue multiple times;
 * the per-instance `init` guard prevents double-binding.
 */
(function () {
    'use strict';

    var ROOT_SELECTOR      = '.ep-yt-playlist';
    var INFINITE_SCROLL_PX = 120;
    var INIT_FLAG          = 'epYtPlaylistInit';

    // Per-layout selector tables — keep markup contracts in one place.
    // New Pro layouts plug in here without any other JS change.
    var SELECTORS = {
        queue: {
            iframe:     '.ep-yt-queue__iframe',
            list:       '.ep-yt-queue__items',
            item:       '.ep-yt-queue__item',
            activeItem: '.ep-yt-queue__item.is-active',
            loader:     '.ep-yt-queue__loader',
            loopBtn:    '.ep-yt-queue__loop',
            shuffleBtn: '.ep-yt-queue__shuffle',
            posCurrent: '.ep-yt-queue__position-current',
            scrollAxis: 'y'
        },
        theatre: {
            iframe:     '.ep-yt-theatre__iframe',
            list:       '.ep-yt-theatre__strip',
            item:       '.ep-yt-theatre__card',
            activeItem: '.ep-yt-theatre__card.is-active',
            loader:     '.ep-yt-theatre__loader',
            loopBtn:    '.ep-yt-theatre__loop',
            shuffleBtn: '.ep-yt-theatre__shuffle',
            posCurrent: '.ep-yt-theatre__position-current',
            navPrev:    '.ep-yt-theatre__nav--prev',
            navNext:    '.ep-yt-theatre__nav--next',
            scrollAxis: 'x'
        },
        // Library: no inline player. Clicking a card opens the modal.
        library: {
            iframe:     '.ep-yt-library__modal-iframe',
            list:       '.ep-yt-library__grid',
            item:       '.ep-yt-library__card',
            activeItem: '.ep-yt-library__card.is-active',
            loader:     '.ep-yt-library__loader',
            modal:      '.ep-yt-library__modal',
            modalClose: '.ep-yt-library__modal-close',
            posCurrent: '.ep-yt-library__position-current',
            scrollAxis: 'y'
        },
        spotlight: {
            iframe:     '.ep-yt-spotlight__iframe',
            list:       '.ep-yt-spotlight__rail',
            item:       '.ep-yt-spotlight__rail-card',
            activeItem: '.ep-yt-spotlight__rail-card.is-active',
            loader:     '.ep-yt-spotlight__loader',
            loopBtn:    '.ep-yt-spotlight__loop',
            shuffleBtn: '.ep-yt-spotlight__shuffle',
            posCurrent: '.ep-yt-spotlight__position-current',
            heroTitle:  '.ep-yt-spotlight__hero-title',
            heroDesc:   '.ep-yt-spotlight__hero-desc',
            scrollAxis: 'y'
        },
        cinema: {
            iframe:     '.ep-yt-cinema__iframe',
            list:       '.ep-yt-cinema__items',
            item:       '.ep-yt-cinema__item',
            activeItem: '.ep-yt-cinema__item.is-active',
            loader:     '.ep-yt-cinema__loader',
            loopBtn:    '.ep-yt-cinema__loop',
            shuffleBtn: '.ep-yt-cinema__shuffle',
            posCurrent: '.ep-yt-cinema__position-current',
            playPrev:     '.ep-yt-cinema__prev',
            playNext:     '.ep-yt-cinema__next',
            overlayOpen:  '.ep-yt-cinema__open-queue',
            overlayClose: '.ep-yt-cinema__close-queue',
            overlay:      '.ep-yt-cinema__overlay',
            scrollAxis: 'y'
        },
        magazine: {
            iframe:     '.ep-yt-magazine__iframe',
            list:       '.ep-yt-magazine__rail',
            item:       '.ep-yt-magazine__rail-item',
            activeItem: '.ep-yt-magazine__rail-item.is-active',
            loader:     '.ep-yt-magazine__loader',
            loopBtn:    '.ep-yt-magazine__loop',
            shuffleBtn: '.ep-yt-magazine__shuffle',
            posCurrent: '.ep-yt-magazine__position-current',
            heroTitle:  '.ep-yt-magazine__hero-title',
            heroDesc:   '.ep-yt-magazine__hero-desc',
            readMore:   '.ep-yt-magazine__readmore',
            scrollAxis: 'y'
        }
    };

    function ready(fn) {
        if (document.readyState !== 'loading') fn();
        else document.addEventListener('DOMContentLoaded', fn);
    }
    function $$(root, sel) { return Array.prototype.slice.call(root.querySelectorAll(sel)); }
    function $(root, sel) { return root.querySelector(sel); }

    function getLayout(root) {
        var l = root.getAttribute('data-layout') || 'queue';
        return SELECTORS[l] ? l : 'queue';
    }

    function swapVideoId(iframe, vid) {
        if (!iframe || !vid) return;
        try {
            var src = iframe.src;
            var next = src.replace(/(\/embed\/)([^?&"'>]+)(.*)?/, '$1' + vid + '$3');
            if (/[?&]autoplay=0/.test(next)) {
                next = next.replace(/([?&])autoplay=0/, '$1autoplay=1');
            } else if (next.indexOf('autoplay=') === -1) {
                next += (next.indexOf('?') === -1 ? '?' : '&') + 'autoplay=1';
            }
            iframe.src = next;
        } catch (err) { /* ignore */ }
    }

    function setActive(root, sel, item) {
        $$(root, sel.activeItem).forEach(function (el) { el.classList.remove('is-active'); });
        if (item) {
            item.classList.add('is-active');
            var list = $(root, sel.list);
            if (list && typeof item.scrollIntoView === 'function') {
                var listRect = list.getBoundingClientRect();
                var itemRect = item.getBoundingClientRect();
                var off = sel.scrollAxis === 'x'
                    ? (itemRect.left < listRect.left || itemRect.right > listRect.right)
                    : (itemRect.top < listRect.top || itemRect.bottom > listRect.bottom);
                if (off) {
                    item.scrollIntoView({ block: 'nearest', inline: 'nearest', behavior: 'smooth' });
                }
            }
        }
    }

    function updateCounter(root, sel) {
        var current = $(root, sel.posCurrent);
        var active  = $(root, sel.activeItem);
        if (current && active) {
            current.textContent = active.getAttribute('data-index') || '';
        }
    }

    function playByIndex(root, sel, index, state) {
        var items = $$(root, sel.item);
        if (!items.length) return;
        if (index >= items.length) {
            if (state.loop) index = 0;
            else return;
        }
        if (index < 0) index = 0;
        var item   = items[index];
        var iframe = $(root, sel.iframe);
        var vid    = item.getAttribute('data-vid');
        if (!vid || !iframe) return;
        swapVideoId(iframe, vid);
        setActive(root, sel, item);
        updateCounter(root, sel);
    }

    function visualIndex(root, sel, item) {
        var items = $$(root, sel.item);
        return items.indexOf(item);
    }

    function attachClickToSwap(root, sel, state) {
        var list = $(root, sel.list);
        if (!list) return;
        list.addEventListener('click', function (event) {
            var item = event.target.closest(sel.item);
            if (!item || !list.contains(item)) return;
            event.preventDefault();
            state.cursor = visualIndex(root, sel, item);
            playByIndex(root, sel, state.cursor, state);
            // Layouts that have a "hero" area (Spotlight, Magazine) promote
            // the clicked item's title + description into that area.
            if (sel.heroTitle || sel.heroDesc) {
                var titleEl = sel.heroTitle ? $(root, sel.heroTitle) : null;
                var descEl  = sel.heroDesc  ? $(root, sel.heroDesc)  : null;
                if (titleEl) {
                    var t = item.getAttribute('data-title') || '';
                    if (t) titleEl.textContent = t;
                }
                if (descEl) {
                    var d = item.getAttribute('data-description') || '';
                    if (d !== '') descEl.textContent = d;
                }
            }
            // Library: open the modal player on click.
            if (sel.modal && state.layout === 'library') {
                var modal = $(root, sel.modal);
                if (modal) modal.hidden = false;
            }
        });
    }

    // Cinema overlay queue + Library modal close wiring.
    function attachLayoutExtras(root, sel, state) {
        if (state.layout === 'cinema') {
            var openBtn  = $(root, sel.overlayOpen);
            var closeBtn = $(root, sel.overlayClose);
            var overlay  = $(root, sel.overlay);
            if (openBtn && overlay) openBtn.addEventListener('click', function () { overlay.classList.add('is-open'); });
            if (closeBtn && overlay) closeBtn.addEventListener('click', function () { overlay.classList.remove('is-open'); });
        }
        if (state.layout === 'library') {
            var modal = $(root, sel.modal);
            var close = $(root, sel.modalClose);
            if (close && modal) close.addEventListener('click', function () { modal.hidden = true; });
            if (modal) modal.addEventListener('click', function (event) {
                if (event.target === modal) modal.hidden = true; // backdrop click
            });
        }
        if (state.layout === 'magazine') {
            var more = $(root, sel.readMore);
            var desc = $(root, sel.heroDesc);
            if (more && desc) more.addEventListener('click', function () {
                desc.classList.toggle('is-expanded');
                more.textContent = desc.classList.contains('is-expanded')
                    ? (more.getAttribute('data-collapse-label') || 'Show less')
                    : (more.getAttribute('data-expand-label')   || 'Read more');
            });
        }
    }

    function attachLoopShuffle(root, sel, state) {
        var loopBtn    = $(root, sel.loopBtn);
        var shuffleBtn = $(root, sel.shuffleBtn);
        if (loopBtn) {
            loopBtn.addEventListener('click', function () {
                state.loop = !state.loop;
                loopBtn.setAttribute('aria-pressed', String(state.loop));
            });
        }
        if (shuffleBtn) {
            shuffleBtn.addEventListener('click', function () {
                state.shuffle = !state.shuffle;
                shuffleBtn.setAttribute('aria-pressed', String(state.shuffle));
                if (state.shuffle) shuffleOrder(root, sel, state);
                else restoreOrder(root, sel, state);
            });
        }
    }

    function shuffleOrder(root, sel, state) {
        var list = $(root, sel.list);
        if (!list) return;
        var items = $$(root, sel.item);
        if (!state.originalOrder) {
            state.originalOrder = items.map(function (i) { return i.getAttribute('data-vid'); });
        }
        var shuffled = items.slice();
        for (var i = shuffled.length - 1; i > 0; i--) {
            var j = Math.floor(Math.random() * (i + 1));
            var tmp = shuffled[i]; shuffled[i] = shuffled[j]; shuffled[j] = tmp;
        }
        shuffled.forEach(function (el) { list.appendChild(el); });
        var active = $(root, sel.activeItem);
        state.cursor = visualIndex(root, sel, active);
    }

    function restoreOrder(root, sel, state) {
        if (!state.originalOrder) return;
        var list = $(root, sel.list);
        if (!list) return;
        var byVid = {};
        $$(root, sel.item).forEach(function (el) {
            byVid[el.getAttribute('data-vid')] = el;
        });
        state.originalOrder.forEach(function (vid) {
            if (byVid[vid]) list.appendChild(byVid[vid]);
        });
        var active = $(root, sel.activeItem);
        state.cursor = visualIndex(root, sel, active);
    }

    function attachInfiniteScroll(root, sel, state) {
        var list = $(root, sel.list);
        if (!list) return;
        list.addEventListener('scroll', function () {
            if (state.loadingNext || !state.nextPageToken) return;
            var remaining = sel.scrollAxis === 'x'
                ? list.scrollWidth - list.scrollLeft - list.clientWidth
                : list.scrollHeight - list.scrollTop - list.clientHeight;
            if (remaining < INFINITE_SCROLL_PX) {
                fetchNextPage(root, sel, state);
            }
        });

        // Hard cap on initial-fill rounds — protects against pathological
        // cases where the container grows along with the strip and the
        // "is it scrollable yet?" predicate never flips to false.
        var fillRounds = 0;
        function fillUntilScrollable() {
            if (!state.nextPageToken || state.loadingNext) return;
            if (fillRounds >= 3) return;
            var room = sel.scrollAxis === 'x'
                ? list.scrollWidth <= list.clientWidth + 8
                : list.scrollHeight <= list.clientHeight + 8;
            if (room) {
                fillRounds++;
                fetchNextPage(root, sel, state).then(function () {
                    setTimeout(fillUntilScrollable, 50);
                });
            }
        }
        setTimeout(fillUntilScrollable, 50);
    }

    // Playback prev/next buttons (cinema): step the active video backward /
    // forward through the playlist, reusing the same cursor model as auto-loop.
    // Distinct from theatre's nav buttons, which scroll the card strip.
    function attachPlaybackNavButtons(root, sel, state) {
        var prev = sel.playPrev ? $(root, sel.playPrev) : null;
        var next = sel.playNext ? $(root, sel.playNext) : null;
        function step(delta) {
            var items = $$(root, sel.item);
            if (!items.length) return;
            var active = $(root, sel.activeItem);
            state.cursor = active ? visualIndex(root, sel, active) : state.cursor;
            var target = state.cursor + delta;
            if (target >= items.length) target = state.loop ? 0 : items.length - 1;
            if (target < 0) target = state.loop ? items.length - 1 : 0;
            state.cursor = target;
            playByIndex(root, sel, target, state);
        }
        if (prev) prev.addEventListener('click', function () { step(-1); });
        if (next) next.addEventListener('click', function () { step(1); });
    }

    function attachTheatreNavButtons(root, sel) {
        var prev = $(root, sel.navPrev);
        var next = $(root, sel.navNext);
        var list = $(root, sel.list);
        if (!list) return;
        function pageBy(dir) {
            list.scrollBy({ left: dir * list.clientWidth * 0.8, behavior: 'smooth' });
        }
        if (prev) prev.addEventListener('click', function () { pageBy(-1); });
        if (next) next.addEventListener('click', function () { pageBy(1); });
    }

    function fetchNextPage(root, sel, state) {
        if (state.loadingNext || !state.nextPageToken) return Promise.resolve();
        state.loadingNext = true;
        var loader = $(root, sel.loader);
        if (loader) loader.hidden = false;

        var data = window.embedpressFrontendData || {};
        var restUrl = (data.rest_url || '/wp-json/') + 'embedpress/v1/youtube-playlist-items';

        var params = new URLSearchParams({
            playlist_id: state.playlistId,
            page_token:  state.nextPageToken,
            page_size:   String(state.pageSize),
            offset:      String($$(root, sel.item).length),
            layout:      state.layout
        });

        var headers = { 'Accept': 'application/json' };
        if (data.rest_nonce) headers['X-WP-Nonce'] = data.rest_nonce;

        return fetch(restUrl + '?' + params.toString(), { headers: headers, credentials: 'same-origin' })
            .then(function (r) { return r.ok ? r.json() : Promise.reject(r); })
            .then(function (payload) {
                if (payload && payload.html) {
                    var list = $(root, sel.list);
                    list.insertAdjacentHTML('beforeend', payload.html);
                }
                state.nextPageToken = payload && payload.next_page_token ? payload.next_page_token : '';
                root.setAttribute('data-next-page-token', state.nextPageToken);
                if (state.shuffle) {
                    shuffleOrder(root, sel, state);
                } else {
                    state.originalOrder = $$(root, sel.item).map(function (i) {
                        return i.getAttribute('data-vid');
                    });
                }
            })
            .catch(function () { state.nextPageToken = ''; })
            .finally(function () {
                state.loadingNext = false;
                if (loader) loader.hidden = true;
            });
    }

    function attachEndedListener(root, sel, state) {
        var iframe = $(root, sel.iframe);
        if (!iframe) return;
        try {
            iframe.contentWindow.postMessage(JSON.stringify({
                event: 'listening',
                id:    state.id
            }), '*');
        } catch (e) { /* non-fatal */ }
    }

    function handleEnded(state) {
        var root = state.root;
        var sel  = SELECTORS[state.layout];
        if (state.shuffle) {
            var items = $$(root, sel.item);
            if (items.length <= 1) return;
            var next = state.cursor;
            for (var attempt = 0; attempt < 5; attempt++) {
                next = Math.floor(Math.random() * items.length);
                if (next !== state.cursor) break;
            }
            state.cursor = next;
            playByIndex(root, sel, next, state);
        } else {
            state.cursor++;
            playByIndex(root, sel, state.cursor, state);
        }
    }

    function bindGlobalMessageRouter() {
        if (window[INIT_FLAG + 'MsgBound']) return;
        window[INIT_FLAG + 'MsgBound'] = true;
        window.addEventListener('message', function (event) {
            if (typeof event.data !== 'string') return;
            if (event.origin.indexOf('youtube.com') === -1 && event.origin.indexOf('youtube-nocookie.com') === -1) return;
            var data;
            try { data = JSON.parse(event.data); } catch (e) { return; }
            if (data && data.event === 'onStateChange' && data.info === 0) {
                $$(document, ROOT_SELECTOR).forEach(function (root) {
                    var sel = SELECTORS[getLayout(root)];
                    var iframe = $(root, sel.iframe);
                    if (iframe && iframe.contentWindow === event.source) {
                        var state = root[INIT_FLAG + 'State'];
                        if (state) handleEnded(state);
                    }
                });
            }
        });
    }

    function initOne(root) {
        if (root[INIT_FLAG]) return;
        root[INIT_FLAG] = true;

        var layout = getLayout(root);
        var sel    = SELECTORS[layout];
        var active = $(root, sel.activeItem) || $(root, sel.item);
        var state  = {
            root:           root,
            layout:         layout,
            id:             'epyp-' + Math.random().toString(36).slice(2, 9),
            playlistId:     root.getAttribute('data-playlist-id') || '',
            pageSize:       parseInt(root.getAttribute('data-pagesize'), 10) || 6,
            nextPageToken:  root.getAttribute('data-next-page-token') || '',
            cursor:         active ? visualIndex(root, sel, active) : 0,
            loop:           false,
            shuffle:        false,
            loadingNext:    false,
            originalOrder:  null
        };
        root[INIT_FLAG + 'State'] = state;

        attachClickToSwap(root, sel, state);
        attachLoopShuffle(root, sel, state);
        attachInfiniteScroll(root, sel, state);
        attachEndedListener(root, sel, state);
        attachLayoutExtras(root, sel, state);
        attachPlaybackNavButtons(root, sel, state);
        if (layout === 'theatre') attachTheatreNavButtons(root, sel);
    }

    function init() {
        bindGlobalMessageRouter();
        $$(document, ROOT_SELECTOR).forEach(initOne);

        if (typeof window.MutationObserver === 'function') {
            var obs = new MutationObserver(function (mutations) {
                mutations.forEach(function (m) {
                    Array.prototype.forEach.call(m.addedNodes || [], function (node) {
                        if (node.nodeType !== 1) return;
                        if (node.matches && node.matches(ROOT_SELECTOR)) initOne(node);
                        if (node.querySelectorAll) {
                            Array.prototype.forEach.call(node.querySelectorAll(ROOT_SELECTOR), initOne);
                        }
                    });
                });
            });
            obs.observe(document.body, { childList: true, subtree: true });
        }
    }

    ready(init);
})();
