/**
 * SailWP iframe bridge — runs on every front-end page that's rendered
 * inside the cloud dashboard iframe. Sole job: keep parent and iframe
 * in sync via postMessage. The PHP side handled headers + auto-login;
 * this script is the runtime channel.
 *
 * Outgoing messages (iframe → parent):
 *   { type: 'swcp:ready',        url, title }
 *   { type: 'swcp:nav-change',   url, title }
 *   { type: 'swcp:edit-applied', postId, savedAt }   // dispatched by inline-edit.js
 *
 * Incoming messages (parent → iframe):
 *   { type: 'swcp:navigate', url }   // hard navigation inside iframe
 *   { type: 'swcp:refresh' }          // reload current page
 *   { type: 'swcp:scroll', x, y }    // scroll to coordinates
 *
 * Origin allowlist comes from the localized SailWPCopilotIframe global,
 * which PHP populates from SailWP_Copilot_Iframe_Bridge::ALLOWED_ANCESTORS.
 */
(function () {
    'use strict';

    // Bail outside an iframe — nothing to bridge.
    if (window.top === window.self) return;

    var cfg = window.SailWPCopilotIframe || {};
    var allowedOrigins = Array.isArray(cfg.allowedOrigins) ? cfg.allowedOrigins : [];

    function isAllowed(origin) {
        if (!origin || origin === 'null') return false;
        for (var i = 0; i < allowedOrigins.length; i++) {
            if (allowedOrigins[i] === origin) return true;
        }
        return false;
    }

    function send(payload) {
        // postMessage to '*' is technically open, but we never include
        // sensitive data — only the URL/title the parent can already see
        // by virtue of having loaded the iframe. The parent verifies the
        // sender via event.source, not origin.
        try { window.parent.postMessage(payload, '*'); } catch (e) {}
    }

    function snapshot(type) {
        return {
            type: type,
            url: location.href,
            path: location.pathname + location.search + location.hash,
            title: document.title || '',
        };
    }

    // 1. Announce on first paint.
    function announce() {
        send(snapshot('swcp:ready'));
    }
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', announce, { once: true });
    } else {
        announce();
    }

    // 2. Track in-page navigation (pushState SPAs, hash links). For full
    //    page navigations we re-announce on load of the next page; this
    //    catches the cases where the URL changes without a reload.
    (function patchHistory() {
        var push = history.pushState;
        var replace = history.replaceState;
        history.pushState = function () {
            var r = push.apply(this, arguments);
            send(snapshot('swcp:nav-change'));
            return r;
        };
        history.replaceState = function () {
            var r = replace.apply(this, arguments);
            send(snapshot('swcp:nav-change'));
            return r;
        };
        window.addEventListener('popstate', function () { send(snapshot('swcp:nav-change')); });
        window.addEventListener('hashchange', function () { send(snapshot('swcp:nav-change')); });
    })();

    // 3. Bubble inline-editor saves up to the parent so the dashboard's
    //    activity feed can refresh without polling. inline-edit.js
    //    dispatches this custom event after a successful update-design
    //    POST — defined in v0.25.22 alongside this bridge.
    window.addEventListener('swcp:design-saved', function (ev) {
        var detail = ev && ev.detail ? ev.detail : {};
        send({
            type: 'swcp:edit-applied',
            postId: detail.postId || null,
            savedAt: Date.now(),
        });
    });

    // 4. Preserve edit-mode across navigation. Even though the plugin sets
    //    a session cookie (swcp_iframe_edit) as the primary signal, we add
    //    ?swcp_edit=1 to every same-origin link in the iframe as a visible
    //    cue (URL shows "I am editing") and as defense in depth — if a
    //    plugin or theme clears the cookie we still survive on the param.
    document.addEventListener('click', function (ev) {
        // Only plain left-clicks; let cmd/ctrl/shift/middle-click through.
        if (ev.defaultPrevented || ev.button !== 0) return;
        if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
        var link = ev.target && ev.target.closest ? ev.target.closest('a[href]') : null;
        if (!link) return;
        var href = link.getAttribute('href');
        if (!href || href.charAt(0) === '#') return;
        try {
            var u = new URL(href, location.href);
            if (u.origin !== location.origin) return; // external — leave alone
            if (u.searchParams.has('swcp_edit')) return;
            u.searchParams.set('swcp_edit', '1');
            link.setAttribute('href', u.toString());
        } catch (e) { /* malformed href — leave alone */ }
    }, true);

    // 5. Listen for commands from the parent. Cross-origin postMessage
    //    is the trust boundary — only the dashboard origins may steer.
    window.addEventListener('message', function (ev) {
        if (!isAllowed(ev.origin)) return;
        var msg = ev.data;
        if (!msg || typeof msg !== 'object' || typeof msg.type !== 'string') return;

        switch (msg.type) {
            case 'swcp:navigate':
                if (typeof msg.url === 'string' && msg.url.length) {
                    // Same-origin navigation only — refuse cross-origin
                    // hops, the dashboard should never need them.
                    try {
                        var target = new URL(msg.url, location.href);
                        if (target.origin === location.origin) {
                            location.href = target.href;
                        }
                    } catch (_) {}
                }
                break;
            case 'swcp:refresh':
                location.reload();
                break;
            case 'swcp:scroll':
                window.scrollTo({
                    left: Number(msg.x) || 0,
                    top:  Number(msg.y) || 0,
                    behavior: 'smooth',
                });
                break;
        }
    });
})();
