(function () {
    if (window.__MELON_LITE_SEARCH_INITED) return;
    window.__MELON_LITE_SEARCH_INITED = true;

    const RECENT_KEY = 'melon_lite_recent_searches_v20';
    const FILTER_PRESET_KEY = 'melon_lite_filter_presets_v25';
    const MAX_RECENT = 10;
    const MAX_FILTER_PRESETS = 18;
    const MAX_KEYWORDS = 16;
    const SUGGEST_CACHE_TTL_MS = 1000 * 60 * 3;
    const FILTER_PRESET_KEYS = [
        'q', 'artist', 'album', 'genre', 'tag_id', 'min_rating', 'liked',
        'has_note', 'has_lyrics', 'recent_played', 'popular_only',
        'hide_missing_meta', 'manual_only', 'playlist_id', 'sort', 'tab'
    ];
    const FILTER_SORT_KEYS = [
        'relevance', 'title', 'artist', 'album', 'newest', 'recent_played',
        'popular', 'rating', 'liked', 'duration', 'random'
    ];
    const suggestCache = new Map();
    const SUGGEST_CACHE_MAX = 30;
    let keywordCache = null;
    let keywordRequest = null;

    function safeJsonParse(value, fallback) {
        try {
            const parsed = JSON.parse(value);
            return parsed || fallback;
        } catch (err) {
            return fallback;
        }
    }

    function getRecentSearches() {
        const items = safeJsonParse(localStorage.getItem(RECENT_KEY) || '[]', []);
        return Array.isArray(items) ? items : [];
    }

    function saveRecentSearch(query) {
        query = String(query || '').trim().slice(0, 120);
        if (!query || hasUnsafePathValue(query)) return;
        const now = Date.now();
        const map = new Map();
        getRecentSearches().forEach(function (item) {
            if (!item || !item.q) return;
            map.set(item.q, {
                q: item.q,
                count: Math.max(1, parseInt(item.count, 10) || 1),
                at: parseInt(item.at, 10) || 0
            });
        });
        const existing = map.get(query) || { q: query, count: 0, at: 0 };
        existing.count += 1;
        existing.at = now;
        map.set(query, existing);
        const next = Array.from(map.values())
            .sort(function (a, b) { return b.at - a.at; })
            .slice(0, MAX_RECENT);
        try {
            localStorage.setItem(RECENT_KEY, JSON.stringify(next));
        } catch (err) {}
    }

    function removeRecentSearch(query) {
        query = String(query || '').trim();
        if (!query) return;
        const next = getRecentSearches().filter(function (item) {
            return item && item.q !== query;
        });
        try {
            if (next.length) localStorage.setItem(RECENT_KEY, JSON.stringify(next));
            else localStorage.removeItem(RECENT_KEY);
        } catch (err) {}
    }

    function clearRecentSearches() {
        try { localStorage.removeItem(RECENT_KEY); } catch (err) {}
    }

    function hasUnsafePathValue(value) {
        return /[A-Za-z]:[\\/]/.test(String(value || ''));
    }

    function sanitizePresetValue(key, value) {
        value = String(value == null ? '' : value).trim();
        if (hasUnsafePathValue(value)) return '';
        if (key === 'q' || key === 'artist' || key === 'album' || key === 'genre') return value.slice(0, 120);
        if (key === 'tag_id' || key === 'playlist_id') return String(Math.max(0, parseInt(value, 10) || 0));
        if (key === 'min_rating') return String(Math.max(0, Math.min(5, parseInt(value, 10) || 0)));
        if (key === 'liked' || key === 'has_note' || key === 'has_lyrics') return value === '1' || value === '0' ? value : '';
        if (key === 'recent_played' || key === 'popular_only' || key === 'hide_missing_meta' || key === 'manual_only') return value === '1' ? '1' : '';
        if (key === 'sort') return FILTER_SORT_KEYS.indexOf(value) >= 0 ? value : 'relevance';
        if (key === 'tab') return ['songs', 'artists', 'albums', 'genres', 'playlists', 'lyrics'].indexOf(value) >= 0 ? value : '';
        return '';
    }

    function normalizeFilterPreset(item) {
        if (!item || typeof item !== 'object') return null;
        const source = item.filters && typeof item.filters === 'object' ? item.filters : {};
        const filters = {};
        FILTER_PRESET_KEYS.forEach(function (key) {
            const value = sanitizePresetValue(key, source[key]);
            if (value !== '' && !(key === 'tag_id' && value === '0') && !(key === 'playlist_id' && value === '0') && !(key === 'min_rating' && value === '0')) {
                filters[key] = value;
            }
        });
        const name = String(item.name || '').trim().replace(/\s+/g, ' ').slice(0, 40);
        if (!name || hasUnsafePathValue(name)) return null;
        return {
            id: String(item.id || ('preset_' + Date.now())).replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 48) || ('preset_' + Date.now()),
            name: name,
            filters: filters,
            createdAt: parseInt(item.createdAt, 10) || Date.now(),
            updatedAt: parseInt(item.updatedAt, 10) || Date.now()
        };
    }

    function getFilterPresets() {
        const items = safeJsonParse(localStorage.getItem(FILTER_PRESET_KEY) || '[]', []);
        if (!Array.isArray(items)) return [];
        return items.map(normalizeFilterPreset).filter(Boolean).slice(0, MAX_FILTER_PRESETS);
    }

    function persistFilterPresets(items) {
        try {
            localStorage.setItem(FILTER_PRESET_KEY, JSON.stringify(items.slice(0, MAX_FILTER_PRESETS)));
        } catch (err) {}
    }

    function collectCurrentSearchFilters() {
        const form = document.querySelector('.advanced-filter-form');
        const pageForm = document.querySelector('[data-search-form]');
        const data = {};
        if (pageForm) {
            const q = pageForm.querySelector('input[name="q"]');
            if (q) data.q = sanitizePresetValue('q', q.value);
        }
        if (form) {
            FILTER_PRESET_KEYS.forEach(function (key) {
                const field = form.querySelector('[name="' + key + '"]');
                if (!field) return;
                if (field.type === 'checkbox') {
                    if (field.checked) data[key] = '1';
                    return;
                }
                const value = sanitizePresetValue(key, field.value);
                if (value !== '') data[key] = value;
            });
        }
        Object.keys(data).forEach(function (key) {
            if (data[key] === '' || data[key] === '0') delete data[key];
        });
        return data;
    }

    function filterPresetUrl(filters) {
        const params = new URLSearchParams();
        FILTER_PRESET_KEYS.forEach(function (key) {
            if (!filters || filters[key] == null || filters[key] === '') return;
            const value = sanitizePresetValue(key, filters[key]);
            if (value === '' || value === '0') return;
            params.set(key, value);
        });
        return 'search.php' + (params.toString() ? '?' + params.toString() : '');
    }

    function describeFilterPreset(filters) {
        const labels = [];
        if (filters.q) labels.push('검색어: ' + filters.q);
        if (filters.artist) labels.push('아티스트');
        if (filters.album) labels.push('앨범');
        if (filters.genre) labels.push('장르');
        if (filters.tag_id) labels.push('태그');
        if (filters.min_rating) labels.push(filters.min_rating + '점 이상');
        if (filters.liked === '1') labels.push('좋아요');
        if (filters.has_lyrics === '1') labels.push('가사 있음');
        if (filters.has_note === '1') labels.push('메모 있음');
        if (filters.popular_only === '1') labels.push('재생 기록');
        if (filters.manual_only === '1') labels.push('수동 수정');
        if (filters.sort) labels.push('정렬: ' + filters.sort);
        return labels.length ? labels.slice(0, 5).join(' · ') : '필터 조건만 저장됨';
    }

    function saveFilterPresetFromPage(panel) {
        const nameInput = panel.querySelector('[data-filter-preset-name]');
        const name = String(nameInput ? nameInput.value : '').trim().replace(/\s+/g, ' ').slice(0, 40);
        const filters = collectCurrentSearchFilters();
        if (!name) {
            if (window.showToast) window.showToast('프리셋 이름을 입력하세요.', 'warning');
            return;
        }
        if (hasUnsafePathValue(name) || Object.values(filters).some(hasUnsafePathValue)) {
            if (window.showToast) window.showToast('로컬 파일 경로는 프리셋에 저장할 수 없습니다.', 'error');
            return;
        }
        const now = Date.now();
        const items = getFilterPresets();
        const existingIndex = items.findIndex(function (item) { return item.name === name; });
        const next = {
            id: existingIndex >= 0 ? items[existingIndex].id : 'preset_' + now,
            name: name,
            filters: filters,
            createdAt: existingIndex >= 0 ? items[existingIndex].createdAt : now,
            updatedAt: now
        };
        if (existingIndex >= 0) items.splice(existingIndex, 1);
        items.unshift(next);
        persistFilterPresets(items);
        if (nameInput) nameInput.value = '';
        renderAllFilterPresetPanels();
        if (window.showToast) window.showToast(existingIndex >= 0 ? '필터 프리셋을 업데이트했습니다.' : '필터 프리셋을 저장했습니다.', 'success');
    }

    function renameFilterPreset(id) {
        const items = getFilterPresets();
        const item = items.find(function (preset) { return preset.id === id; });
        if (!item) return;
        const nextName = window.prompt('프리셋 이름을 입력하세요.', item.name);
        if (nextName === null) return;
        const name = String(nextName || '').trim().replace(/\s+/g, ' ').slice(0, 40);
        if (!name || hasUnsafePathValue(name)) {
            if (window.showToast) window.showToast('사용할 수 없는 이름입니다.', 'warning');
            return;
        }
        item.name = name;
        item.updatedAt = Date.now();
        persistFilterPresets(items);
        renderAllFilterPresetPanels();
    }

    function deleteFilterPreset(id) {
        persistFilterPresets(getFilterPresets().filter(function (preset) { return preset.id !== id; }));
        renderAllFilterPresetPanels();
        if (window.showToast) window.showToast('필터 프리셋을 삭제했습니다.', 'info');
    }

    function escapeHtml(text) {
        return String(text || '').replace(/[&<>"']/g, function (ch) {
            return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[ch];
        });
    }

    function safeKeywordText(value) {
        const text = String(value || '').trim().replace(/\s+/g, ' ').slice(0, 120);
        return hasUnsafePathValue(text) ? '' : text;
    }

    function recentSearchUrl(query) {
        const q = safeKeywordText(query);
        return 'search.php' + (q ? '?q=' + encodeURIComponent(q) : '');
    }

    function fetchRecommendedKeywords() {
        if (keywordCache) return Promise.resolve(keywordCache);
        if (keywordRequest) return keywordRequest;
        keywordRequest = fetch((window.APP_BASE_PATH || './') + 'api/search_keywords.php', {
            credentials: 'same-origin',
            cache: 'no-store',
            headers: {
                'Accept': 'application/json',
                'Cache-Control': 'no-cache'
            }
        })
            .then(function (res) { return res.json(); })
            .then(function (data) {
                if (!data.success) throw new Error(data.message || 'keywords failed');
                const items = data.data && Array.isArray(data.data.items) ? data.data.items : (Array.isArray(data.items) ? data.items : []);
                keywordCache = items.map(function (item) {
                    return {
                        label: safeKeywordText(item.label),
                        type: safeKeywordText(item.type || 'keyword'),
                        reason: safeKeywordText(item.reason || '')
                    };
                }).filter(function (item) { return item.label; }).slice(0, MAX_KEYWORDS);
                return keywordCache;
            })
            .catch(function () {
                keywordCache = [];
                return keywordCache;
            })
            .finally(function () {
                keywordRequest = null;
            });
        return keywordRequest;
    }

    function renderRecentChips(items, compact) {
        if (!items || !items.length) {
            return '<p class="search-assist-empty">최근 검색어가 없습니다.</p>';
        }
        return items.map(function (item) {
            const q = safeKeywordText(item.q);
            if (!q) return '';
            return '<span class="recent-search-chip-wrap">' +
                '<a class="recent-search-chip" href="' + escapeHtml(recentSearchUrl(q)) + '" data-search-query="' + escapeHtml(q) + '">' +
                    '<i class="fa-regular fa-clock" aria-hidden="true"></i><span>' + escapeHtml(q) + '</span>' +
                    (!compact ? '<small>' + escapeHtml(item.count || 1) + '</small>' : '') +
                '</a>' +
                '<button type="button" class="recent-search-delete" data-delete-recent-search="' + escapeHtml(q) + '" aria-label="' + escapeHtml(q) + ' 삭제" title="삭제"><i class="fa-solid fa-xmark" aria-hidden="true"></i></button>' +
            '</span>';
        }).join('');
    }

    function renderKeywordChips(items) {
        if (!items || !items.length) {
            return '<p class="search-assist-empty">추천 키워드를 준비 중입니다.</p>';
        }
        return items.map(function (item) {
            const label = safeKeywordText(item.label);
            if (!label) return '';
            return '<a class="search-keyword-chip" href="' + escapeHtml(recentSearchUrl(label)) + '" data-search-query="' + escapeHtml(label) + '">' +
                '<i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i>' +
                '<span>' + escapeHtml(label) + '</span>' +
                (item.reason ? '<small>' + escapeHtml(item.reason) + '</small>' : '') +
            '</a>';
        }).join('');
    }

    function renderSearchAssistBox(form, keywords) {
        const box = ensureSuggestBox(form);
        const recent = getRecentSearches().filter(function (item) { return item && safeKeywordText(item.q); }).slice(0, MAX_RECENT);
        box.innerHTML = '<div class="quick-search-assist" data-search-assist-box>' +
            '<section class="search-assist-section">' +
                '<div class="search-assist-head"><strong>최근 검색어</strong>' +
                    (recent.length ? '<button type="button" data-clear-recent-searches>전체 삭제</button>' : '') +
                '</div>' +
                '<div class="search-assist-chip-list">' + renderRecentChips(recent, true) + '</div>' +
            '</section>' +
            '<section class="search-assist-section">' +
                '<div class="search-assist-head"><strong>추천 검색어</strong><span>빠른 탐색</span></div>' +
                '<div class="search-keyword-list">' + renderKeywordChips(keywords || keywordCache || []) + '</div>' +
            '</section>' +
        '</div>';
        box.hidden = false;
        positionSuggestBox(form);
    }

    function showSearchAssistBox(form) {
        renderSearchAssistBox(form, keywordCache || []);
        fetchRecommendedKeywords().then(function (items) {
            const input = form.querySelector('input[type="search"], input[name="q"], input[name="search"]');
            if (!input || input.value.trim() !== '') return;
            renderSearchAssistBox(form, items);
        });
    }

    function makeAbsoluteUrl(url) {
        return new URL(url, window.location.origin + (window.APP_BASE_PATH || './')).href;
    }

    function getSuggestMinimumLength(query) {
        return /[ㄱ-ㅎㅏ-ㅣ가-힣]/.test(query || '') ? 1 : 2;
    }

    function hasSuggestableQuery(query) {
        const q = (query || '').trim();
        return q.length >= getSuggestMinimumLength(q);
    }

    function ensureSuggestBox(form) {
        let box = form.querySelector('.quick-suggest-box');
        if (!box) {
            box = document.createElement('div');
            box.className = 'quick-suggest-box';
            box.setAttribute('role', 'listbox');
            box.hidden = true;
            form.appendChild(box);
        }
        return box;
    }

    function positionSuggestBox(form) {
        const box = ensureSuggestBox(form);
        const input = form.querySelector('input[type="search"], input[name="q"], input[name="search"]');
        if (!input || box.hidden) return;
        const rect = input.getBoundingClientRect();
        if (!rect.width || !rect.height) return;

        const isCompact = window.matchMedia && window.matchMedia('(max-width: 1199px)').matches;
        const gutter = isCompact ? 12 : 8;
        const bottomReserve = isCompact ? 126 : 40;
        const viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
        const viewportHeight = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
        const width = Math.min(Math.max(rect.width, isCompact ? 280 : 320), viewportWidth - (gutter * 2));
        const left = Math.min(Math.max(rect.left, gutter), Math.max(gutter, viewportWidth - width - gutter));
        const top = Math.max(gutter, rect.bottom + 8);
        const maxHeight = Math.max(180, viewportHeight - top - bottomReserve);

        box.classList.add('quick-suggest-box-floating');
        box.style.position = 'fixed';
        box.style.left = left + 'px';
        box.style.right = 'auto';
        box.style.top = top + 'px';
        box.style.bottom = 'auto';
        box.style.width = width + 'px';
        box.style.maxHeight = Math.min(maxHeight, isCompact ? 420 : 480) + 'px';
    }

    function repositionVisibleSuggestBoxes() {
        document.querySelectorAll('.quick-suggest-box:not([hidden])').forEach(function (box) {
            const form = box.closest('.sidebar-search-form, .quick-search-form, .enhanced-search-form, [data-search-form]');
            if (form) positionSuggestBox(form);
        });
    }

    function typeLabel(type) {
        const labels = { song: '곡', artist: '아티스트', album: '앨범', tag: '태그', playlist: '플레이리스트' };
        return labels[type] || type || '';
    }

    function renderSuggestions(form, items) {
        const box = ensureSuggestBox(form);
        if (!items || items.length === 0) {
            showSearchAssistBox(form);
            return;
        }
        box.innerHTML = items.map(function (item) {

            const playData = item.type === 'song' ? [
                'data-play-id="' + escapeHtml(item.play_id) + '"',
                'data-title="' + escapeHtml(item.title) + '"',
                'data-artist="' + escapeHtml(item.artist) + '"',
                'data-album="' + escapeHtml(item.album) + '"',
                'data-duration="' + escapeHtml(item.duration || 0) + '"'
            ].join(' ') : '';
            return '<div class="quick-suggest-item" role="option" tabindex="0" data-url="' + escapeHtml(item.url) + '" ' + playData + '>' +
                '<span class="suggest-type">' + escapeHtml(typeLabel(item.type)) + '</span>' +
                '<strong>' + escapeHtml(item.label) + '</strong>' +
                '<small>' + escapeHtml(item.sub || '') + '</small>' +
                (item.type === 'song' ? '<button type="button" class="suggest-play-btn" title="바로 재생" aria-label="바로 재생"><i class="fas fa-play"></i></button>' : '') +
                '</div>';
        }).join('');
        box.hidden = false;
        positionSuggestBox(form);
    }

    function renderSuggestMessage(form, message, type) {
        const box = ensureSuggestBox(form);
        box.innerHTML = '<div class="quick-suggest-status ' + escapeHtml(type || 'info') + '" role="status">' + escapeHtml(message) + '</div>';
        box.hidden = false;
        positionSuggestBox(form);
    }

    function normalizeSuggestItems(data) {
        if (!data) return [];
        if (Array.isArray(data.items)) return data.items;
        if (data.data && Array.isArray(data.data.items)) return data.data.items;
        if (data.results && Array.isArray(data.results)) return data.results;
        return [];
    }

    function getCachedSuggestions(query) {
        const cached = suggestCache.get(query);
        if (!cached) return null;
        if (!cached.at || Date.now() - cached.at > SUGGEST_CACHE_TTL_MS) {
            suggestCache.delete(query);
            return null;
        }
        return Array.isArray(cached.items) ? cached.items : [];
    }

    function putCachedSuggestions(query, items) {
        suggestCache.set(query, {
            at: Date.now(),
            items: Array.isArray(items) ? items : []
        });
        while (suggestCache.size > SUGGEST_CACHE_MAX) {
            suggestCache.delete(suggestCache.keys().next().value);
        }
    }

    function bindQuickSearchForm(form) {
        if (!form || form.dataset.searchBound === '1') return;
        form.dataset.searchBound = '1';
        const input = form.querySelector('input[type="search"], input[name="q"], input[name="search"]');
        if (!input) return;
        input.setAttribute('autocomplete', input.getAttribute('autocomplete') || 'off');
        input.setAttribute('aria-autocomplete', 'list');
        let timer = null;
        let controller = null;
        let activeSuggestQuery = '';

        function requestSuggestions() {
            const q = input.value.trim();
            clearTimeout(timer);
            if (controller && activeSuggestQuery !== q) {
                controller.abort();
                controller = null;
                activeSuggestQuery = '';
            }
            if (!hasSuggestableQuery(q)) {
                if (q === '') showSearchAssistBox(form);
                else renderSuggestions(form, []);
                return;
            }
            timer = setTimeout(function () {
                const cached = getCachedSuggestions(q);
                if (cached) {
                    renderSuggestions(form, cached);
                    return;
                }
                if (controller && activeSuggestQuery === q) return;
                const requestQuery = q;
                renderSuggestMessage(form, '검색 제안을 불러오는 중...', 'loading');
                const localController = new AbortController();
                controller = localController;
                activeSuggestQuery = requestQuery;
                fetch((window.APP_BASE_PATH || './') + 'api/search_suggest.php?q=' + encodeURIComponent(requestQuery), {
                    credentials: 'same-origin',
                    cache: 'no-store',
                    signal: localController.signal,
                    headers: {
                        'Accept': 'application/json',
                        'Cache-Control': 'no-cache'
                    }
                })
                    .then(function (res) { return res.json(); })
                    .then(function (data) {
                        if (!data.success) throw new Error(data.message || 'suggest failed');
                        if (input.value.trim() !== requestQuery) return;
                        const items = normalizeSuggestItems(data);
                        putCachedSuggestions(requestQuery, items);
                        renderSuggestions(form, items);
                    })
                    .catch(function (err) {
                        if (err.name !== 'AbortError') renderSuggestMessage(form, '검색 제안을 불러오지 못했습니다.', 'error');
                    })
                    .finally(function () {
                        if (controller === localController) {
                            controller = null;
                            activeSuggestQuery = '';
                        }
                    });
            }, 220);
        }

        input.addEventListener('input', requestSuggestions);
        input.addEventListener('search', requestSuggestions);
        input.addEventListener('compositionend', requestSuggestions);
        input.addEventListener('focus', function () {
            if (hasSuggestableQuery(input.value)) requestSuggestions();
            else showSearchAssistBox(form);
        });

        form.addEventListener('submit', function () {
            saveRecentSearch(input.value);
        });

        form.addEventListener('click', function (event) {
            const deleteRecent = event.target.closest('[data-delete-recent-search]');
            if (deleteRecent) {
                event.preventDefault();
                event.stopPropagation();
                removeRecentSearch(deleteRecent.getAttribute('data-delete-recent-search'));
                renderRecentPanel();
                showSearchAssistBox(form);
                return;
            }
            const clearRecent = event.target.closest('[data-clear-recent-searches]');
            if (clearRecent) {
                event.preventDefault();
                event.stopPropagation();
                clearRecentSearches();
                renderRecentPanel();
                showSearchAssistBox(form);
                return;
            }
            const queryLink = event.target.closest('[data-search-query]');
            if (queryLink && !event.target.closest('.suggest-play-btn')) {
                const q = queryLink.getAttribute('data-search-query') || '';
                if (q) saveRecentSearch(q);
                return;
            }
            const play = event.target.closest('.suggest-play-btn');
            const item = event.target.closest('.quick-suggest-item');
            if (!item) return;
            event.preventDefault();
            event.stopPropagation();
            if (play && item.dataset.playId && window.AppPlayer && window.AppPlayer.playTrackObject) {
                saveRecentSearch(input.value);
                window.AppPlayer.playTrackObject({
                    id: item.dataset.playId,
                    title: item.dataset.title || '',
                    artist: item.dataset.artist || '',
                    album: item.dataset.album || '',
                    duration: parseInt(item.dataset.duration, 10) || 0
                });
                ensureSuggestBox(form).hidden = true;
                return;
            }
            const url = item.dataset.url;
            if (url) {
                saveRecentSearch(input.value);
                if (window.MelonLiteNavigate) window.MelonLiteNavigate(new URL(makeAbsoluteUrl(url)), { push: true });
                else window.location.href = makeAbsoluteUrl(url);
            }
        });

        form.addEventListener('keydown', function (event) {
            if (event.key !== 'Enter') return;
            const item = event.target.closest('.quick-suggest-item');
            if (!item) return;
            item.click();
        });

    }

    function bindGlobalSuggestDismiss() {
        if (document.documentElement.dataset.searchDismissBound === '1') return;
        document.documentElement.dataset.searchDismissBound = '1';
        document.addEventListener('click', function (event) {
            const activeForm = event.target.closest('[data-search-form], .sidebar-search-form, .quick-search-form, .enhanced-search-form');
            document.querySelectorAll('.quick-suggest-box').forEach(function (box) {
                if (activeForm && activeForm.contains(box)) return;
                box.hidden = true;
            });
        });
        window.addEventListener('resize', repositionVisibleSuggestBoxes, { passive: true });
        window.addEventListener('orientationchange', repositionVisibleSuggestBoxes, { passive: true });
        window.addEventListener('scroll', repositionVisibleSuggestBoxes, { passive: true, capture: true });
    }

    function ensureSearchPageKeywordPanel(panel) {
        if (!panel || panel.querySelector('[data-search-keyword-list]')) return;
        const block = document.createElement('div');
        block.className = 'search-assist-page-grid search-assist-page-grid-js';
        block.innerHTML =
            '<div class="search-assist-block">' +
                '<div class="search-assist-subhead">' +
                    '<strong>추천 검색어</strong>' +
                    '<span>청취 데이터 기반</span>' +
                '</div>' +
                '<div class="search-keyword-list" data-search-keyword-list></div>' +
            '</div>';
        panel.appendChild(block);
    }

    function renderRecentPanel() {
        const panel = document.querySelector('[data-recent-search-panel]');
        const list = document.querySelector('[data-recent-search-list]');
        if (!panel || !list) return;
        ensureSearchPageKeywordPanel(panel);
        const items = getRecentSearches();
        panel.hidden = false;
        list.innerHTML = renderRecentChips(items, false);
        const clear = document.querySelector('[data-clear-recent-searches]');
        if (clear) clear.hidden = items.length === 0;
        const keywordList = document.querySelector('[data-search-keyword-list]');
        if (keywordList) {
            keywordList.innerHTML = renderKeywordChips(keywordCache || []);
            fetchRecommendedKeywords().then(function (keywords) {
                keywordList.innerHTML = renderKeywordChips(keywords);
            });
        }
    }

    function renderFilterPresetPanel(panel) {
        const list = panel.querySelector('[data-filter-preset-list]');
        const empty = panel.querySelector('[data-filter-preset-empty]');
        if (!list) return;
        const items = getFilterPresets();
        if (empty) empty.hidden = items.length > 0;
        if (items.length === 0) {
            list.innerHTML = '';
            return;
        }
        list.innerHTML = items.map(function (item) {
            return '<article class="filter-preset-card" data-filter-preset-id="' + escapeHtml(item.id) + '">' +
                '<div class="filter-preset-card-main">' +
                    '<strong>' + escapeHtml(item.name) + '</strong>' +
                    '<span>' + escapeHtml(describeFilterPreset(item.filters)) + '</span>' +
                '</div>' +
                '<div class="filter-preset-card-actions">' +
                    '<a class="btn btn-primary btn-sm" href="' + escapeHtml(filterPresetUrl(item.filters)) + '"><i class="fa-solid fa-play"></i> 실행</a>' +
                    '<button type="button" class="btn btn-secondary btn-sm" data-rename-filter-preset="' + escapeHtml(item.id) + '" title="이름 변경" aria-label="이름 변경"><i class="fa-solid fa-pen"></i></button>' +
                    '<button type="button" class="btn btn-secondary btn-sm" data-delete-filter-preset="' + escapeHtml(item.id) + '" title="삭제" aria-label="삭제"><i class="fa-solid fa-trash"></i></button>' +
                '</div>' +
            '</article>';
        }).join('');
    }

    function renderAllFilterPresetPanels() {
        document.querySelectorAll('[data-filter-preset-panel]').forEach(renderFilterPresetPanel);
    }

    function bindFilterPresetPanel(panel) {
        if (!panel || panel.dataset.filterPresetBound === '1') return;
        panel.dataset.filterPresetBound = '1';
        const save = panel.querySelector('[data-save-filter-preset]');
        if (save) {
            save.addEventListener('click', function () {
                saveFilterPresetFromPage(panel);
            });
        }
        panel.addEventListener('click', function (event) {
            const rename = event.target.closest('[data-rename-filter-preset]');
            if (rename) {
                event.preventDefault();
                renameFilterPreset(rename.getAttribute('data-rename-filter-preset'));
                return;
            }
            const del = event.target.closest('[data-delete-filter-preset]');
            if (del) {
                event.preventDefault();
                deleteFilterPreset(del.getAttribute('data-delete-filter-preset'));
            }
        });
    }

    function initSearchPage() {
        bindGlobalSuggestDismiss();
        document.querySelectorAll('.sidebar-search-form, .quick-search-form, .enhanced-search-form, [data-search-form]').forEach(bindQuickSearchForm);
        renderRecentPanel();
        document.querySelectorAll('[data-filter-preset-panel]').forEach(bindFilterPresetPanel);
        renderAllFilterPresetPanels();
        const pageForm = document.querySelector('[data-search-form]');
        if (pageForm && pageForm.dataset.submitRecentBound !== '1') {
            pageForm.dataset.submitRecentBound = '1';
            pageForm.addEventListener('submit', function () {
                const input = pageForm.querySelector('input[name="q"]');
                if (input) saveRecentSearch(input.value);
            });
        }
        document.querySelectorAll('[data-recent-search-panel]').forEach(function (panel) {
            if (panel.dataset.searchRecentPanelBound === '1') return;
            panel.dataset.searchRecentPanelBound = '1';
            panel.addEventListener('click', function (event) {
                const del = event.target.closest('[data-delete-recent-search]');
                if (del) {
                    event.preventDefault();
                    removeRecentSearch(del.getAttribute('data-delete-recent-search'));
                    renderRecentPanel();
                    return;
                }
                const clear = event.target.closest('[data-clear-recent-searches]');
                if (clear) {
                    event.preventDefault();
                    clearRecentSearches();
                    renderRecentPanel();
                    return;
                }
                const queryLink = event.target.closest('[data-search-query]');
                if (queryLink) saveRecentSearch(queryLink.getAttribute('data-search-query') || '');
            });
        });
    }

    window.MelonLiteInitSearchPage = initSearchPage;
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initSearchPage);
    } else {
        initSearchPage();
    }
    document.addEventListener('melonlite:page-ready', initSearchPage);
})();
