/*
 * whp-async-forms.js — progressive-enhancement async form submission.
 *
 * Opt a form in with `data-whp-async`. On submit it posts via fetch (no full
 * page reload / no lost scroll position), shows a toast from the JSON response,
 * and updates the DOM according to a small set of declarative attributes.
 *
 * The matching server handler must, when it sees the `X-WHP-Async: 1` request
 * header, emit JSON `{ "success": bool, "message": "..." }` and exit instead of
 * re-rendering the page. (Helper: whp_is_async_request() in includes/whp-async.php.)
 *
 * Form attributes:
 *   data-whp-async                 enable async submit
 *   data-whp-confirm="text"        confirm() before submitting (replaces inline
 *                                  onsubmit="return confirm(...)")
 *   data-whp-remove-row            on success, fade out + remove the form's closest
 *                                  <tr> (clean async delete — no reload)
 *   data-whp-remove="#sel"         on success, remove the element matching #sel
 *   data-whp-reset                 on success, reset the form fields
 *   data-whp-refresh               on success, soft-refresh the page region named by
 *                                  data-whp-refresh-target (see below) by re-fetching
 *                                  the page; falls back to a scroll-preserving reload
 *   data-whp-refresh-target="#sel" element to swap during a refresh
 *
 * Default when none of remove/refresh is set: a scroll-preserving reload (position
 * restored), so creates re-render correctly with all widgets re-initialised while
 * never dumping the user back to the top of the page.
 */
(function () {
    'use strict';

    var SCROLL_KEY = 'whp-restore-scroll';

    function toast(msg, kind) {
        var wrap = document.getElementById('whp-async-toast');
        if (!wrap) {
            wrap = document.createElement('div');
            wrap.id = 'whp-async-toast';
            wrap.className = 'position-fixed bottom-0 end-0 p-3';
            wrap.style.zIndex = '1090';
            document.body.appendChild(wrap);
        }
        var el = document.createElement('div');
        el.className = 'alert alert-' + (kind || 'success') + ' shadow';
        el.setAttribute('role', (kind === 'danger' || kind === 'warning') ? 'alert' : 'status');
        el.textContent = msg;
        wrap.appendChild(el);
        setTimeout(function () { el.remove(); }, 4500);
    }

    function preserveScrollReload() {
        try { sessionStorage.setItem(SCROLL_KEY, JSON.stringify({ p: location.pathname + location.search, y: window.scrollY })); } catch (e) {}
        location.reload();
    }

    // Restore scroll after a preserve-scroll reload.
    document.addEventListener('DOMContentLoaded', function () {
        try {
            var raw = sessionStorage.getItem(SCROLL_KEY);
            if (raw) {
                var s = JSON.parse(raw);
                sessionStorage.removeItem(SCROLL_KEY);
                if (s && s.p === location.pathname + location.search) { window.scrollTo(0, s.y || 0); }
            }
        } catch (e) {}
    });

    function softRefresh(targetSel) {
        return fetch(location.href, { headers: { 'X-WHP-Async': '1' }, credentials: 'same-origin' })
            .then(function (r) { return r.text(); })
            .then(function (html) {
                var doc = new DOMParser().parseFromString(html, 'text/html');
                var fresh = doc.querySelector(targetSel), cur = document.querySelector(targetSel);
                if (!fresh || !cur) { preserveScrollReload(); return; }
                cur.innerHTML = fresh.innerHTML;
                // Re-enhance any shared widgets inside the swapped region.
                if (window.WhpTable) {
                    cur.querySelectorAll('table[data-whp-table]').forEach(function (t) {
                        t.__whpEnhanced = false; window.WhpTable.enhance(t);
                    });
                }
            })
            .catch(function () { preserveScrollReload(); });
    }

    document.addEventListener('submit', function (ev) {
        var form = ev.target.closest && ev.target.closest('form[data-whp-async]');
        if (!form) { return; }
        ev.preventDefault();

        if (form.dataset.whpConfirm && !window.confirm(form.dataset.whpConfirm)) { return; }

        var submitBtn = form.querySelector('[type="submit"]');
        var btnHtml = submitBtn ? submitBtn.innerHTML : null;
        if (submitBtn) { submitBtn.disabled = true; }

        // Post to a dedicated JSON endpoint (data-whp-action) when given — required
        // because WHP pages render inside index.php's layout and can't return clean
        // JSON themselves. Falls back to the form's own action for simple cases.
        fetch(form.dataset.whpAction || form.getAttribute('action') || location.href, {
            method: (form.getAttribute('method') || 'POST').toUpperCase(),
            body: new FormData(form),
            headers: { 'X-WHP-Async': '1' },
            credentials: 'same-origin'
        })
        .then(function (r) { return r.json(); })
        .then(function (data) {
            // Prefer the server's severity (success/warning/info/danger) for the
            // toast colour; fall back to the boolean when no type is provided.
            var kind = data.type;
            if (['success', 'warning', 'info', 'danger'].indexOf(kind) === -1) {
                kind = data.success ? 'success' : 'danger';
            }
            toast(data.message || (data.success ? 'Done.' : 'Action failed.'), kind);
            if (!data.success) { return; }
            if (form.hasAttribute('data-whp-remove-row')) {
                var tr = form.closest('tr');
                if (tr) { tr.style.transition = 'opacity .25s'; tr.style.opacity = '0'; setTimeout(function () { tr.remove(); }, 260); }
            } else if (form.dataset.whpRemove) {
                var el = document.querySelector(form.dataset.whpRemove); if (el) { el.remove(); }
            } else if (form.hasAttribute('data-whp-refresh')) {
                if (form.dataset.whpReset) { form.reset(); }
                softRefresh(form.dataset.whpRefreshTarget || form.dataset.whpRefresh);
            } else {
                preserveScrollReload();
            }
            if (form.dataset.whpReset && !form.hasAttribute('data-whp-refresh')) { form.reset(); }
        })
        .catch(function (e) { toast('Request failed: ' + e.message, 'danger'); })
        .finally(function () { if (submitBtn) { submitBtn.disabled = false; if (btnHtml !== null) submitBtn.innerHTML = btnHtml; } });
    });
})();
