/**
 * Reusable icon-grid selector (replaces a plain <select>).
 *
 * Usage:
 *   import { IconSelect } from '../core/icon-select.ts';
 *
 *   const sel = new IconSelect({
 *       target: document.getElementById('my-select'),   // the <select> to enhance
 *       items: [
 *           { value: 'fire',  icon: '<svg>…</svg>', label: 'Fire',  desc: 'Warm flickering effect' },
 *           { value: 'water', icon: '<svg>…</svg>', label: 'Water', desc: 'Cool flowing colors' },
 *       ],
 *       onChange: (value) => { … },   // optional callback after selection
 *       columns: 2,                   // grid columns (default: 2)
 *   });
 *
 * The original <select> is hidden but stays in the DOM (value kept in sync).
 * Call sel.setValue(v) to change programmatically, sel.destroy() to remove.
 */

import { desktopFocus } from './ui.ts';

const POPUP_CLASS = 'icon-select-popup';

/** Close every open icon-select popup. */
export function closeAllIconSelects() {
    document.querySelectorAll(`.${POPUP_CLASS}`).forEach(p => {
        (p as HTMLElement).classList.remove('open');
    });
}

// Global click-away listener (registered once)
let _globalListenerAdded = false;
function _ensureGlobalListener() {
    if (_globalListenerAdded) return;
    _globalListenerAdded = true;
    document.addEventListener('click', (e) => {
        const target = e.target as HTMLElement;
        if (!target.closest(`.${POPUP_CLASS}`) && !target.closest('.icon-select-trigger')) {
            closeAllIconSelects();
        }
    });
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') closeAllIconSelects();
    });
}

export interface IconSelectItem {
    value: string;
    icon: string;
    label: string;
    desc?: string;
}

export interface IconSelectOpts {
    target: HTMLSelectElement;
    items: IconSelectItem[];
    onChange?: (value: string) => void;
    columns?: number;
    placeholder?: string;
    searchable?: boolean;
    searchPlaceholder?: string;
}

export class IconSelect {
    _select: HTMLSelectElement;
    _items: IconSelectItem[];
    _onChange: ((value: string) => void) | undefined;
    _columns: number;
    _placeholder: string;
    _searchable: boolean;
    _searchPlaceholder: string;
    _trigger: HTMLButtonElement;
    _popup: HTMLDivElement;
    _searchInput: HTMLInputElement | null = null;
    _scrollHandler: (() => void) | null = null;
    _scrollTargets: (HTMLElement | Window)[] = [];

    constructor({ target, items, onChange, columns = 2, placeholder = '', searchable = false, searchPlaceholder = 'Filter…' }: IconSelectOpts) {
        _ensureGlobalListener();

        this._select = target;
        this._items = items;
        this._onChange = onChange;
        this._columns = columns;
        this._placeholder = placeholder;
        this._searchable = searchable;
        this._searchPlaceholder = searchPlaceholder;

        // Hide the native select
        this._select.style.display = 'none';

        // Build trigger button (inserted next to select)
        this._trigger = document.createElement('button');
        this._trigger.type = 'button';
        this._trigger.className = 'icon-select-trigger';
        this._trigger.addEventListener('click', (e) => {
            e.stopPropagation();
            this._toggle();
        });
        this._select.parentNode!.insertBefore(this._trigger, this._select.nextSibling);

        // Build popup (portaled to body to avoid overflow clipping)
        this._popup = document.createElement('div');
        this._popup.className = POPUP_CLASS;
        this._popup.addEventListener('click', (e) => e.stopPropagation());
        this._popup.innerHTML = this._buildGrid();
        document.body.appendChild(this._popup);

        this._bindPopupEvents();

        // Sync to current select value
        this._syncTrigger();
    }

    _bindPopupEvents() {
        // Bind item clicks
        this._popup.querySelectorAll('.icon-select-cell').forEach(cell => {
            cell.addEventListener('click', () => {
                this.setValue((cell as HTMLElement).dataset.value!, true);
                this._popup.classList.remove('open');
                this._removeScrollListener();
            });
        });

        // Bind search input
        this._searchInput = this._popup.querySelector('.icon-select-search') as HTMLInputElement | null;
        if (this._searchInput) {
            this._searchInput.addEventListener('input', () => {
                const q = this._searchInput!.value.toLowerCase().trim();
                this._popup.querySelectorAll('.icon-select-cell').forEach(cell => {
                    const el = cell as HTMLElement;
                    el.classList.toggle('disabled', !!q && !el.dataset.search!.includes(q));
                });
            });
        }
    }

    _buildGrid() {
        const cells = this._items.map(item => {
            const search = (item.label + ' ' + (item.desc || '')).toLowerCase();
            return `<div class="icon-select-cell" data-value="${item.value}" data-search="${search}">
                <span class="icon-select-cell-icon">${item.icon}</span>
                <span class="icon-select-cell-label">${item.label}</span>
                ${item.desc ? `<span class="icon-select-cell-desc">${item.desc}</span>` : ''}
            </div>`;
        }).join('');

        const searchHTML = this._searchable
            ? `<input class="icon-select-search" type="text" placeholder="${this._searchPlaceholder}" autocomplete="off">`
            : '';
        return searchHTML + `<div class="icon-select-grid" style="grid-template-columns:repeat(${this._columns},1fr)">${cells}</div>`;
    }

    _syncTrigger() {
        const val = this._select.value;
        const item = this._items.find(i => i.value === val);
        if (item) {
            this._trigger.innerHTML =
                `<span class="icon-select-trigger-icon">${item.icon}</span>` +
                `<span class="icon-select-trigger-label">${item.label}</span>` +
                `<span class="icon-select-trigger-arrow">&#x25BE;</span>`;
        } else if (this._placeholder) {
            this._trigger.innerHTML =
                `<span class="icon-select-trigger-label">${this._placeholder}</span>` +
                `<span class="icon-select-trigger-arrow">&#x25BE;</span>`;
        }
        // Update active state in grid
        this._popup.querySelectorAll('.icon-select-cell').forEach(cell => {
            const el = cell as HTMLElement;
            el.classList.toggle('active', el.dataset.value === val);
        });
    }

    _positionPopup() {
        const rect = this._trigger.getBoundingClientRect();
        const gap = 6;               // visual gap between trigger and popup
        const pad = 8;               // min distance from viewport edge
        const popupW = Math.max(rect.width, 200);
        const spaceBelow = window.innerHeight - rect.bottom - gap - pad;
        const spaceAbove = rect.top - gap - pad;

        // Determine direction
        const openUp = spaceBelow < 200 && spaceAbove > spaceBelow;
        const available = openUp ? spaceAbove : spaceBelow;

        // Horizontal: clamp so popup doesn't overflow right edge
        let left = rect.left;
        if (left + popupW > window.innerWidth - pad) {
            left = window.innerWidth - pad - popupW;
        }
        if (left < pad) left = pad;

        this._popup.style.left = left + 'px';
        this._popup.style.width = popupW + 'px';
        this._popup.style.maxHeight = available + 'px';

        if (openUp) {
            this._popup.style.top = '';
            this._popup.style.bottom = (window.innerHeight - rect.top + gap) + 'px';
        } else {
            this._popup.style.top = (rect.bottom + gap) + 'px';
            this._popup.style.bottom = '';
        }
    }

    _toggle() {
        const wasOpen = this._popup.classList.contains('open');
        closeAllIconSelects();
        if (!wasOpen) {
            this._positionPopup();
            this._popup.classList.add('open');
            this._addScrollListener();
            if (this._searchInput) {
                this._searchInput.value = '';
                this._popup.querySelectorAll('.icon-select-cell').forEach(cell => {
                    (cell as HTMLElement).classList.remove('disabled');
                });
                requestAnimationFrame(() => desktopFocus(this._searchInput!));
            }
        }
    }

    /** Close popup when any scrollable ancestor scrolls (prevents stale position). */
    _addScrollListener() {
        if (this._scrollHandler) return;
        this._scrollHandler = () => {
            this._popup.classList.remove('open');
            this._removeScrollListener();
        };
        // Listen on capture phase to catch scroll on any ancestor
        let el: Node | null = this._trigger.parentNode;
        this._scrollTargets = [];
        while (el && el !== document) {
            const htmlEl = el as HTMLElement;
            if (htmlEl.scrollHeight > htmlEl.clientHeight || htmlEl.classList?.contains('modal-content')) {
                htmlEl.addEventListener('scroll', this._scrollHandler, { passive: true });
                this._scrollTargets.push(htmlEl);
            }
            el = el.parentNode;
        }
        window.addEventListener('scroll', this._scrollHandler, { passive: true });
        this._scrollTargets.push(window);
    }

    _removeScrollListener() {
        if (!this._scrollHandler) return;
        for (const el of this._scrollTargets) {
            el.removeEventListener('scroll', this._scrollHandler);
        }
        this._scrollTargets = [];
        this._scrollHandler = null;
    }

    /** Change the value programmatically. */
    setValue(value: string, fireChange = false) {
        this._select.value = value;
        this._syncTrigger();
        if (fireChange) {
            // Fire native change event so existing onchange handlers work
            this._select.dispatchEvent(new Event('change', { bubbles: true }));
            if (this._onChange) this._onChange(value);
        }
    }

    /** Refresh labels (e.g. after language change). Call with new items array. */
    updateItems(items: IconSelectItem[]) {
        this._items = items;
        this._popup.innerHTML = this._buildGrid();
        this._bindPopupEvents();
        this._syncTrigger();
    }

    /** Remove the enhancement, restore native <select>. */
    destroy() {
        this._removeScrollListener();
        this._trigger.remove();
        this._popup.remove();
        this._select.style.display = '';
    }
}

/**
 * Show a standalone type-picker overlay.
 *
 * Displays a centered modal with an icon grid. When the user picks a type,
 * the overlay closes and `onPick(value)` is called. Clicking the backdrop
 * or pressing Escape dismisses without picking.
 */
export function showTypePicker({ title, items, onPick }: { title: string; items: IconSelectItem[]; onPick: (value: string) => void }) {
    const showFilter = items.length > 9;

    // Build cells
    const cells = items.map(item =>
        `<div class="icon-select-cell" data-value="${item.value}" data-search="${(item.label + ' ' + (item.desc || '')).toLowerCase()}">
            <span class="icon-select-cell-icon">${item.icon}</span>
            <span class="icon-select-cell-label">${item.label}</span>
            ${item.desc ? `<span class="icon-select-cell-desc">${item.desc}</span>` : ''}
        </div>`
    ).join('');

    // Create overlay
    const overlay = document.createElement('div');
    overlay.className = 'type-picker-overlay';
    overlay.innerHTML = `
        <div class="type-picker-dialog">
            <div class="type-picker-title">${title}</div>
            ${showFilter ? '<input class="type-picker-filter" type="text" placeholder="Filter…" autocomplete="off">' : ''}
            <div class="icon-select-grid">${cells}</div>
        </div>`;
    document.body.appendChild(overlay);

    const close = () => { overlay.remove(); document.removeEventListener('keydown', onKey); };

    // Filter logic
    if (showFilter) {
        const input = overlay.querySelector('.type-picker-filter') as HTMLInputElement;
        const allCells = overlay.querySelectorAll('.icon-select-cell');
        input.addEventListener('input', () => {
            const q = input.value.toLowerCase().trim();
            allCells.forEach(cell => {
                const el = cell as HTMLElement;
                const match = !q || el.dataset.search!.includes(q);
                el.classList.toggle('disabled', !match);
            });
        });
        // Auto-focus filter after animation (skip on touch devices to avoid keyboard popup)
        requestAnimationFrame(() => setTimeout(() => desktopFocus(input), 200));
    }

    // Backdrop click
    overlay.addEventListener('click', (e) => {
        if (e.target === overlay) close();
    });

    // Escape key
    const onKey = (e: KeyboardEvent) => {
        if (e.key === 'Escape') close();
    };
    document.addEventListener('keydown', onKey);

    // Cell clicks
    overlay.querySelectorAll('.icon-select-cell').forEach(cell => {
        cell.addEventListener('click', () => {
            if (cell.classList.contains('disabled')) return;
            close();
            onPick((cell as HTMLElement).dataset.value!);
        });
    });

    // Animate in
    requestAnimationFrame(() => overlay.classList.add('open'));
}
