/**
 * CatalogBrowsing — UX glue for the ProductCategory listing page.
 *
 * Responsibilities:
 *   - Initialize noUiSlider on every [data-filter-price-slider] and
 *     [data-filter-range-slider] element; wire two-way sync with the
 *     accompanying number inputs.
 *   - Swatch click → toggle the underlying checkbox + visual selected
 *     state (the form still submits via standard browser flow).
 *   - Auto-submit the form on filter-input change when the user pauses
 *     for ~600ms (debounced) — desktop only. Mobile keeps explicit
 *     "Apply" button submit to avoid mid-typing reloads.
 *
 * noUiSlider is BUNDLED here (SystemFindings/task-16d). It used to be a
 * jsdelivr <link> + <script defer> pair in web.blade.php, justified in a comment
 * as "keeps the Vite bundle slim; falls back to plain number inputs when
 * blocked". Two problems with that trade, and the second is the one that
 * matters:
 *   - the fallback is silent. `typeof window.noUiSlider === 'undefined'` returns
 *     early below, so a jsdelivr outage does not break the page, it removes the
 *     price and range filters — a broken filter UI that reports nothing;
 *   - the version was pinned in a URL nobody greps (15.7.1), which is finding
 *     #19's drift half. It is pinned in package.json now, exactly, so the bundle
 *     and the old URL name the same release.
 * The early returns are KEPT: they are cheap, and they still describe the truth
 * (the sliders only initialise where the markup asks for them).
 *
 * Test markers used by Playwright:
 *   [data-catalog-form]
 *   [data-filter-price-slider]
 *   [data-filter-range-slider]
 *   [data-filter-color-swatch]
 */

// noUiSlider — the stock stylesheet plus the module itself (task-16d).
//
// 🔑 THE STOCK CSS IS IMPORTED FIRST, AND THAT IS NOT COSMETIC. catalog-browsing
// .css below says, in its own words, "these are theme overrides" — .noUi-target,
// .noUi-connect and .noUi-handle are restyled there at the SAME specificity the
// stock sheet uses, so whichever arrives last wins. The <link> order in
// web.blade.php was stock-then-theme; swapping these two lines reproduces the
// stock slider on the catalogue page with nothing to show for it.
import 'nouislider/dist/nouislider.css';

// task-116b: import the companion stylesheet here so it lands in this entry's
// CSS chunk (build/assets/catalog-browsing.css). A standalone css/catalog-
// browsing.css entry would collide on the "catalog-browsing" basename with this
// JS entry and rollup would rename the JS bundle to catalog-browsing2.js. See
// web.js for the full rationale.
import '../css/catalog-browsing.css';

// `window.noUiSlider` is what the code below reads — the CDN <script> set that
// global, and keeping it means the `typeof window.noUiSlider === 'undefined'`
// guards stay meaningful rather than becoming dead branches.
import * as noUiSlider from 'nouislider';

window.noUiSlider = noUiSlider;

(function () {
    'use strict';

    function ready(fn) {
        if (document.readyState !== 'loading') {
            fn();
        } else {
            document.addEventListener('DOMContentLoaded', fn);
        }
    }

    ready(function () {
        initPriceSlider();
        initRangeSliders();
        initColorSwatches();
        initDebouncedAutosubmit();
    });

    /**
     * Initialize the price range slider (single instance per page).
     */
    function initPriceSlider() {
        const el = document.querySelector('[data-filter-price-slider]');
        if (!el || typeof window.noUiSlider === 'undefined') {
            return;
        }
        const min = parseFloat(el.dataset.priceMin || '0');
        const max = parseFloat(el.dataset.priceMax || '0');
        if (!(max > min)) {
            // Singleton or empty range — skip the slider init.
            return;
        }
        const selectedMin = el.dataset.priceSelectedMin ? parseFloat(el.dataset.priceSelectedMin) : min;
        const selectedMax = el.dataset.priceSelectedMax ? parseFloat(el.dataset.priceSelectedMax) : max;

        window.noUiSlider.create(el, {
            start: [selectedMin, selectedMax],
            connect: true,
            range: { min: min, max: max },
            step: 1,
        });

        const minInput = document.querySelector('[data-filter-price-input="min"]');
        const maxInput = document.querySelector('[data-filter-price-input="max"]');

        el.noUiSlider.on('update', function (values) {
            if (minInput) {
                minInput.value = Math.round(parseFloat(values[0]));
            }
            if (maxInput) {
                maxInput.value = Math.round(parseFloat(values[1]));
            }
        });

        // Sync the input → slider when user types directly.
        [minInput, maxInput].forEach(function (input) {
            if (!input) return;
            input.addEventListener('change', function () {
                const a = parseFloat(minInput && minInput.value !== '' ? minInput.value : min);
                const b = parseFloat(maxInput && maxInput.value !== '' ? maxInput.value : max);
                el.noUiSlider.set([a, b]);
            });
        });
    }

    /**
     * Initialize attribute range sliders (`number` kind facets).
     */
    function initRangeSliders() {
        if (typeof window.noUiSlider === 'undefined') {
            return;
        }
        const sliders = document.querySelectorAll('[data-filter-range-slider]');
        sliders.forEach(function (el) {
            const tag = el.dataset.rangeTag;
            const min = parseFloat(el.dataset.rangeMin || '0');
            const max = parseFloat(el.dataset.rangeMax || '0');
            if (!(max > min) || !tag) {
                return;
            }
            const selectedMin = el.dataset.rangeSelectedMin ? parseFloat(el.dataset.rangeSelectedMin) : min;
            const selectedMax = el.dataset.rangeSelectedMax ? parseFloat(el.dataset.rangeSelectedMax) : max;

            window.noUiSlider.create(el, {
                start: [selectedMin, selectedMax],
                connect: true,
                range: { min: min, max: max },
                step: 1,
            });

            const minInput = document.querySelector('input[data-filter-range-input="min"][name="num[' + tag + '][min]"]');
            const maxInput = document.querySelector('input[data-filter-range-input="max"][name="num[' + tag + '][max]"]');

            el.noUiSlider.on('update', function (values) {
                if (minInput) {
                    minInput.value = Math.round(parseFloat(values[0]));
                }
                if (maxInput) {
                    maxInput.value = Math.round(parseFloat(values[1]));
                }
            });

            [minInput, maxInput].forEach(function (input) {
                if (!input) return;
                input.addEventListener('change', function () {
                    const a = parseFloat(minInput && minInput.value !== '' ? minInput.value : min);
                    const b = parseFloat(maxInput && maxInput.value !== '' ? maxInput.value : max);
                    el.noUiSlider.set([a, b]);
                });
            });
        });
    }

    /**
     * Wire color swatch clicks. The underlying [type=checkbox] still
     * drives form submission — we just visually toggle the swatch.
     */
    function initColorSwatches() {
        document.querySelectorAll('[data-filter-color-swatch]').forEach(function (swatch) {
            swatch.addEventListener('click', function () {
                // Defer to next tick so the native checkbox toggle has
                // settled first.
                setTimeout(function () {
                    const input = document.getElementById(swatch.getAttribute('for'));
                    if (!input) return;
                    if (input.checked) {
                        swatch.classList.add('is-selected');
                    } else {
                        swatch.classList.remove('is-selected');
                    }
                }, 0);
            });
        });
    }

    /**
     * Debounced auto-submit on filter input change. Desktop only —
     * mobile relies on the explicit Apply button to avoid awkward
     * partial-typing reloads.
     */
    function initDebouncedAutosubmit() {
        const form = document.querySelector('[data-catalog-form]');
        if (!form) {
            return;
        }
        let timer = null;
        const debounce = function (delay) {
            return function () {
                if (timer) clearTimeout(timer);
                timer = setTimeout(function () {
                    // Strip any cursor when filters change.
                    const stale = form.querySelector('input[name="page_cursor"]');
                    if (stale) stale.remove();
                    form.submit();
                }, delay);
            };
        };

        // Only enable debounced auto-submit on desktop (≥ lg breakpoint).
        if (window.matchMedia('(max-width: 991.98px)').matches) {
            return;
        }

        form.addEventListener('change', function (event) {
            const target = event.target;
            if (!target) return;
            // Sort dropdown already submits via its own onchange handler.
            if (target.matches('[data-sort-dropdown]')) return;
            // Number inputs — debounce more aggressively (700ms) to allow typing
            // a 3-digit value without firing.
            const delay = target.matches('input[type="number"]') ? 700 : 400;
            debounce(delay)();
        });
    }
})();
