/**
 * Embedding Settings JavaScript
 * Handles the embedding configuration UI and API calls
 */

// Available providers and models loaded from API
let providerOptions = [];
let availableModels = {};
let currentSettings = null;
let originalValues = {};
let autoSaveListenersAttached = false;

// safeFetch is now provided by utils/safe-fetch.js loaded in base.html

/**
 * Initialize the page
 */
document.addEventListener('DOMContentLoaded', function() {
    // Load available models first, then settings
    loadAvailableModels().then(() => {
        // After models are loaded, load current settings
        loadCurrentSettings();
    });

    // Setup provider change handler
    document.getElementById('embedding-provider').addEventListener('change', function() {
        updateModelOptions();
        toggleOllamaUrlField();
    });

    // Setup model change handler
    document.getElementById('embedding-model').addEventListener('change', updateModelDescription);

    // Setup Ollama URL change handler
    document.getElementById('ollama-url').addEventListener('input', function() {
        // Mark as changed if needed
    });

    // Setup config form submission
    document.getElementById('rag-config-form').addEventListener('submit', handleConfigSubmit);
});

/**
 * Load available embedding providers and models
 */
async function loadAvailableModels() {
    try {
        const response = await safeFetch('/library/api/rag/models');
        const data = await response.json();

        if (data.success) {
            providerOptions = data.provider_options || [];
            availableModels = data.providers || {};

            // Populate provider dropdown
            populateProviders();

            // Update models for current provider (don't select yet, wait for settings)
            updateModelOptions();

            // Update provider information
            updateProviderInfo();
        } else {
            showError('Failed to load available models: ' + data.error);
        }
    } catch (error) {
        SafeLogger.error('Error loading models:', error);
        showError('Failed to load available models');
    }
}

/**
 * Load current RAG settings from database
 */
async function loadCurrentSettings() {
    try {
        const response = await safeFetch('/library/api/rag/settings');
        const data = await response.json();

        if (data.success && data.settings) {
            currentSettings = data.settings;
            const settings = data.settings;

            // Set provider
            const providerSelect = document.getElementById('embedding-provider');
            if (settings.embedding_provider) {
                providerSelect.value = settings.embedding_provider;
            }

            // Update models for this provider
            updateModelOptions();

            // Set model
            const modelSelect = document.getElementById('embedding-model');
            if (settings.embedding_model) {
                modelSelect.value = settings.embedding_model;
                updateModelDescription();
            }

            // Set chunk size and overlap
            if (settings.chunk_size) {
                document.getElementById('chunk-size').value = settings.chunk_size;
            }
            if (settings.chunk_overlap) {
                document.getElementById('chunk-overlap').value = settings.chunk_overlap;
            }

            // Set new advanced settings
            if (settings.splitter_type) {
                document.getElementById('splitter-type').value = settings.splitter_type;
            }
            if (settings.distance_metric) {
                document.getElementById('distance-metric').value = settings.distance_metric;
            }
            if (settings.index_type) {
                document.getElementById('index-type').value = settings.index_type;
            }
            if (settings.normalize_vectors !== undefined) {
                document.getElementById('normalize-vectors').checked = settings.normalize_vectors;
            }
            if (settings.text_separators) {
                // Convert array to JSON string for display
                document.getElementById('text-separators').value = JSON.stringify(settings.text_separators);
            }

            // Load Ollama URL from global settings
            await loadOllamaUrl();

            // Show/hide Ollama URL field based on provider
            toggleOllamaUrlField();

            // Update the saved defaults display
            renderSavedDefaults(settings);

            // Store original values for change tracking (read from DOM after all fields are set)
            originalValues = {
                'local_search_embedding_provider': document.getElementById('embedding-provider').value,
                'local_search_embedding_model': document.getElementById('embedding-model').value,
                'local_search_chunk_size': parseInt(document.getElementById('chunk-size').value) || 1000,
                'local_search_chunk_overlap': parseInt(document.getElementById('chunk-overlap').value) || 200,
                'local_search_splitter_type': document.getElementById('splitter-type').value,
                'local_search_distance_metric': document.getElementById('distance-metric').value,
                'local_search_index_type': document.getElementById('index-type').value,
                'local_search_normalize_vectors': document.getElementById('normalize-vectors').checked,
                'local_search_text_separators': (function() {
                    try { return JSON.parse(document.getElementById('text-separators').value); }
                    catch (e) { return ["\n\n", "\n", ". ", " ", ""]; }
                })(),
                'embeddings.ollama.url': document.getElementById('ollama-url').value
            };

            // Attach auto-save listeners after original values are captured
            attachAutoSaveListeners();
        }
    } catch (error) {
        SafeLogger.error('Error loading current settings:', error);
        // Don't show error to user - just use defaults
    }
}

/**
 * Render the saved default settings display
 */
function renderSavedDefaults(settings) {
    const container = document.getElementById('saved-default-settings');
    if (!container) return;

    // Get provider display name
    const providerLabels = {
        'sentence_transformers': 'Sentence Transformers (Local)',
        'ollama': 'Ollama (Local)',
        'openai': 'OpenAI API'
    };
    const providerLabel = providerLabels[settings.embedding_provider] || settings.embedding_provider;

    // bearer:disable javascript_lang_dangerous_insert_html
    container.innerHTML = `
        <div class="ldr-info-item">
            <span class="ldr-info-label">Provider:</span>
            <span class="ldr-info-value">${escapeHtml(providerLabel)}</span>
        </div>
        <div class="ldr-info-item">
            <span class="ldr-info-label">Embedding Model:</span>
            <span class="ldr-info-value">${escapeHtml(settings.embedding_model || '')}</span>
        </div>
        <div class="ldr-info-item">
            <span class="ldr-info-label">Chunk Size:</span>
            <span class="ldr-info-value">${escapeHtml(String(settings.chunk_size ?? 1000))} characters</span>
        </div>
        <div class="ldr-info-item">
            <span class="ldr-info-label">Chunk Overlap:</span>
            <span class="ldr-info-value">${escapeHtml(String(settings.chunk_overlap ?? 200))} characters</span>
        </div>
        <div class="ldr-info-item">
            <span class="ldr-info-label">Splitter Type:</span>
            <span class="ldr-info-value">${escapeHtml(settings.splitter_type || 'recursive')}</span>
        </div>
        <div class="ldr-info-item">
            <span class="ldr-info-label">Distance Metric:</span>
            <span class="ldr-info-value">${escapeHtml(settings.distance_metric || 'cosine')}</span>
        </div>
        <div class="ldr-info-item">
            <span class="ldr-info-label">Index Type:</span>
            <span class="ldr-info-value">${escapeHtml(settings.index_type || 'flat')}</span>
        </div>
    `;
}

/**
 * Compare two values for equality, handling type coercion and objects
 */
function areValuesEqual(a, b) {
    if (a === b) return true;
    // Handle string/number comparison (e.g. "1000" vs 1000)
    if (String(a) === String(b)) return true;
    // Handle JSON comparison for arrays/objects
    try {
        return JSON.stringify(a) === JSON.stringify(b);
    } catch (e) {
        return false;
    }
}

/**
 * Format a value for display in notifications
 */
function formatValueForDisplay(value) {
    if (value === null || value === undefined || value === '') {
        return 'empty';
    } else if (typeof value === 'boolean') {
        return value ? 'enabled' : 'disabled';
    } else if (Array.isArray(value)) {
        return JSON.stringify(value);
    } else if (typeof value === 'string' && value.length > 30) {
        return value.substring(0, 28) + '...';
    } else {
        return String(value);
    }
}

/**
 * Save a single setting via the settings API and show a notification
 * @param {string} key - The settings database key (e.g. 'local_search_embedding_provider')
 * @param {*} value - The new value to save
 * @param {string} displayName - Human-readable name for the notification
 * @param {*} oldValue - Previous value for old → new display
 */
async function saveSetting(key, value, displayName, oldValue) {
    // Skip no-ops
    if (oldValue !== undefined && areValuesEqual(value, oldValue)) {
        return;
    }

    try {
        const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
        const response = await safeFetch('/settings/api/' + key, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': csrfToken
            },
            body: JSON.stringify({ value: value })
        });

        const data = await response.json();
        if (data.error) {
            if (window.ui && window.ui.showMessage) {
                window.ui.showMessage('Failed to save ' + displayName + ': ' + data.error, 'error');
            } else {
                showError('Failed to save ' + displayName + ': ' + data.error);
            }
            return;
        }

        // Show success notification with old → new
        const oldDisplay = formatValueForDisplay(oldValue);
        const newDisplay = formatValueForDisplay(value);
        const message = displayName + ': ' + oldDisplay + ' \u2192 ' + newDisplay;

        if (window.ui && window.ui.showMessage) {
            window.ui.showMessage(message, 'success', 6000);
        } else {
            showSuccess(message);
        }

        // Update tracked original value
        originalValues[key] = value;

        // Refresh the saved defaults panel
        refreshSavedDefaults();
    } catch (error) {
        SafeLogger.error('Error saving setting ' + key + ':', error);
        if (window.ui && window.ui.showMessage) {
            window.ui.showMessage('Failed to save ' + displayName, 'error');
        } else {
            showError('Failed to save ' + displayName);
        }
    }
}

/**
 * Refresh the saved defaults display from current form values
 */
function refreshSavedDefaults() {
    var textSeps;
    try { textSeps = JSON.parse(document.getElementById('text-separators').value); }
    catch (e) { textSeps = []; }

    renderSavedDefaults({
        embedding_provider: document.getElementById('embedding-provider').value,
        embedding_model: document.getElementById('embedding-model').value,
        chunk_size: parseInt(document.getElementById('chunk-size').value) || 1000,
        chunk_overlap: parseInt(document.getElementById('chunk-overlap').value) || 200,
        splitter_type: document.getElementById('splitter-type').value,
        distance_metric: document.getElementById('distance-metric').value,
        index_type: document.getElementById('index-type').value,
        normalize_vectors: document.getElementById('normalize-vectors').checked,
        text_separators: textSeps
    });
}

/**
 * Attach auto-save event listeners to all form elements.
 * Called after original values are captured from the loaded settings.
 */
function attachAutoSaveListeners() {
    if (autoSaveListenersAttached) return;
    autoSaveListenersAttached = true;

    // Provider dropdown - change (immediate)
    document.getElementById('embedding-provider').addEventListener('change', function() {
        var value = this.value;
        var oldValue = originalValues['local_search_embedding_provider'];
        saveSetting('local_search_embedding_provider', value, 'Embedding provider', oldValue);
    });

    // Model dropdown - change (immediate)
    document.getElementById('embedding-model').addEventListener('change', function() {
        var value = this.value;
        var oldValue = originalValues['local_search_embedding_model'];
        saveSetting('local_search_embedding_model', value, 'Embedding model', oldValue);
    });

    // Chunk size - blur / Enter
    var chunkSizeEl = document.getElementById('chunk-size');
    function saveChunkSize() {
        var value = parseInt(chunkSizeEl.value);
        if (isNaN(value) || value < 100 || value > 5000) return;
        var oldValue = originalValues['local_search_chunk_size'];
        saveSetting('local_search_chunk_size', value, 'Chunk size', oldValue);
    }
    chunkSizeEl.addEventListener('blur', saveChunkSize);
    chunkSizeEl.addEventListener('keydown', function(e) {
        if (e.key === 'Enter') { e.preventDefault(); saveChunkSize(); }
    });

    // Chunk overlap - blur / Enter
    var chunkOverlapEl = document.getElementById('chunk-overlap');
    function saveChunkOverlap() {
        var value = parseInt(chunkOverlapEl.value);
        if (isNaN(value) || value < 0 || value > 1000) return;
        var oldValue = originalValues['local_search_chunk_overlap'];
        saveSetting('local_search_chunk_overlap', value, 'Chunk overlap', oldValue);
    }
    chunkOverlapEl.addEventListener('blur', saveChunkOverlap);
    chunkOverlapEl.addEventListener('keydown', function(e) {
        if (e.key === 'Enter') { e.preventDefault(); saveChunkOverlap(); }
    });

    // Splitter type - change (immediate)
    document.getElementById('splitter-type').addEventListener('change', function() {
        var value = this.value;
        var oldValue = originalValues['local_search_splitter_type'];
        saveSetting('local_search_splitter_type', value, 'Splitter type', oldValue);
    });

    // Distance metric - change (immediate)
    document.getElementById('distance-metric').addEventListener('change', function() {
        var value = this.value;
        var oldValue = originalValues['local_search_distance_metric'];
        saveSetting('local_search_distance_metric', value, 'Distance metric', oldValue);
    });

    // Index type - change (immediate)
    document.getElementById('index-type').addEventListener('change', function() {
        var value = this.value;
        var oldValue = originalValues['local_search_index_type'];
        saveSetting('local_search_index_type', value, 'Index type', oldValue);
    });

    // Normalize vectors - change (immediate)
    document.getElementById('normalize-vectors').addEventListener('change', function() {
        var value = this.checked;
        var oldValue = originalValues['local_search_normalize_vectors'];
        saveSetting('local_search_normalize_vectors', value, 'Normalize vectors', oldValue);
    });

    // Text separators - blur
    document.getElementById('text-separators').addEventListener('blur', function() {
        var rawValue = this.value.trim();
        if (!rawValue) return;
        try {
            var value = JSON.parse(rawValue);
            if (!Array.isArray(value)) {
                showError('Text separators must be a JSON array');
                return;
            }
            var oldValue = originalValues['local_search_text_separators'];
            saveSetting('local_search_text_separators', value, 'Text separators', oldValue);
        } catch (e) {
            showError('Invalid JSON format for text separators');
        }
    });

    // Ollama URL - blur / Enter
    var ollamaUrlEl = document.getElementById('ollama-url');
    function saveOllamaUrlAuto() {
        var value = ollamaUrlEl.value.trim();
        var oldValue = originalValues['embeddings.ollama.url'];
        saveSetting('embeddings.ollama.url', value, 'Ollama URL', oldValue);
    }
    ollamaUrlEl.addEventListener('blur', saveOllamaUrlAuto);
    ollamaUrlEl.addEventListener('keydown', function(e) {
        if (e.key === 'Enter') { e.preventDefault(); saveOllamaUrlAuto(); }
    });
}

/**
 * Populate provider dropdown
 */
function populateProviders() {
    const providerSelect = document.getElementById('embedding-provider');
    const currentValue = providerSelect.value;

    // Clear existing options
    providerSelect.innerHTML = '';

    // Add provider options
    providerOptions.forEach(provider => {
        const option = document.createElement('option');
        option.value = provider.value;
        option.textContent = provider.available === false
            ? provider.label + ' (unavailable)'
            : provider.label;
        providerSelect.appendChild(option);
    });

    // Restore previous value if it exists
    if (currentValue && Array.from(providerSelect.options).some(opt => opt.value === currentValue)) {
        providerSelect.value = currentValue;
    } else if (providerSelect.options.length > 0) {
        providerSelect.value = providerSelect.options[0].value;
    }
}

/**
 * Update model dropdown based on selected provider
 */
function updateModelOptions() {
    const provider = document.getElementById('embedding-provider').value;
    const modelSelect = document.getElementById('embedding-model');
    const descriptionSpan = document.getElementById('model-description');

    // Remove any previous provider warning
    var oldWarning = document.getElementById('provider-unavailable-warning');
    if (oldWarning) oldWarning.remove();

    // Check if selected provider is unavailable
    var providerInfo = providerOptions.find(function(p) { return p.value === provider; });
    if (providerInfo && providerInfo.available === false) {
        var warning = document.createElement('div');
        warning.id = 'provider-unavailable-warning';
        warning.className = 'ldr-alert ldr-alert-danger';
        warning.style.marginTop = '8px';
        // bearer:disable javascript_lang_dangerous_insert_html
        warning.innerHTML = '<i class="fas fa-exclamation-triangle"></i> ' +
            escapeHtml(providerInfo.label) +
            ' is not reachable. Check the URL or service and try again.';
        var providerSelect = document.getElementById('embedding-provider');
        providerSelect.parentNode.insertBefore(warning, providerSelect.nextSibling);
    }

    // Clear existing options
    modelSelect.innerHTML = '';

    // Add models for selected provider
    const models = availableModels[provider] || [];
    models.forEach(modelData => {
        const option = document.createElement('option');
        option.value = modelData.value;

        // Mark embedding compatibility when the provider supplies it
        if (modelData.is_embedding === true) {
            option.textContent = modelData.label + ' (Embedding)';
            option.style.color = 'var(--ldr-success, #2e7d32)';
        } else if (modelData.is_embedding === false) {
            option.textContent = modelData.label + ' (LLM — may not support embeddings)';
            option.style.color = 'var(--ldr-warning, #f9a825)';
        } else {
            option.textContent = modelData.label;
        }

        modelSelect.appendChild(option);
    });

    if (models.length === 0) {
        const placeholder = document.createElement('option');
        placeholder.value = '';
        placeholder.textContent = 'No models available';
        placeholder.disabled = true;
        modelSelect.appendChild(placeholder);
    }

    // Update description and model hint
    updateModelDescription();
    updateModelHint();

    // Add change handler for model selection (remove old handler first)
    modelSelect.removeEventListener('change', onModelChange);
    modelSelect.addEventListener('change', onModelChange);
}

/**
 * Combined handler for model dropdown changes.
 */
function onModelChange() {
    updateModelDescription();
    updateModelHint();
}

/**
 * Show a contextual hint below the model dropdown based on the
 * selected model's embedding compatibility.
 */
function updateModelHint() {
    // Remove previous hint
    var oldHint = document.getElementById('model-embedding-hint');
    if (oldHint) oldHint.remove();

    const provider = document.getElementById('embedding-provider').value;
    const modelSelect = document.getElementById('embedding-model');
    const models = availableModels[provider] || [];
    const selected = models.find(function(m) { return m.value === modelSelect.value; });

    if (!selected || selected.is_embedding === undefined) return;

    var hint = document.createElement('small');
    hint.id = 'model-embedding-hint';
    hint.className = 'ldr-form-text';
    hint.style.display = 'block';
    hint.style.marginTop = '4px';

    if (selected.is_embedding === true) {
        hint.style.color = 'var(--ldr-success, #2e7d32)';
        // bearer:disable javascript_lang_dangerous_insert_html
        hint.innerHTML = '<i class="fas fa-check-circle"></i> Dedicated embedding model &mdash; recommended for search and RAG.';
    } else {
        hint.style.color = 'var(--ldr-warning, #f9a825)';
        // bearer:disable javascript_lang_dangerous_insert_html
        hint.innerHTML = '<i class="fas fa-exclamation-circle"></i> This is an LLM, not a dedicated embedding model. ' +
            'Some LLMs can still produce embeddings but results vary. Use <strong>Test Embedding Model</strong> to verify.';
    }

    modelSelect.parentNode.appendChild(hint);
}

/**
 * Update model description text
 */
function updateModelDescription() {
    const provider = document.getElementById('embedding-provider').value;
    const modelSelect = document.getElementById('embedding-model');
    const descriptionSpan = document.getElementById('model-description');

    // Get selected model's label which contains the description
    const selectedOption = modelSelect.options[modelSelect.selectedIndex];
    if (selectedOption) {
        // Extract description from label (after the dash)
        const label = selectedOption.textContent;
        const parts = label.split(' - ');
        if (parts.length > 1) {
            descriptionSpan.textContent = parts.slice(1).join(' - ');
        } else {
            descriptionSpan.textContent = '';
        }
    } else {
        descriptionSpan.textContent = '';
    }
}

/**
 * Update provider information display
 */
function updateProviderInfo() {
    const providerInfo = document.getElementById('provider-info');

    let infoHTML = '';

    providerOptions.forEach(provider => {
        const providerKey = provider.value;
        const models = availableModels[providerKey] || [];

        // Add provider-specific notes
        let providerNote = '';
        if (providerKey === 'ollama') {
            const embeddingCount = models.filter(function(m) { return m.is_embedding === true; }).length;
            providerNote = `
                <div class="ldr-alert ldr-alert-info" style="margin-top: 10px; padding: 8px 12px; font-size: 0.85em;">
                    <i class="fas fa-info-circle"></i>
                    <strong>${embeddingCount} embedding model${embeddingCount !== 1 ? 's' : ''}</strong> detected
                    out of ${models.length} total.
                    <br><br>
                    <strong>Dedicated embedding models</strong> (e.g. nomic-embed-text, mxbai-embed-large)
                    are optimized for search and produce compact, high-quality vectors.
                    <br><br>
                    <strong>Some LLMs can also generate embeddings</strong> via Ollama's embed API,
                    but not all do &mdash; for example deepseek-r1 and nemotron work,
                    while qwen3 does not. LLM embeddings tend to have much higher dimensions
                    and are not optimized for similarity search.
                    <br><br>
                    Use the <strong>Test Embedding Model</strong> button above to verify
                    your selection works before saving.
                </div>
            `;
        }

        var statusIcon, statusText, statusClass;
        if (provider.available === false) {
            statusIcon = 'fa-exclamation-triangle';
            statusText = 'Not reachable';
            statusClass = 'ldr-provider-status-unavailable';
        } else {
            statusIcon = 'fa-check-circle';
            statusText = 'Ready';
            statusClass = '';
        }

        const embCount = models.filter(function(m) { return m.is_embedding === true; }).length;
        const modelCountLabel = embCount > 0
            ? `${embCount} embedding / ${models.length} total`
            : `${models.length}`;

        infoHTML += `
            <div class="ldr-stat-card">
                <h4>${escapeHtml(provider.label)}</h4>
                <p><strong>Models Available:</strong> ${modelCountLabel}</p>
                <div class="provider-status ${statusClass}">
                    <i class="fas ${statusIcon}"></i> ${statusText}
                </div>
                ${providerNote}
            </div>
        `;
    });

    // bearer:disable javascript_lang_dangerous_insert_html
    providerInfo.innerHTML = infoHTML;
}

/**
 * Handle configuration form submission
 */
async function handleConfigSubmit(event) {
    event.preventDefault();
    SafeLogger.log('🚀 Configuration form submitted!');

    const provider = document.getElementById('embedding-provider').value;

    // Save Ollama URL first if provider is ollama
    if (provider === 'ollama') {
        const ollamaUrlSaved = await saveOllamaUrl();
        if (!ollamaUrlSaved) {
            showError('Failed to save Ollama URL');
            return;
        }
    }

    // Get text separators and parse JSON
    let textSeparators;
    const textSeparatorsValue = document.getElementById('text-separators').value.trim();
    if (textSeparatorsValue) {
        try {
            textSeparators = JSON.parse(textSeparatorsValue);
            if (!Array.isArray(textSeparators)) {
                showError('Text separators must be a JSON array');
                return;
            }
        } catch (e) {
            showError('Invalid JSON format for text separators');
            return;
        }
    } else {
        // Use default if empty
        textSeparators = ["\n\n", "\n", ". ", " ", ""];
    }

    const formData = {
        embedding_provider: provider,
        embedding_model: document.getElementById('embedding-model').value,
        chunk_size: parseInt(document.getElementById('chunk-size').value),
        chunk_overlap: parseInt(document.getElementById('chunk-overlap').value),
        splitter_type: document.getElementById('splitter-type').value,
        distance_metric: document.getElementById('distance-metric').value,
        index_type: document.getElementById('index-type').value,
        normalize_vectors: document.getElementById('normalize-vectors').checked,
        text_separators: textSeparators
    };

    SafeLogger.log('📋 Form data:', formData);

    // Validation
    if (!formData.embedding_provider) {
        SafeLogger.error('❌ No provider selected');
        showError('Please select an embedding provider');
        return;
    }

    if (!formData.embedding_model) {
        SafeLogger.error('❌ No model selected');
        showError('Please select an embedding model');
        return;
    }

    if (formData.chunk_size < 100 || formData.chunk_size > 5000) {
        SafeLogger.error('❌ Invalid chunk size:', formData.chunk_size);
        showError('Chunk size must be between 100 and 5000 characters');
        return;
    }

    if (formData.chunk_overlap < 0 || formData.chunk_overlap > 1000) {
        SafeLogger.error('❌ Invalid chunk overlap:', formData.chunk_overlap);
        showError('Chunk overlap must be between 0 and 1000 characters');
        return;
    }

    SafeLogger.log('✅ Form validation passed');

    try {
        const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
        SafeLogger.log('🔐 CSRF Token:', csrfToken ? 'Found' : 'Not found');

        const response = await safeFetch('/library/api/rag/configure', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': csrfToken
            },
            body: JSON.stringify(formData)
        });

        SafeLogger.log('📡 Response status:', response.status);
        const responseText = await response.text();
        SafeLogger.log('📄 Response text:', responseText);

        let data;
        try {
            data = JSON.parse(responseText);
        } catch (parseError) {
            SafeLogger.error('❌ Failed to parse JSON:', parseError);
            SafeLogger.error('❌ Response was:', responseText);
            showError('Server returned invalid response. Check console for details.');
            return;
        }

        if (data.success) {
            SafeLogger.log('✅ Default settings saved successfully!');
            if (window.ui && window.ui.showMessage) {
                window.ui.showMessage('Default embedding settings saved successfully!', 'success', 6000);
            } else {
                showSuccess('Default embedding settings saved successfully! New collections will use these settings.');
            }
            currentSettings = formData;
            // Update original values after batch save
            originalValues['local_search_embedding_provider'] = formData.embedding_provider;
            originalValues['local_search_embedding_model'] = formData.embedding_model;
            originalValues['local_search_chunk_size'] = formData.chunk_size;
            originalValues['local_search_chunk_overlap'] = formData.chunk_overlap;
            originalValues['local_search_splitter_type'] = formData.splitter_type;
            originalValues['local_search_distance_metric'] = formData.distance_metric;
            originalValues['local_search_index_type'] = formData.index_type;
            originalValues['local_search_normalize_vectors'] = formData.normalize_vectors;
            originalValues['local_search_text_separators'] = formData.text_separators;
            originalValues['embeddings.ollama.url'] = document.getElementById('ollama-url').value.trim();
            // Update the saved defaults display
            renderSavedDefaults(formData);
            // Re-check provider availability (e.g. Ollama URL may have changed)
            await loadAvailableModels();
        } else {
            SafeLogger.error('❌ Server returned error:', data.error);
            showError('Failed to save default settings: ' + data.error);
        }
    } catch (error) {
        SafeLogger.error('❌ Error updating configuration:', error);
        showError('Failed to save configuration: ' + error.message);
    }
}

/**
 * Test configuration by sending a real embedding request
 */
async function testConfiguration() {
    const provider = document.getElementById('embedding-provider').value;
    const model = document.getElementById('embedding-model').value;
    const testBtn = document.getElementById('test-config-btn');
    const testResult = document.getElementById('test-result');

    if (!provider || !model) {
        showError('Please select a provider and model first');
        return;
    }

    // Disable button during test
    testBtn.disabled = true;
    testBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Testing...';

    testResult.style.display = 'block';
    testResult.innerHTML = '<div class="ldr-alert ldr-alert-info"><i class="fas fa-spinner fa-spin"></i> Testing embedding model... For local providers this may take a moment while the model is loaded into memory.</div>';

    try {
        const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');

        // Send test embedding request with selected configuration
        const response = await safeFetch('/library/api/rag/test-embedding', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': csrfToken
            },
            body: JSON.stringify({
                provider: provider,
                model: model,
                test_text: 'This is a test sentence to verify the embedding model is working correctly.'
            })
        });

        const data = await response.json();

        if (data.success) {
            const responseTime = parseInt(data.response_time_ms);
            const slowHint = responseTime > 3000
                ? '<br><i class="fas fa-info-circle"></i> <em>Slow response time may be due to initial model loading. Test again for a more accurate measurement.</em>'
                : '';
            // bearer:disable javascript_lang_dangerous_insert_html
            testResult.innerHTML = `
                <div class="ldr-alert ldr-alert-success">
                    <i class="fas fa-check-circle"></i> <strong>Test Passed!</strong><br>
                    Model: ${window.XSSProtection.escapeHtml(model)}<br>
                    Provider: ${window.XSSProtection.escapeHtml(provider)}<br>
                    Embedding dimension: ${window.XSSProtection.escapeHtml(String(data.dimension))}<br>
                    Response time: ${window.XSSProtection.escapeHtml(String(data.response_time_ms))}ms
                    ${slowHint}
                </div>
            `;
            showSuccess('Embedding test passed!');
        } else {
            // bearer:disable javascript_lang_dangerous_insert_html
            testResult.innerHTML = `
                <div class="ldr-alert ldr-alert-danger">
                    <i class="fas fa-times-circle"></i> <strong>Test Failed!</strong><br>
                    Error: ${window.XSSProtection.escapeHtml(data.error || 'Unknown error')}
                </div>
            `;
            // Safe: showError escapes internally
            showError('Embedding test failed: ' + (data.error || 'Unknown error'));
        }
    } catch (error) {
        // bearer:disable javascript_lang_dangerous_insert_html
        testResult.innerHTML = `
            <div class="ldr-alert ldr-alert-danger">
                <i class="fas fa-times-circle"></i> <strong>Test Failed!</strong><br>
                Error: ${window.XSSProtection.escapeHtml(error.message)}
            </div>
        `;
        // Safe: showError escapes internally
        showError('Test failed: ' + error.message);
    } finally {
        // Re-enable button
        testBtn.disabled = false;
        testBtn.innerHTML = '<i class="fas fa-play"></i> Test Embedding Model';
    }
}

/**
 * Show success message.
 * @param {string} message - Raw, unescaped text. HTML-escaping is handled internally.
 */
function showSuccess(message) {
    const alertDiv = document.createElement('div');
    alertDiv.className = 'ldr-alert ldr-alert-success';
    // Escape message before including in HTML template
    // bearer:disable javascript_lang_dangerous_insert_html
    alertDiv.innerHTML = `<i class="fas fa-check-circle"></i>${escapeHtml(message)}`;

    // Insert at the top of the container
    const container = document.querySelector('.ldr-library-container');
    container.insertBefore(alertDiv, container.firstChild);

    // Auto-remove after 5 seconds
    setTimeout(() => {
        if (alertDiv.parentNode) {
            alertDiv.parentNode.removeChild(alertDiv);
        }
    }, 5000);
}

/**
 * Show info message.
 * @param {string} message - Raw, unescaped text. HTML-escaping is handled internally.
 */
function showInfo(message) {
    const alertDiv = document.createElement('div');
    alertDiv.className = 'ldr-alert ldr-alert-info';
    // Escape message before including in HTML template
    // bearer:disable javascript_lang_dangerous_insert_html
    alertDiv.innerHTML = `<i class="fas fa-info-circle"></i>${escapeHtml(message)}`;

    // Insert at the top of the container
    const container = document.querySelector('.ldr-library-container');
    container.insertBefore(alertDiv, container.firstChild);

    // Auto-remove after 3 seconds
    setTimeout(() => {
        if (alertDiv.parentNode) {
            alertDiv.parentNode.removeChild(alertDiv);
        }
    }, 3000);
}

/**
 * Show error message.
 * @param {string} message - Raw, unescaped text. HTML-escaping is handled internally.
 */
function showError(message) {
    const alertDiv = document.createElement('div');
    alertDiv.className = 'ldr-alert ldr-alert-danger';
    // Escape message before including in HTML template
    // bearer:disable javascript_lang_dangerous_insert_html
    alertDiv.innerHTML = `<i class="fas fa-exclamation-triangle"></i>${escapeHtml(message)}`;

    // Insert at the top of the container
    const container = document.querySelector('.ldr-library-container');
    container.insertBefore(alertDiv, container.firstChild);

    // Auto-remove after 5 seconds
    setTimeout(() => {
        if (alertDiv.parentNode) {
            alertDiv.parentNode.removeChild(alertDiv);
        }
    }, 5000);
}

// Prefer the full escapeHtml from xss-protection.js; inline fallback if it hasn't loaded yet
// bearer:disable javascript_lang_manual_html_sanitization
var escapeHtml = window.escapeHtml || function(str) {
    return String(str).replace(/[&<>"']/g, function(m) {
        return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m];
    });
};


/**
 * Toggle Ollama URL field visibility based on selected provider
 */
function toggleOllamaUrlField() {
    const provider = document.getElementById('embedding-provider').value;
    const ollamaUrlGroup = document.getElementById('ollama-url-group');

    if (provider === 'ollama') {
        ollamaUrlGroup.style.display = 'block';
    } else {
        ollamaUrlGroup.style.display = 'none';
    }
}

/**
 * Load Ollama URL from settings
 */
async function loadOllamaUrl() {
    try {
        const response = await safeFetch('/settings/api/embeddings.ollama.url');
        const data = await response.json();

        if (data && data.value) {
            document.getElementById('ollama-url').value = data.value;
        }
    } catch (error) {
        SafeLogger.error('Error loading Ollama URL:', error);
    }
}

/**
 * Save Ollama URL to settings
 */
async function saveOllamaUrl() {
    const ollamaUrl = document.getElementById('ollama-url').value.trim();

    try {
        const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');

        const response = await safeFetch('/settings/api/embeddings.ollama.url', {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': csrfToken
            },
            body: JSON.stringify({
                value: ollamaUrl
            })
        });

        const data = await response.json();
        if (data.error) {
            SafeLogger.error('Failed to save Ollama URL:', data.error);
            return false;
        }
        return true;
    } catch (error) {
        SafeLogger.error('Error saving Ollama URL:', error);
        return false;
    }
}
