// Generic CSS-stylable replacement for <select>. Native <option> rendering on
// Firefox/Chrome is partly OS-controlled and ignores most CSS; this gives full
// styling control via div-based listbox markup.
//
// Markup contract:
//   <input type="hidden" id="X" name="..." value="...">
//   <div data-combobox data-combobox-target="X"
//        role="combobox" aria-expanded="false" aria-haspopup="listbox"
//        tabindex="0">
//       <span class="sea-select-current">…visible label…</span>
//       <span class="sea-select-arrow" aria-hidden="true">▾</span>
//       <ul class="sea-select-list" role="listbox">
//           <li role="option" data-value="..." aria-selected="true|false">…</li>
//           …
//       </ul>
//   </div>
//
// On selection the hidden input's value is updated and a 'change' event fires
// on it, so existing consumers (`document.getElementById('X').value`) keep working.
var Combobox = (function () {
    'use strict';

    function init(combo) {
        if (!combo || combo.dataset.comboboxInit) return;
        combo.dataset.comboboxInit = '1';

        var hiddenId = combo.dataset.comboboxTarget;
        var hidden   = hiddenId && document.getElementById(hiddenId);
        var current  = combo.querySelector('.sea-select-current');
        var list     = combo.querySelector('.sea-select-list');
        var options  = list ? Array.from(list.querySelectorAll('[role="option"]')) : [];
        var focusIdx = options.findIndex(function (o) { return o.getAttribute('aria-selected') === 'true'; });
        if (focusIdx < 0) focusIdx = 0;

        function isOpen() { return combo.getAttribute('aria-expanded') === 'true'; }
        function open() {
            combo.setAttribute('aria-expanded', 'true');
            // Re-focus the currently-selected option each time we open
            focusIdx = options.findIndex(function (o) { return o.getAttribute('aria-selected') === 'true'; });
            if (focusIdx < 0) focusIdx = 0;
            highlight();
        }
        function close() { combo.setAttribute('aria-expanded', 'false'); }
        function toggle() { isOpen() ? close() : open(); }

        function highlight() {
            options.forEach(function (o, i) {
                o.classList.toggle('sea-select-option--focus', i === focusIdx);
            });
        }

        function select(i) {
            if (i < 0 || i >= options.length) return;
            options.forEach(function (o, idx) {
                o.setAttribute('aria-selected', idx === i ? 'true' : 'false');
            });
            var picked = options[i];
            if (current) current.textContent = picked.textContent.trim();
            if (hidden && hidden.value !== picked.dataset.value) {
                hidden.value = picked.dataset.value;
                hidden.dispatchEvent(new Event('change', { bubbles: true }));
            }
            close();
        }

        combo.addEventListener('click', function (e) {
            var opt = e.target.closest('[role="option"]');
            if (opt) {
                var i = options.indexOf(opt);
                if (i >= 0) select(i);
                return;
            }
            toggle();
        });

        combo.addEventListener('keydown', function (e) {
            switch (e.key) {
                case 'ArrowDown':
                    e.preventDefault();
                    if (!isOpen()) { open(); }
                    else { focusIdx = Math.min(options.length - 1, focusIdx + 1); highlight(); }
                    break;
                case 'ArrowUp':
                    e.preventDefault();
                    if (!isOpen()) { open(); }
                    else { focusIdx = Math.max(0, focusIdx - 1); highlight(); }
                    break;
                case 'Enter':
                case ' ':
                    e.preventDefault();
                    if (isOpen()) select(focusIdx);
                    else open();
                    break;
                case 'Escape':
                    if (isOpen()) { e.preventDefault(); close(); }
                    break;
                case 'Home':
                    if (isOpen()) { e.preventDefault(); focusIdx = 0; highlight(); }
                    break;
                case 'End':
                    if (isOpen()) { e.preventDefault(); focusIdx = options.length - 1; highlight(); }
                    break;
            }
        });

        // Click outside the combo closes the dropdown
        document.addEventListener('click', function (e) {
            if (!combo.contains(e.target)) close();
        });
    }

    function initAll(root) {
        (root || document).querySelectorAll('[data-combobox]').forEach(init);
    }

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

    return { init: init, initAll: initAll };
}());
