        class SearchApp {
            constructor() {
                this.currentPage = 1;
                this.totalResults = 0;
                this.searchQuery = '';
                this.clientId = 'WIKIPEDIA_001';
                this.searchApiUrl = '/sandi/search';
                this.typeaheadApiUrl = '/sandi/typeahead';
                this.highRelevancyFieldsList = [];
                this.lowRelevancyFieldsList = [];
                this.contentFieldsList = [];
                this.lastRequest = null;
                this.lastResponse = null;

                this.taTimer = null;
                this.taIndex = -1;
                this.taSuggestions = [];

                this.initializeEventListeners();
                this.initTypeAhead();
            }

            initializeEventListeners() {
                document.getElementById('searchBtn').addEventListener('click', () => this.performSearch());
                document.getElementById('searchQuery').addEventListener('keypress', (e) => {
                    if (e.key === 'Enter') this.performSearch();
                });

//                document.getElementById('pageSize').addEventListener('change', (e) => {
//                    if (this.searchQuery) {
//                        this.currentPage = 1;
//                        this.performSearch();
//                    }
//                });
            }

            initTypeAhead() {
                const input = document.getElementById('searchQuery');
                const dropdown = document.getElementById('typeaheadDropdown');

                input.addEventListener('input', () => {
                    clearTimeout(this.taTimer);
                    const q = input.value.trim();
                    if (q.length < 2) {
                        this.hideTypeAhead();
                        return;
                    }
                    this.taTimer = setTimeout(() => this.fetchTypeAhead(q), 300);
                });

                input.addEventListener('keydown', (e) => {
                    if (!dropdown.classList.contains('visible')) return;
                    if (e.key === 'ArrowDown') {
                        e.preventDefault();
                        this.taIndex = Math.min(this.taIndex + 1, this.taSuggestions.length - 1);
                        this.highlightTypeAheadItem();
                    } else if (e.key === 'ArrowUp') {
                        e.preventDefault();
                        this.taIndex = Math.max(this.taIndex - 1, -1);
                        this.highlightTypeAheadItem();
                    } else if (e.key === 'Enter') {
                        if (this.taIndex >= 0) {
                            e.preventDefault();
                            this.selectTypeAheadItem(this.taSuggestions[this.taIndex]);
                        } else {
                            this.hideTypeAhead();
                        }
                    } else if (e.key === 'Escape') {
                        this.hideTypeAhead();
                    }
                });

                document.addEventListener('click', (e) => {
                    if (!input.contains(e.target) && !dropdown.contains(e.target)) {
                        this.hideTypeAhead();
                    }
                });
            }

            async fetchTypeAhead(query) {
                const clientId = document.getElementById('clientId').value.trim();
                if (!clientId) return;
                try {
                    const url = `${this.typeaheadApiUrl}?clientId=${encodeURIComponent(clientId)}&query=${encodeURIComponent(query)}`;
                    const response = await fetch(url);
                    const data = await response.json();
                    const suggestions = (data.results || []).filter(s => s && s.trim());
                    this.showTypeAhead(suggestions);
                } catch (e) {
                    this.hideTypeAhead();
                }
            }

            showTypeAhead(suggestions) {
                const dropdown = document.getElementById('typeaheadDropdown');
                this.taSuggestions = suggestions;
                this.taIndex = -1;

                if (suggestions.length === 0) {
                    this.hideTypeAhead();
                    return;
                }

                dropdown.innerHTML = suggestions.map((s, i) =>
                    `<div class="typeahead-item" data-index="${i}">${this.escapeHtml(s)}</div>`
                ).join('');

                dropdown.querySelectorAll('.typeahead-item').forEach(item => {
                    item.addEventListener('mousedown', (e) => e.preventDefault());
                    item.addEventListener('click', () => this.selectTypeAheadItem(suggestions[+item.dataset.index]));
                    item.addEventListener('dblclick', () => {
                        this.selectTypeAheadItem(suggestions[+item.dataset.index]);
                        this.performSearch();
                    });
                    item.addEventListener('mouseenter', () => {
                        this.taIndex = +item.dataset.index;
                        this.highlightTypeAheadItem();
                    });
                });

                dropdown.classList.add('visible');
            }

            hideTypeAhead() {
                document.getElementById('typeaheadDropdown').classList.remove('visible');
                this.taIndex = -1;
            }

            highlightTypeAheadItem() {
                document.querySelectorAll('#typeaheadDropdown .typeahead-item').forEach((el, i) => {
                    el.classList.toggle('active', i === this.taIndex);
                });
                if (this.taIndex >= 0) {
                    const active = document.querySelector('#typeaheadDropdown .typeahead-item.active');
                    if (active) active.scrollIntoView({ block: 'nearest' });
                }
            }

            selectTypeAheadItem(text) {
                document.getElementById('searchQuery').value = text;
                this.hideTypeAhead();
            }

            generateRequestId() {
                return 'search-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
            }

            async performSearch(page = 1) {
                this.currentPage = page;
                this.searchQuery = document.getElementById('searchQuery').value.trim();
                this.clientId = document.getElementById('clientId').value.trim();

                if (!this.searchQuery) {
                    this.showError('Please enter a search query');
                    return;
                }

                if (!this.clientId) {
                    this.showError('Please enter a client ID');
                    return;
                }

                this.showLoading(true);
                this.clearError();
                this.clearResults();

                // Get the three field inputs
                const highRelevancyFields = document.getElementById('highRelevancyFields').value.trim();
                const lowRelevancyFields = document.getElementById('lowRelevancyFields').value.trim();
                const contentFields = document.getElementById('contentFields').value.trim();

                // Store field lists for later use in rendering
                this.highRelevancyFieldsList = highRelevancyFields ? highRelevancyFields.split(',').map(f => f.trim()) : [];
                this.lowRelevancyFieldsList = lowRelevancyFields ? lowRelevancyFields.split(',').map(f => f.trim()) : [];
                this.contentFieldsList = contentFields ? contentFields.split(',').map(f => f.trim()) : [];

                // Combine all fields into a single comma-separated string
                const allFields = [highRelevancyFields, lowRelevancyFields, contentFields]
                    .filter(f => f)
                    .join(',');

                // Always add id and score to the result fields
                let resultFields = allFields || '*';
                if (resultFields === '*') {
                    resultFields = '*,score';
                } else {
                    // Add id, url, score if not already included
                    const fieldsArray = resultFields.split(',').map(f => f.trim());
                    if (!fieldsArray.includes('id')) {
                        resultFields += ',id';
                    }
                    if (!fieldsArray.includes('url')) {
                        resultFields += ',url';
                    }
                    if (!fieldsArray.includes('score')) {
                        resultFields += ',score';
                    }
                }

                const searchRequest = {
                    requestId: this.generateRequestId(),
                    clientId: this.clientId,
                    searchClients: document.getElementById('searchClients').value.trim(),
                    pageSize: parseInt(document.getElementById('pageSize').value),
                    pageNumber: this.currentPage,
                    searchQuery: this.searchQuery,
                    filterQuery: document.getElementById('filterQuery').value.trim(),
                    resultFields: resultFields,
                    groupFields: document.getElementById('groupFields').value.trim(),
                    facetFields: document.getElementById('facetFields').value.trim(),
                    sortFields: document.getElementById('sortFields').value.trim(),
                    precision: document.getElementById('precision').value,
                    legacy: document.getElementById('legacy').checked,
                    synonyms: document.getElementById('synonyms').checked,
                    dym: document.getElementById('dym').checked,
                    collapse: document.getElementById('collapse').checked,
                    highlightFields: document.getElementById('highlightFields').value.trim(),
                    highlightTags: document.getElementById('highlightTags').value.trim(),
					expand: document.getElementById('expand').checked,
					rerank: document.getElementById('rerank').checked,
					rag: document.getElementById('rag').checked
                };

                // Store the request
                this.lastRequest = searchRequest;
                this.displayRequest();

                try {
                    const response = await fetch(this.searchApiUrl, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(searchRequest)
                    });

                    const data = await response.json();

                    // Store the response
                    this.lastResponse = data;
                    this.displayResponse();

                    if (data.status === 'SUCCESS') {
						this.displayRAGAnswer(data.ragAnswer);
                        this.displayResults(data);
                        this.displayPagination(data);
                        this.displayDYMSuggestion(data.dymQuery);
                    } else {
                        this.showError(data.message || 'Search failed');
                    }
                } catch (error) {
                    console.error('Search error:', error);
                    this.showError('Failed to connect to search service. Please check if the search API is running on port 8081.');
                    this.lastResponse = { error: error.message };
                    this.displayResponse();
                } finally {
                    this.showLoading(false);
                }
            }

            displayResults(data) {
                const container = document.getElementById('resultsContainer');
                this.totalResults = data.foundResults || 0;

                if (!data.results || data.results.length === 0) {
                    container.innerHTML = `
                        <div class="no-results">
                            <h3>No results found</h3>
                            <p>Try different keywords or check your spelling</p>
                        </div>
                    `;
                    return;
                }

                const synopsisHtml = `
                    <div class="results-synopsis">
                        <div class="results-info">
                            Found ${this.totalResults.toLocaleString()} results in ${data.took}ms
                        </div>
                        <div class="results-info">
                            Page ${this.currentPage} of ${Math.ceil(this.totalResults / parseInt(document.getElementById('pageSize').value))}
                        </div>
                    </div>
                `;

                const resultsHtml = data.results.map(result => this.renderSearchResult(result)).join('');
                container.innerHTML = synopsisHtml + resultsHtml;
            }

            renderSearchResult(result) {
                // Combine fields based on the configured field lists
                const title = this.combineFields(result, this.highRelevancyFieldsList) || 'Untitled';
                const content = this.combineFields(result, this.contentFieldsList);

                const id = result.id || '#';
                const score = result.score || 0;
                const url = result.url || result.uri || '';

                // Get low relevancy fields as tags
                const lowRelevancyTags = this.getLowRelevancyTags(result, this.lowRelevancyFieldsList);

                const truncatedContent = content && content.length > 300
                    ? content.substring(0, 300) + '...'
                    : content;

                const titleHtml = url
                    ? `<a href="${this.escapeHtml(url)}" target="_blank" rel="noopener noreferrer" class="result-title">${this.escapeHtml(title)}</a>`
                    : `<div class="result-title">${this.escapeHtml(title)}</div>`;

                return `
                    <div class="search-result">
                        ${titleHtml}
                        ${truncatedContent ? `<div class="result-content">${this.escapeHtml(truncatedContent)}</div>` : ''}
                        ${lowRelevancyTags ? `<div class="result-meta"><div class="result-categories">${lowRelevancyTags}</div></div>` : ''}
                        <div class="result-footer">
                            <div class="result-id-score">
                                <span>ID: ${this.escapeHtml(id)}</span>
                                <span style="margin-left: 1rem;">Score: ${score.toFixed(3)}</span>
                            </div>
                        </div>
                    </div>
                `;
            }

            getLowRelevancyTags(result, fieldsList) {
                if (!fieldsList || fieldsList.length === 0) {
                    return '';
                }

                const tags = [];
                for (const field of fieldsList) {
                    if (result[field]) {
                        const value = result[field];
                        if (Array.isArray(value)) {
                            value.forEach(v => {
                                if (v) {
                                    tags.push(`<span class="category-tag">${this.escapeHtml(v)}</span>`);
                                }
                            });
                        } else if (value) {
                            tags.push(`<span class="category-tag">${this.escapeHtml(value)}</span>`);
                        }
                    }
                }

                return tags.join('');
            }

            combineFields(result, fieldsList) {
                if (!fieldsList || fieldsList.length === 0) {
                    return '';
                }

                const values = [];
                for (const field of fieldsList) {
                    if (result[field]) {
                        const value = Array.isArray(result[field])
                            ? result[field].join(' ')
                            : result[field];
                        if (value) {
                            values.push(value);
                        }
                    }
                }

                return values.join(' ');
            }

            displayPagination(data) {
                const container = document.getElementById('paginationContainer');
                const pageSize = parseInt(document.getElementById('pageSize').value);
                const totalPages = Math.ceil(this.totalResults / pageSize);

                if (totalPages <= 1) {
                    container.innerHTML = '';
                    return;
                }

                let paginationHtml = '<div class="pagination">';

                // Previous button
                paginationHtml += `
                    <button ${this.currentPage === 1 ? 'disabled' : ''}
                            onclick="searchApp.performSearch(${this.currentPage - 1})">
                        Previous
                    </button>
                `;

                // Page numbers
                const startPage = Math.max(1, this.currentPage - 2);
                const endPage = Math.min(totalPages, this.currentPage + 2);

                if (startPage > 1) {
                    paginationHtml += `<button onclick="searchApp.performSearch(1)">1</button>`;
                    if (startPage > 2) {
                        paginationHtml += '<span>...</span>';
                    }
                }

                for (let i = startPage; i <= endPage; i++) {
                    paginationHtml += `
                        <button class="${i === this.currentPage ? 'active' : ''}"
                                onclick="searchApp.performSearch(${i})">
                            ${i}
                        </button>
                    `;
                }

                if (endPage < totalPages) {
                    if (endPage < totalPages - 1) {
                        paginationHtml += '<span>...</span>';
                    }
                    paginationHtml += `<button onclick="searchApp.performSearch(${totalPages})">${totalPages}</button>`;
                }

                // Next button
                paginationHtml += `
                    <button ${this.currentPage === totalPages ? 'disabled' : ''}
                            onclick="searchApp.performSearch(${this.currentPage + 1})">
                        Next
                    </button>
                `;

                paginationHtml += `
                    <div class="page-info">
                        Showing ${((this.currentPage - 1) * pageSize) + 1}-${Math.min(this.currentPage * pageSize, this.totalResults)}
                        of ${this.totalResults.toLocaleString()}
                    </div>
                `;

                paginationHtml += '</div>';
                container.innerHTML = paginationHtml;
            }

			displayRAGAnswer(ragAnswer) {
			    const container = document.getElementById('ragContainer');
			    if (ragAnswer && ragAnswer.trim()) {
			        container.innerHTML = `
			            <div class="rag-answer">
			                <h3>AI Summary</h3>
			                <p>${this.escapeHtml(ragAnswer)}</p>
			            </div>
			        `;
			    } else {
			        container.innerHTML = '';
			    }
			}

            displayDYMSuggestion(dymQuery) {
                const container = document.getElementById('dymContainer');
                if (dymQuery && dymQuery.trim() && dymQuery !== this.searchQuery.toLowerCase()) {
                    container.innerHTML = `
                        <div class="dym-suggestion">
                            <strong>Did you mean:</strong>
                            <a href="#" class="dym-link" onclick="searchApp.searchWithSuggestion('${this.escapeHtml(dymQuery)}'); return false;">
                                ${this.escapeHtml(dymQuery)}
                            </a>
                        </div>
                    `;
                } else {
                    container.innerHTML = '';
                }
            }

            searchWithSuggestion(suggestion) {
                document.getElementById('searchQuery').value = suggestion;
                this.performSearch(1);
            }

            showLoading(show) {
                document.getElementById('loadingIndicator').style.display = show ? 'block' : 'none';
                document.getElementById('searchBtn').disabled = show;
            }

            showError(message) {
                document.getElementById('errorContainer').innerHTML = `
                    <div class="error">${this.escapeHtml(message)}</div>
                `;
            }

            clearError() {
                document.getElementById('errorContainer').innerHTML = '';
            }

            clearResults() {
                document.getElementById('resultsContainer').innerHTML = '';
                document.getElementById('paginationContainer').innerHTML = '';
                document.getElementById('dymContainer').innerHTML = '';
    			document.getElementById('ragContainer').innerHTML = '';
            }

            escapeHtml(text) {
                const div = document.createElement('div');
                div.textContent = text;
                return div.innerHTML;
            }

            displayRequest() {
                const container = document.getElementById('requestJson');
                if (this.lastRequest) {
                    container.value = JSON.stringify(this.lastRequest, null, 2);
                } else {
                    container.value = 'No request yet. Perform a search to see the request JSON.';
                }
            }

            displayResponse() {
                const container = document.getElementById('responseJson');
                if (this.lastResponse) {
                    container.textContent = JSON.stringify(this.lastResponse, null, 2);
                } else {
                    container.textContent = 'No response yet. Perform a search to see the response JSON.';
                }
            }

            async submitEditedRequest() {
                const requestJson = document.getElementById('requestJson').value.trim();

                if (!requestJson || requestJson === 'No request yet. Perform a search to see the request JSON.') {
                    this.showError('Please create or edit a valid request JSON');
                    return;
                }

                let searchRequest;
                try {
                    searchRequest = JSON.parse(requestJson);
                } catch (error) {
                    this.showError('Invalid JSON format: ' + error.message);
                    return;
                }

                this.showLoading(true);
                this.clearError();

                // Store the edited request
                this.lastRequest = searchRequest;

                // Update Query tab form fields with request values
                this.updateQueryFormFromRequest(searchRequest);

                try {
                    const response = await fetch(this.searchApiUrl, {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(searchRequest)
                    });

                    const data = await response.json();

                    // Store the response
                    this.lastResponse = data;
                    this.displayResponse();

                    if (data.status === 'SUCCESS') {
                        // Update search state from the request
                        this.searchQuery = searchRequest.searchQuery || '';
                        this.clientId = searchRequest.clientId || '';
                        this.currentPage = searchRequest.pageNumber || 1;
                        this.totalResults = data.foundResults || 0;

                        // Extract field lists from the request
                        const resultFields = searchRequest.resultFields || '';
                        const allFieldsArray = resultFields.split(',').map(f => f.trim()).filter(f => f && f !== 'id' && f !== 'score');

                        // Try to reconstruct field lists (best effort)
                        this.highRelevancyFieldsList = [];
                        this.lowRelevancyFieldsList = [];
                        this.contentFieldsList = [];

                        // Simple heuristic: assign fields based on common patterns
                        allFieldsArray.forEach(field => {
                            if (field.includes('title') || field.includes('name')) {
                                this.highRelevancyFieldsList.push(field);
                            } else if (field.includes('cat') || field.includes('tag') || field.includes('type')) {
                                this.lowRelevancyFieldsList.push(field);
                            } else if (field.includes('content') || field.includes('text') || field.includes('description')) {
                                this.contentFieldsList.push(field);
                            } else {
                                this.contentFieldsList.push(field);
                            }
                        });

                        // Display results on Query tab
                        this.displayRAGAnswer(data.ragAnswer);
                        this.displayResults(data);
                        this.displayPagination(data);
                        this.displayDYMSuggestion(data.dymQuery);

                        // Switch to Query tab to show results
                        const queryTab = document.getElementById('query');
                        const requestTab = document.getElementById('request');
                        const tabs = document.querySelectorAll('.nav-tab');

                        document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
                        queryTab.classList.add('active');

                        tabs.forEach(tab => tab.classList.remove('active'));
                        tabs[0].classList.add('active'); // Activate Query tab button
                    } else {
                        this.showError(data.message || 'Search failed');
                    }
                } catch (error) {
                    console.error('Search error:', error);
                    this.showError('Failed to connect to search service. Please check if the search API is running on port 8081.');
                    this.lastResponse = { error: error.message };
                    this.displayResponse();
                } finally {
                    this.showLoading(false);
                }
            }

            updateQueryFormFromRequest(request) {
                // Update text inputs
                if (request.searchQuery !== undefined) {
                    document.getElementById('searchQuery').value = request.searchQuery;
                }
                if (request.clientId !== undefined) {
                    document.getElementById('clientId').value = request.clientId;
                }
                if (request.searchClients !== undefined) {
                    document.getElementById('searchClients').value = request.searchClients;
                }
                if (request.filterQuery !== undefined) {
                    document.getElementById('filterQuery').value = request.filterQuery;
                }
                if (request.sortFields !== undefined) {
                    document.getElementById('sortFields').value = request.sortFields;
                }
                if (request.groupFields !== undefined) {
                    document.getElementById('groupFields').value = request.groupFields;
                }
                if (request.facetFields !== undefined) {
                    document.getElementById('facetFields').value = request.facetFields;
                }
                if (request.highlightFields !== undefined) {
                    document.getElementById('highlightFields').value = request.highlightFields;
                }
                if (request.highlightTags !== undefined) {
                    document.getElementById('highlightTags').value = request.highlightTags;
                }

                // Update page size
                if (request.pageSize !== undefined) {
                    document.getElementById('pageSize').value = request.pageSize;
                }

                // Update checkboxes
                if (request.legacy !== undefined) {
                    document.getElementById('legacy').checked = request.legacy;
                }
                if (request.synonyms !== undefined) {
                    document.getElementById('synonyms').checked = request.synonyms;
                }
                if (request.collapse !== undefined) {
                    document.getElementById('collapse').checked = request.collapse;
                }
                if (request.dym !== undefined) {
                    document.getElementById('dym').checked = request.dym;
                }
                if (request.expand !== undefined) {
                    document.getElementById('expand').checked = request.expand;
                }
                if (request.rerank !== undefined) {
                    document.getElementById('rerank').checked = request.rerank;
                }
                if (request.rag !== undefined) {
                    document.getElementById('rag').checked = request.rag;
                }

                // Update field inputs by parsing resultFields
                if (request.resultFields !== undefined) {
                    const resultFields = request.resultFields;
                    const allFieldsArray = resultFields.split(',').map(f => f.trim()).filter(f => f && f !== 'id' && f !== 'score');

                    const highRelevancyFields = [];
                    const lowRelevancyFields = [];
                    const contentFields = [];

                    // Simple heuristic: assign fields based on common patterns
                    allFieldsArray.forEach(field => {
                        if (field.includes('title') || field.includes('name')) {
                            highRelevancyFields.push(field);
                        } else if (field.includes('cat') || field.includes('tag') || field.includes('type')) {
                            lowRelevancyFields.push(field);
                        } else if (field.includes('content') || field.includes('text') || field.includes('description')) {
                            contentFields.push(field);
                        } else {
                            contentFields.push(field);
                        }
                    });

                    document.getElementById('highRelevancyFields').value = highRelevancyFields.join(',');
                    document.getElementById('lowRelevancyFields').value = lowRelevancyFields.join(',');
                    document.getElementById('contentFields').value = contentFields.join(',');
                }
            }
        }

        // Tab switching function
        function switchTab(tabName) {
            // Hide all tab contents
            const tabContents = document.querySelectorAll('.tab-content');
            tabContents.forEach(content => content.classList.remove('active'));

            // Remove active class from all tabs
            const tabs = document.querySelectorAll('.nav-tab');
            tabs.forEach(tab => tab.classList.remove('active'));

            // Show selected tab content
            document.getElementById(tabName).classList.add('active');

            // Add active class to clicked tab
            event.target.classList.add('active');
        }

        // Initialize the search app
        const searchApp = new SearchApp();
