/**
 * Note: URLValidator is available globally via /static/js/security/url-validator.js
 * Research Details Page JavaScript
 * Handles displaying detailed metrics for a specific research session
 */
(function() {
    'use strict';

    // Shared formatting helpers live on window.formatting (services/formatting.js)
    const { formatNumber, formatCurrency, generateChartColors } = window.formatting;

    let researchId = null;
    let metricsData = null;
    let timelineChart = null;
    let currentChartView = 'bars';
    let lastTimelineData = null;
    let searchChart = null;

    // XSS protection: escape dynamic content before innerHTML interpolation
    // bearer:disable javascript_lang_manual_html_sanitization
    const escapeHtmlFallback = (str) => String(str ?? '').replace(/[&<>"']/g, (m) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'})[m]);
    const escapeHtml = window.escapeHtml || escapeHtmlFallback;

    /**
     * Get chart colors from CSS variables for theme support
     */
    function getChartColors() {
        const style = getComputedStyle(document.documentElement);
        return {
            // Primary accent
            accent: style.getPropertyValue('--accent-primary').trim() || '#6e4ff6',
            accentRgb: style.getPropertyValue('--accent-primary-rgb').trim() || '110, 79, 246',
            // Success color
            success: style.getPropertyValue('--success-color').trim() || '#0acf97',
            successRgb: style.getPropertyValue('--success-color-rgb').trim() || '10, 207, 151',
            // Text colors
            textPrimary: style.getPropertyValue('--text-primary').trim() || '#f5f5f5',
            textMuted: style.getPropertyValue('--text-muted').trim() || '#8a8aa0',
            // Tertiary accent (for input tokens)
            tertiary: style.getPropertyValue('--accent-tertiary').trim() || '#40bfff',
            tertiaryRgb: style.getPropertyValue('--accent-tertiary-rgb').trim() || '64, 191, 255',
            // Error color (for output tokens)
            error: style.getPropertyValue('--error-color').trim() || '#fa5c7c',
            errorRgb: style.getPropertyValue('--error-color-rgb').trim() || '250, 92, 124',
            // Background
            bgPrimary: style.getPropertyValue('--bg-primary').trim() || '#121212',
        };
    }

    // Get research ID from URL
    function getResearchIdFromUrl() {
        const id = URLBuilder.extractResearchIdFromPattern('details');
        SafeLogger.log('getResearchIdFromUrl called, extracted ID:', id);
        SafeLogger.log('Current URL:', window.location.href);
        SafeLogger.log('Current pathname:', window.location.pathname);
        return id;
    }

    // Load link analytics for the research
    async function loadLinkAnalytics() {
        try {
            SafeLogger.log('Loading link analytics for research:', researchId);

            const response = await fetch(`/metrics/api/metrics/research/${researchId}/links`);
            if (!response.ok) {
                SafeLogger.error('Failed to load link analytics:', response.status);
                return;
            }

            const result = await response.json();
            if (result.status !== 'success') {
                SafeLogger.error('Error loading link analytics:', result.message);
                return;
            }

            const data = result.data;

            // Show the link analytics sections
            document.getElementById('source-distribution-section').style.display = 'block';
            document.getElementById('link-analytics-section').style.display = 'block';

            // Update summary metrics
            document.getElementById('total-links').textContent = data.total_links || 0;
            document.getElementById('unique-domains').textContent = data.unique_domains || 0;

            // Update category metrics from LLM classification
            const domainCategories = data.domain_categories || {};
            const categoryEntries = Object.entries(domainCategories);

            // Update the first two category cards with actual data
            if (categoryEntries.length > 0) {
                document.getElementById('academic-sources').textContent = categoryEntries[0]?.[1] || 0;
                // Update label
                const academicLabel = document.querySelector('#academic-sources').previousElementSibling;
                if (academicLabel) academicLabel.textContent = categoryEntries[0]?.[0] || 'Category 1';
            } else {
                document.getElementById('academic-sources').textContent = 0;
            }

            if (categoryEntries.length > 1) {
                document.getElementById('news-sources').textContent = categoryEntries[1]?.[1] || 0;
                // Update label
                const newsLabel = document.querySelector('#news-sources').previousElementSibling;
                if (newsLabel) newsLabel.textContent = categoryEntries[1]?.[0] || 'Category 2';
            } else {
                document.getElementById('news-sources').textContent = 0;
            }

            // Display domain list
            const domainList = document.getElementById('domain-list');
            if (data.domains && data.domains.length > 0) {
                // Use DOMPurify for secure HTML rendering
                const domainHtml = data.domains.map(domain => `
                    <div style="display: flex; justify-content: space-between; align-items: center; padding: 0.75rem; border-bottom: 1px solid var(--border-color);">
                        <span style="font-weight: 500;">${window.escapeHtml(domain.domain)}</span>
                        <div style="display: flex; gap: 1rem; align-items: center;">
                            <span style="background: var(--primary-color); color: white; padding: 0.25rem 0.5rem; border-radius: 0.25rem; font-size: 0.875rem;">
                                ${window.escapeHtml(domain.count)} links
                            </span>
                            <span style="color: var(--text-secondary); font-size: 0.875rem;">
                                ${window.escapeHtml(domain.percentage)}%
                            </span>
                        </div>
                    </div>
                `).join('');
                if (window.sanitizeHtml) {
                    // bearer:disable javascript_lang_dangerous_insert_html
                    domainList.innerHTML = window.sanitizeHtml(domainHtml);
                } else if (window.escapeHtml) {
                    // bearer:disable javascript_lang_dangerous_insert_html
                    // eslint-disable-next-line no-unsanitized/property -- audited 2026-03-28: all interpolations use escapeHtml/esc, numeric coercion, or hardcoded strings
                    domainList.innerHTML = domainHtml; // Already escaped individual fields above
                } else {
                    domainList.textContent = 'Domain data unavailable (security module not loaded)';
                }
            } else if (window.safeSetInnerHTML) {
                window.safeSetInnerHTML(domainList, '<div style="text-align: center; color: var(--text-secondary); padding: 1rem;">No domain data available</div>', true);
            } else {
                domainList.textContent = 'No domain data available';
            }

            // Display resource samples
            const resourceSample = document.getElementById('resource-sample');
            if (data.resources && data.resources.length > 0) {
                // Use the local escapeHtml closure (defined at top of
                // file) instead of window.escapeHtml ?-fallback chains —
                // the local always exists, so the conditional always
                // takes the fallback branch and bypasses real escaping
                // when the global helper hasn't loaded yet.
                const escapeAttr = window.escapeHtmlAttribute || escapeHtml;
                const resourceHtml = data.resources.map(resource => `
                    <div style="padding: 0.75rem; border-bottom: 1px solid var(--border-color);">
                        <div style="font-weight: 500; margin-bottom: 0.25rem;">${escapeHtml(resource.title)}</div>
                        <a href="${escapeAttr(resource.url)}" target="_blank" rel="noopener noreferrer" style="color: var(--primary-color); text-decoration: none; font-size: 0.875rem; word-break: break-all;">
                            ${escapeHtml(resource.url)}
                        </a>
                        ${resource.preview ? `<div style="color: var(--text-secondary); font-size: 0.875rem; margin-top: 0.5rem;">${escapeHtml(resource.preview)}</div>` : ''}
                    </div>
                `).join('');
                if (window.sanitizeHtml) {
                    // bearer:disable javascript_lang_dangerous_insert_html
                    resourceSample.innerHTML = window.sanitizeHtml(resourceHtml);
                } else {
                    // bearer:disable javascript_lang_dangerous_insert_html
                    // eslint-disable-next-line no-unsanitized/property -- audited 2026-03-28: all interpolations use escapeHtml/esc, numeric coercion, or hardcoded strings
                    resourceSample.innerHTML = resourceHtml; // Fields already escaped above
                }
            } else if (window.safeSetInnerHTML) {
                window.safeSetInnerHTML(resourceSample, '<div style="text-align: center; color: var(--text-secondary); padding: 1rem;">No resource samples available</div>', true);
            } else {
                resourceSample.textContent = 'No resource samples available';
            }

            // Create generic source type pie chart
            if (domainCategories && Object.keys(domainCategories).length > 0) {
                const ctx = document.getElementById('source-type-chart');
                if (ctx) {
                    // Generate dynamic labels and data from whatever categories the LLM provides
                    const labels = Object.keys(domainCategories);
                    const chartData = Object.values(domainCategories);

                    // Generate colors dynamically based on number of categories
                    const colors = generateChartColors(labels.length);

                    new Chart(ctx, {
                        type: 'pie',
                        data: {
                            labels,
                            datasets: [{
                                data: chartData,
                                backgroundColor: colors.background,
                                borderColor: colors.border,
                                borderWidth: 1
                            }]
                        },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            plugins: {
                                legend: {
                                    position: 'bottom'
                                }
                            }
                        }
                    });
                }
            } else if (data.total_links > 0) {
                // Show placeholder when there are links but no categories
                const chartContainer = document.getElementById('source-type-chart').parentElement;
                chartContainer.innerHTML = `
                    <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 300px; text-align: center;">
                        <i class="fas fa-robot" style="font-size: 3rem; color: var(--text-secondary); margin-bottom: 1rem;"></i>
                        <h3 style="color: var(--text-primary); margin-bottom: 0.5rem;">AI Classification Not Available</h3>
                        <p style="color: var(--text-secondary); margin-bottom: 1rem; max-width: 400px;">
                            Domain categories haven't been classified yet. Use the button below to analyze your domains.
                        </p>
                    </div>
                `;
            } else {
                // Show no links message
                const chartContainer = document.getElementById('source-type-chart').parentElement;
                chartContainer.innerHTML = `
                    <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 300px; text-align: center;">
                        <i class="fas fa-link" style="font-size: 3rem; color: var(--text-secondary); margin-bottom: 1rem;"></i>
                        <h3 style="color: var(--text-primary); margin-bottom: 0.5rem;">No Links Available</h3>
                        <p style="color: var(--text-secondary);">This research session doesn't have any links to classify.</p>
                    </div>
                `;
            }

            // Always add the classify button if there are links (alongside the chart or placeholder)
            if (data.total_links > 0) {
                const sourceDistributionSection = document.getElementById('source-distribution-section');
                const cardContent = sourceDistributionSection.querySelector('.ldr-card-content');

                // Add classify button container after the chart
                const classifyContainer = document.createElement('div');
                classifyContainer.innerHTML = `
                    <div style="text-align: center; margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid var(--border-color);">
                        <button id="classify-domains-btn" class="btn btn-primary" style="display: flex; align-items: center; gap: 0.5rem; margin: 0 auto;">
                            <i class="fas fa-magic"></i>
                            Classify Domains with AI
                        </button>
                        <p style="color: var(--text-secondary); font-size: 0.875rem; margin-top: 0.5rem;">
                            Analyze and categorize all domains using AI classification
                        </p>
                    </div>
                `;
                cardContent.appendChild(classifyContainer);

                // Add click handler for link analytics button
                const classifyBtn = document.getElementById('classify-domains-btn');

                classifyBtn.addEventListener('click', () => {
                    window.location.href = '/metrics/links';
                });
            }

        } catch (error) {
            SafeLogger.error('Error loading link analytics:', error);
        }
    }

    // Load research metrics data
    async function loadResearchMetrics() {
        try {
            SafeLogger.log('Loading research metrics for ID:', researchId);

            // Show loading state
            document.getElementById('loading').style.display = 'block';
            document.getElementById('details-content').style.display = 'none';
            document.getElementById('error').style.display = 'none';

            // Load research details (includes strategy)
            SafeLogger.log('Fetching research details...');
            SafeLogger.log('Using research ID:', researchId);
            const detailsUrl = URLBuilder.historyDetails(researchId);
            SafeLogger.log('Details URL:', detailsUrl);
            SafeLogger.log('Full URL being fetched:', window.location.origin + detailsUrl);
            const detailsResponse = await fetch(detailsUrl);
            SafeLogger.log('Details response status:', detailsResponse.status);
            SafeLogger.log('Details response URL:', detailsResponse.url);

            let researchDetails = null;
            if (detailsResponse.ok) {
                researchDetails = await detailsResponse.json();
                SafeLogger.log('Research details loaded:', researchDetails);
            } else {
                SafeLogger.error('Failed to load research details:', detailsResponse.status);
                const errorText = await detailsResponse.text();
                SafeLogger.error('Error response:', errorText);
                try {
                    const errorJson = JSON.parse(errorText);
                    SafeLogger.error('Error JSON:', errorJson);
                    if (errorJson.message) {
                        throw new Error(errorJson.message);
                    }
                } catch {
                    // Not JSON, use original text
                }
                throw new Error(`Failed to load research details: ${detailsResponse.status}`);
            }

            // Load research metrics
            SafeLogger.log('Fetching research metrics...');
            const metricsResponse = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH, researchId));
            SafeLogger.log('Metrics response status:', metricsResponse.status);

            if (!metricsResponse.ok) {
                throw new Error(`Metrics API failed: ${metricsResponse.status}`);
            }

            const metricsResult = await metricsResponse.json();
            SafeLogger.log('Metrics result:', metricsResult);

            if (metricsResult.status !== 'success') {
                throw new Error('Failed to load research metrics');
            }

            metricsData = metricsResult.metrics;
            SafeLogger.log('Metrics data loaded:', metricsData);

            // Display research details first
            if (researchDetails) {
                displayResearchDetails(researchDetails);
            }

            // Load timeline metrics
            SafeLogger.log('Fetching timeline metrics...');
            const timelineResponse = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_TIMELINE, researchId));
            SafeLogger.log('Timeline response status:', timelineResponse.status);

            let timelineData = null;
            if (timelineResponse.ok) {
                const timelineResult = await timelineResponse.json();
                SafeLogger.log('Timeline result:', timelineResult);
                if (timelineResult.status === 'success') {
                    timelineData = timelineResult.metrics;
                }
            }

            // Load search metrics
            SafeLogger.log('Fetching search metrics...');
            const searchResponse = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_SEARCH, researchId));
            SafeLogger.log('Search response status:', searchResponse.status);

            let searchData = null;
            if (searchResponse.ok) {
                const searchResult = await searchResponse.json();
                SafeLogger.log('Search result:', searchResult);
                if (searchResult.status === 'success') {
                    searchData = searchResult.metrics;
                }
            }

            // Display all data
            SafeLogger.log('Displaying research metrics...');
            displayResearchMetrics();

            if (timelineData) {
                SafeLogger.log('Displaying timeline metrics...');
                displayTimelineMetrics(timelineData);

                SafeLogger.log('Chart.js available:', typeof Chart !== 'undefined');
                SafeLogger.log('Timeline data for chart:', timelineData);
                createTimelineChart(timelineData);
                displayCallStackTraces(timelineData.timeline);
            }

            if (searchData) {
                SafeLogger.log('Displaying search metrics...');
                displaySearchMetrics(searchData);
                createSearchChart(searchData);
            }

            // Load cost data
            SafeLogger.log('Loading cost data...');
            loadCostData();

            SafeLogger.log('Showing details content...');
            const loadingEl = document.getElementById('loading');
            const contentEl = document.getElementById('details-content');
            const errorEl = document.getElementById('error');

            loadingEl.style.display = 'none';
            errorEl.style.display = 'none';
            contentEl.style.display = 'block';

            // Show metrics sections
            SafeLogger.log('Showing metrics sections...');
            const tokenMetricsSection = document.getElementById('token-metrics-section');
            const searchMetricsSection = document.getElementById('search-metrics-section');

            if (tokenMetricsSection) {
                tokenMetricsSection.style.display = 'block';
                SafeLogger.log('Token metrics section shown');
            }

            const tokenUsageTopChart = document.getElementById('token-usage-top-chart');
            if (tokenUsageTopChart) {
                tokenUsageTopChart.style.display = 'block';
            }

            if (searchMetricsSection) {
                searchMetricsSection.style.display = 'block';
                SafeLogger.log('Search metrics section shown');
            }

            // Force visibility with CSS overrides
            contentEl.style.visibility = 'visible';
            contentEl.style.opacity = '1';
            contentEl.style.position = 'relative';
            contentEl.style.zIndex = '1000';

            SafeLogger.log('Loading display:', loadingEl.style.display);
            SafeLogger.log('Content display:', contentEl.style.display);
            SafeLogger.log('Error display:', errorEl.style.display);

            // Verify content is actually populated
            const totalTokensEl = document.getElementById('total-tokens');
            const researchQueryEl = document.getElementById('research-query');
            SafeLogger.log('Total tokens value:', totalTokensEl ? totalTokensEl.textContent : 'ELEMENT NOT FOUND');
            SafeLogger.log('Research query value:', researchQueryEl ? researchQueryEl.textContent : 'ELEMENT NOT FOUND');
            SafeLogger.log('Content element height:', contentEl.offsetHeight);
            SafeLogger.log('Content element children:', contentEl.children.length);

        } catch (error) {
            SafeLogger.error('Error loading research metrics:', error);
            SafeLogger.error('Error details:', error.message, error.stack);
            showError();
        }
    }

    // Display research details from history endpoint
    function displayResearchDetails(details) {
        SafeLogger.log('displayResearchDetails called with:', details);

        // Update basic research info
        if (details.query) {
            document.getElementById('research-query').textContent = details.query;
        }
        if (details.mode) {
            document.getElementById('research-mode').textContent = details.mode;
        }
        if (details.created_at) {
            const date = new Date(details.created_at);
            document.getElementById('research-date').textContent = date.toLocaleString();
        }

        // Update strategy information
        if (details.strategy) {
            document.getElementById('research-strategy').textContent = details.strategy;
        } else {
            document.getElementById('research-strategy').textContent = 'Not recorded';
        }

        // Update progress
        if (details.progress !== undefined) {
            const progressFill = document.getElementById('detail-progress-fill');
            const progressText = document.getElementById('detail-progress-percentage');
            if (progressFill && progressText) {
                progressFill.style.width = `${details.progress}%`;
                progressText.textContent = `${details.progress}%`;
            }
        }
    }

    // Display basic research metrics
    function displayResearchMetrics() {
        SafeLogger.log('displayResearchMetrics called with:', metricsData);
        if (!metricsData) {
            SafeLogger.error('No metrics data available');
            return;
        }

        // Update summary cards
        const totalTokensEl = document.getElementById('total-tokens');
        const totalTokens = formatNumber(metricsData.total_tokens || 0);
        SafeLogger.log('Setting total tokens to:', totalTokens);
        totalTokensEl.textContent = totalTokens;

        // Calculate prompt/completion tokens from model usage
        let totalPromptTokens = 0;
        let totalCompletionTokens = 0;
        let totalCalls = 0;
        let model = 'Unknown';

        if (metricsData.model_usage && metricsData.model_usage.length > 0) {
            metricsData.model_usage.forEach(usage => {
                totalPromptTokens += usage.prompt_tokens || 0;
                totalCompletionTokens += usage.completion_tokens || 0;
                totalCalls += usage.calls || 0;
                if (model === 'Unknown') {
                    model = usage.model || 'Unknown';
                }
            });
        }

        document.getElementById('prompt-tokens').textContent = formatNumber(totalPromptTokens);
        document.getElementById('completion-tokens').textContent = formatNumber(totalCompletionTokens);
        document.getElementById('llm-calls').textContent = formatNumber(metricsData.total_calls || totalCalls);

        // Update model info
        document.getElementById('model-used').textContent = model;

        // Response time will be updated by timeline data
        document.getElementById('avg-response-time').textContent = '0s';
    }

    // Display timeline metrics
    function displayTimelineMetrics(timelineData) {
        if (!timelineData) return;

        // Update research info from timeline data
        if (timelineData.research_details) {
            const details = timelineData.research_details;
            document.getElementById('research-query').textContent = details.query || 'Unknown';
            document.getElementById('research-mode').textContent = details.mode || 'Unknown';
            if (details.created_at) {
                const date = new Date(details.created_at);
                document.getElementById('research-date').textContent = date.toLocaleString();
            }
        }

        // Update summary info
        if (timelineData.summary) {
            const summary = timelineData.summary;
            const avgResponseTime = (summary.avg_response_time || 0) / 1000;
            document.getElementById('avg-response-time').textContent = `${avgResponseTime.toFixed(1)}s`;
        }

        // Display phase breakdown
        if (timelineData.phase_stats) {
            const container = document.getElementById('phase-breakdown');
            container.innerHTML = '';

            Object.entries(timelineData.phase_stats).forEach(([phaseName, stats]) => {
                const item = document.createElement('div');
                item.className = 'ldr-phase-stat-item';
                // bearer:disable javascript_lang_dangerous_insert_html
                // eslint-disable-next-line no-unsanitized/property -- audited 2026-03-28: all interpolations use escapeHtml/esc, numeric coercion, or hardcoded strings
                item.innerHTML = `
                    <div class="ldr-phase-name">${escapeHtml(phaseName)}</div>
                    <div class="ldr-phase-tokens">${formatNumber(stats.tokens)} tokens</div>
                    <div class="ldr-phase-calls">${formatNumber(stats.count)} calls</div>
                `;
                container.appendChild(item);
            });
        }
    }

    // Display search metrics
    function displaySearchMetrics(searchData) {
        if (!searchData) return;

        // Update search summary metrics
        const totalSearches = searchData.total_searches || 0;
        const totalResults = searchData.total_results || 0;
        const avgResponseTime = searchData.avg_response_time || 0;
        const successRate = searchData.success_rate || 0;

        document.getElementById('total-searches').textContent = formatNumber(totalSearches);
        document.getElementById('total-search-results').textContent = formatNumber(totalResults);
        document.getElementById('avg-search-response-time').textContent = `${avgResponseTime.toFixed(0)}ms`;
        document.getElementById('search-success-rate').textContent = `${successRate.toFixed(1)}%`;

        // Display search engine breakdown
        const container = document.getElementById('search-engine-breakdown');
        if (container && searchData.search_calls) {
            container.innerHTML = '';

            searchData.search_calls.forEach(call => {
                const item = document.createElement('div');
                item.className = 'ldr-search-engine-item';
                // bearer:disable javascript_lang_dangerous_insert_html
                // eslint-disable-next-line no-unsanitized/property -- audited 2026-03-28: all interpolations use escapeHtml/esc, numeric coercion, or hardcoded strings
                item.innerHTML = `
                    <div class="ldr-search-engine-info">
                        <div class="ldr-search-engine-name">${escapeHtml(call.engine || 'Unknown')}</div>
                        <div class="ldr-search-engine-query">${escapeHtml(call.query || 'No query')}</div>
                    </div>
                    <div class="ldr-search-engine-stats">
                        <div class="ldr-search-results">${formatNumber(call.results_count || 0)} results</div>
                        <div class="ldr-search-time">${((call.response_time_ms || 0) / 1000).toFixed(1)}s</div>
                    </div>
                `;
                container.appendChild(item);
            });
        }

        // Display per-engine aggregated performance
        displaySearchEnginePerformance(searchData.engine_stats || []);
        // Display search timeline with timestamps and status
        displaySearchTimeline(searchData.search_calls || []);
    }

    // Display search engine performance breakdown (aggregated by engine)
    function displaySearchEnginePerformance(enginePerformance) {
        const container = document.getElementById('search-engine-performance');
        if (!container) return;
        if (!enginePerformance || enginePerformance.length === 0) {
            // bearer:disable javascript_lang_dangerous_insert_html
            container.innerHTML = '<p style="text-align: center; color: var(--text-secondary);">No search engine data available</p>';
            return;
        }
        const html = enginePerformance.map(engine => `
            <div class="ldr-search-engine-item">
                <div class="ldr-search-engine-info">
                    <div class="ldr-search-engine-name">${escapeHtml(engine.engine || 'Unknown')}</div>
                </div>
                <div class="ldr-search-engine-stats">
                    <span>${engine.call_count ?? 0} searches</span>
                    <span>${(engine.success_rate ?? 0).toFixed(1)}% success</span>
                    <span>${((engine.avg_response_time ?? 0) / 1000).toFixed(2)}s avg</span>
                    <span>${formatNumber(engine.total_results ?? 0)} results</span>
                </div>
            </div>
        `).join('');
        // bearer:disable javascript_lang_dangerous_insert_html
        // eslint-disable-next-line no-unsanitized/property -- audited: all values escaped via escapeHtml or numeric coercion
        container.innerHTML = html;
    }

    // Display search timeline with timestamps and success/failure status
    function displaySearchTimeline(searchTimeline) {
        const container = document.getElementById('search-timeline');
        if (!container) return;
        if (!searchTimeline || searchTimeline.length === 0) {
            // bearer:disable javascript_lang_dangerous_insert_html
            container.innerHTML = '<p style="text-align: center; color: var(--text-secondary);">No search timeline data available</p>';
            return;
        }
        const html = searchTimeline.map(search => {
            const parsedDate = search.timestamp ? new Date(search.timestamp) : null;
            const timestamp = parsedDate && !isNaN(parsedDate) ? parsedDate.toLocaleTimeString() : '\u2014';
            const statusClass = search.success_status === 'success' ? 'ldr-search-status-success' : 'ldr-search-status-error';
            const statusText = search.success_status === 'success' ? 'Success' : 'Failed';
            return `
                <div class="ldr-search-timeline-item">
                    <div style="flex: 1;">
                        <div class="ldr-search-timeline-query">${escapeHtml(search.engine || 'Unknown')}</div>
                        <div class="ldr-search-timeline-meta">${timestamp} \u00b7 ${escapeHtml(search.query || 'N/A')}</div>
                    </div>
                    <div class="ldr-search-timeline-results">
                        ${formatNumber(search.results_count || 0)} results \u00b7 ${((search.response_time_ms ?? 0) / 1000).toFixed(2)}s
                    </div>
                    <span class="${statusClass}" style="margin-left: 0.75rem; font-weight: 600;">${statusText}</span>
                </div>
            `;
        }).join('');
        // bearer:disable javascript_lang_dangerous_insert_html
        // eslint-disable-next-line no-unsanitized/property -- audited: all values escaped via escapeHtml, timestamps from toLocaleTimeString, status from hardcoded literals
        container.innerHTML = html;
    }

    // Display call stack traces for LLM calls
    function displayCallStackTraces(timeline) {
        const container = document.getElementById('call-stack-traces');
        if (!container) return;

        const itemsWithCallStack = (timeline || []).filter(item => item.call_stack || item.calling_function);

        // Show the card if we have data
        const card = document.getElementById('call-stack-card');
        if (card && itemsWithCallStack.length > 0) {
            card.style.display = 'block';
        }

        if (itemsWithCallStack.length === 0) {
            // bearer:disable javascript_lang_dangerous_insert_html
            container.innerHTML = '<p style="text-align: center; color: var(--text-secondary);">No call stack traces available for this research</p>';
            return;
        }

        container.innerHTML = '';
        itemsWithCallStack.forEach(item => {
            const trace = document.createElement('div');
            trace.style.cssText = 'margin-bottom: 1rem; padding: 0.75rem; background: var(--card-bg); border-radius: 6px; border: 1px solid var(--border-color);';

            const parsedDate = item.timestamp ? new Date(item.timestamp) : null;
            const timestamp = parsedDate && !isNaN(parsedDate) ? parsedDate.toLocaleTimeString() : '\u2014';

            // bearer:disable javascript_lang_dangerous_insert_html
            // eslint-disable-next-line no-unsanitized/property -- audited: all values escaped via escapeHtml, timestamp from toLocaleTimeString, numerics coerced
            trace.innerHTML = `
                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
                    <div style="font-weight: 500; color: var(--text-primary);">
                        ${escapeHtml(item.calling_function || 'Unknown Function')}
                    </div>
                    <div style="font-size: 0.875rem; color: var(--text-secondary);">
                        ${timestamp} \u2014 ${item.prompt_tokens || 0} in + ${item.completion_tokens || 0} out = ${item.tokens || 0} tokens, ${item.response_time_ms || 0}ms
                    </div>
                </div>
                <div style="font-family: 'Courier New', monospace; font-size: 0.75rem; background: var(--bg-tertiary); padding: 0.5rem; border-radius: 4px; color: var(--text-secondary); overflow-x: auto; margin-bottom: 0.5rem;">
                    ${escapeHtml(item.call_stack || 'No stack trace available')}
                </div>
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 0.5rem; font-size: 0.875rem;">
                    <div><strong>File:</strong> ${escapeHtml((item.calling_file || 'Unknown').split('/').pop())}</div>
                    <div><strong>Phase:</strong> ${escapeHtml(item.research_phase || 'N/A')}</div>
                    <div><strong>Model:</strong> ${escapeHtml(item.model_name || 'N/A')}</div>
                    <div><strong>Status:</strong>
                        <span style="color: ${item.success_status === 'success' ? 'var(--success-color)' : 'var(--error-color)'}">
                            ${escapeHtml(item.success_status || 'Unknown')}
                        </span>
                    </div>
                </div>
            `;
            container.appendChild(trace);
        });
    }

    // Create timeline chart with toggle support (bars vs cumulative line)
    function createTimelineChart(timelineData, viewType) {
        if (!timelineData || !timelineData.timeline) return;
        if (typeof Chart === 'undefined') return;

        const chartElement = document.getElementById('timeline-chart');
        if (!chartElement) return;

        lastTimelineData = timelineData;
        const view = viewType || currentChartView;

        try {
            const ctx = chartElement.getContext('2d');
            if (timelineChart) {
                timelineChart.destroy();
            }

            const timeline = timelineData.timeline;
            const colors = getChartColors();

            // Shared tooltip callbacks
            function tooltipTitle(tooltipItems) {
                const item = timeline[tooltipItems[0].dataIndex];
                return `${item.research_phase || 'Unknown Phase'} - ${item.model_name || 'Unknown Model'}`;
            }
            function tooltipAfterBody(tooltipItems) {
                const item = timeline[tooltipItems[0].dataIndex];
                const lines = [];
                if (item.search_engine_selected) lines.push(`Engine: ${item.search_engine_selected}`);
                if (item.success_status) lines.push(`Status: ${item.success_status}`);
                if (item.response_time_ms > 0) lines.push(`Response time: ${(item.response_time_ms / 1000).toFixed(1)}s`);
                if (item.timestamp) {
                    const d = new Date(item.timestamp);
                    if (!isNaN(d)) lines.push(`Time: ${d.toLocaleTimeString()}`);
                }
                return lines;
            }

            if (view === 'line') {
                // Cumulative line chart with dual axes
                const timeLabels = timeline.map(item => {
                    const d = item.timestamp ? new Date(item.timestamp) : null;
                    return d && !isNaN(d) ? d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit' }) : '';
                });

                timelineChart = new Chart(ctx, {
                    type: 'line',
                    data: {
                        labels: timeLabels,
                        datasets: [{
                            label: 'Cumulative Total Tokens',
                            data: timeline.map(item => item.cumulative_tokens),
                            borderColor: colors.success,
                            backgroundColor: `rgba(${colors.successRgb}, 0.1)`,
                            borderWidth: 3, fill: true, tension: 0.1,
                            pointRadius: 3, pointHoverRadius: 5,
                        }, {
                            label: 'Cumulative Input Tokens',
                            data: timeline.map(item => item.cumulative_prompt_tokens),
                            borderColor: colors.tertiary,
                            backgroundColor: `rgba(${colors.tertiaryRgb}, 0.05)`,
                            borderWidth: 2, fill: true, tension: 0.1,
                            pointRadius: 2, pointHoverRadius: 4,
                        }, {
                            label: 'Cumulative Output Tokens',
                            data: timeline.map(item => item.cumulative_completion_tokens),
                            borderColor: colors.error,
                            backgroundColor: `rgba(${colors.errorRgb}, 0.05)`,
                            borderWidth: 2, fill: true, tension: 0.1,
                            pointRadius: 2, pointHoverRadius: 4,
                        }, {
                            label: 'Input Tokens per Call',
                            data: timeline.map(item => item.prompt_tokens),
                            borderColor: `rgba(${colors.tertiaryRgb}, 0.8)`,
                            borderWidth: 1, fill: false, tension: 0.1,
                            pointRadius: 2, pointHoverRadius: 4,
                            yAxisID: 'y1', borderDash: [5, 5],
                        }, {
                            label: 'Output Tokens per Call',
                            data: timeline.map(item => item.completion_tokens),
                            borderColor: `rgba(${colors.errorRgb}, 0.8)`,
                            borderWidth: 1, fill: false, tension: 0.1,
                            pointRadius: 2, pointHoverRadius: 4,
                            yAxisID: 'y1', borderDash: [5, 5],
                        }]
                    },
                    options: {
                        responsive: true, maintainAspectRatio: false,
                        interaction: { intersect: false, mode: 'index' },
                        scales: {
                            x: { title: { display: true, text: 'Time' } },
                            y: { type: 'linear', position: 'left', beginAtZero: true,
                                title: { display: true, text: 'Cumulative Tokens' } },
                            y1: { type: 'linear', position: 'right', beginAtZero: true,
                                title: { display: true, text: 'Tokens per Call' },
                                grid: { drawOnChartArea: false } }
                        },
                        plugins: {
                            legend: { position: 'top' },
                            tooltip: {
                                callbacks: {
                                    title: tooltipTitle,
                                    label(context) {
                                        return (context.dataset.label || '') + ': ' + formatNumber(context.parsed.y);
                                    },
                                    afterBody: tooltipAfterBody
                                }
                            }
                        }
                    }
                });
            } else {
                // Stacked bar chart (default)
                const chartData = timeline.map((item, index) => ({
                    phase: item.research_phase || item.phase || `Step ${index + 1}`,
                    tokens: item.tokens || 0,
                    promptTokens: item.prompt_tokens || 0,
                    completionTokens: item.completion_tokens || 0,
                }));

                timelineChart = new Chart(ctx, {
                    type: 'bar',
                    data: {
                        labels: chartData.map(item => item.phase),
                        datasets: [{
                            label: 'Input Tokens',
                            data: chartData.map(item => item.promptTokens),
                            backgroundColor: `rgba(${colors.accentRgb}, 0.8)`,
                            borderColor: colors.accent,
                            borderWidth: 1, borderRadius: 4, borderSkipped: false,
                        }, {
                            label: 'Output Tokens',
                            data: chartData.map(item => item.completionTokens),
                            backgroundColor: `rgba(${colors.successRgb}, 0.8)`,
                            borderColor: colors.success,
                            borderWidth: 1, borderRadius: 4, borderSkipped: false,
                        }]
                    },
                    options: {
                        responsive: true, maintainAspectRatio: false,
                        animation: { duration: 1000, easing: 'easeInOutQuart' },
                        interaction: { intersect: false, mode: 'index' },
                        scales: {
                            x: { stacked: true, grid: { display: false },
                                ticks: { font: { size: 11 }, maxRotation: 45 } },
                            y: { stacked: true, beginAtZero: true,
                                ticks: { callback(v) { return formatNumber(v); } },
                                title: { display: true, text: 'Tokens' } }
                        },
                        plugins: {
                            legend: { display: true, position: 'top', align: 'end',
                                labels: { usePointStyle: true, pointStyle: 'rect', font: { size: 11 }, padding: 15 } },
                            tooltip: {
                                backgroundColor: 'rgba(0, 0, 0, 0.8)',
                                titleColor: 'white', bodyColor: 'white',
                                cornerRadius: 8, displayColors: true,
                                callbacks: {
                                    title: tooltipTitle,
                                    beforeBody(tooltipItems) {
                                        const item = timeline[tooltipItems[0].dataIndex];
                                        return [`Total: ${formatNumber(item.tokens || 0)} tokens`];
                                    },
                                    afterBody: tooltipAfterBody
                                }
                            }
                        }
                    }
                });
            }

            SafeLogger.log('Timeline chart created successfully (view: ' + view + ')');
        } catch (error) {
            SafeLogger.error('Error creating timeline chart:', error);
        }
    }

    // Create search chart
    function createSearchChart(searchData) {
        if (!searchData || !searchData.search_calls) {
            SafeLogger.log('No search data for chart');
            return;
        }

        const chartElement = document.getElementById('search-chart');
        if (!chartElement) {
            SafeLogger.error('Search chart element not found');
            return;
        }

        const ctx = chartElement.getContext('2d');

        // Destroy existing chart
        if (searchChart) {
            searchChart.destroy();
        }

        // Prepare enhanced search data
        const searchCalls = searchData.search_calls.map((call, index) => ({
            label: call.query ? call.query.substring(0, 20) + '...' : `Search ${index + 1}`,
            results: call.results_count || 0,
            engine: call.engine || 'Unknown',
            responseTime: call.response_time_ms || 0,
            timestamp: call.timestamp
        }));

        const labels = searchCalls.map(call => call.label);
        const results = searchCalls.map(call => call.results);

        searchChart = new Chart(ctx, {
            type: 'line',
            data: {
                labels,
                datasets: [{
                    label: 'Results Found',
                    data: results,
                    borderColor: 'rgba(168, 85, 247, 1)',
                    backgroundColor: 'rgba(168, 85, 247, 0.1)',
                    borderWidth: 2.5,
                    fill: true,
                    tension: 0.3,
                    pointBackgroundColor: 'rgba(168, 85, 247, 1)',
                    pointBorderColor: 'rgba(255, 255, 255, 1)',
                    pointBorderWidth: 2,
                    pointRadius: 4,
                    pointHoverRadius: 6
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                animation: {
                    duration: 800,
                    easing: 'easeInOutQuart'
                },
                interaction: {
                    intersect: false,
                    mode: 'index'
                },
                scales: {
                    x: {
                        grid: {
                            display: false
                        },
                        ticks: {
                            font: {
                                size: 10
                            },
                            maxRotation: 45
                        }
                    },
                    y: {
                        beginAtZero: true,
                        grid: {
                            color: 'rgba(0, 0, 0, 0.1)',
                            drawBorder: false
                        },
                        ticks: {
                            font: {
                                size: 10
                            },
                            callback(value) {
                                return formatNumber(value);
                            }
                        },
                        title: {
                            display: true,
                            text: 'Results',
                            font: {
                                size: 11,
                                weight: 'bold'
                            }
                        }
                    }
                },
                plugins: {
                    legend: {
                        display: false
                    },
                    tooltip: {
                        backgroundColor: 'rgba(0, 0, 0, 0.8)',
                        titleColor: 'white',
                        bodyColor: 'white',
                        borderColor: 'rgba(255, 255, 255, 0.1)',
                        borderWidth: 1,
                        cornerRadius: 6,
                        callbacks: {
                            title(tooltipItems) {
                                const index = tooltipItems[0].dataIndex;
                                return searchCalls[index].label;
                            },
                            beforeBody(tooltipItems) {
                                const index = tooltipItems[0].dataIndex;
                                const call = searchCalls[index];
                                return [`Engine: ${call.engine}`];
                            },
                            afterBody(tooltipItems) {
                                const index = tooltipItems[0].dataIndex;
                                const call = searchCalls[index];
                                const lines = [];

                                if (call.responseTime > 0) {
                                    lines.push(`Response time: ${(call.responseTime / 1000).toFixed(1)}s`);
                                }

                                return lines;
                            }
                        }
                    }
                }
            }
        });
    }

    // Load cost data
    async function loadCostData() {
        // Temporarily disable cost calculation until pricing logic is optimized
        document.getElementById('total-cost').textContent = '-';

        /* TODO: re-enable when pricing logic is optimized
        try {
            const response = await fetch(URLBuilder.build(URLS.METRICS_API.RESEARCH_COSTS, researchId));
            if (response.ok) {
                const data = await response.json();
                if (data.status === 'success') {
                    document.getElementById('total-cost').textContent = formatCurrency(data.total_cost || 0);
                }
            }
        } catch (error) {
            SafeLogger.error('Error loading cost data:', error);
            document.getElementById('total-cost').textContent = '-';
        }
        */
    }

    // Show error message
    function showError() {
        document.getElementById('loading').style.display = 'none';
        document.getElementById('error').style.display = 'block';
        document.getElementById('details-content').style.display = 'none';
    }

    // Check if all required DOM elements exist
    function checkRequiredElements() {
        const requiredIds = [
            'loading', 'error', 'details-content', 'total-tokens', 'prompt-tokens',
            'completion-tokens', 'llm-calls', 'avg-response-time', 'model-used',
            'research-query', 'research-mode', 'research-date', 'research-strategy', 'total-cost',
            'phase-breakdown', 'search-engine-breakdown', 'timeline-chart', 'search-chart'
        ];

        const missing = [];
        requiredIds.forEach(id => {
            if (!document.getElementById(id)) {
                missing.push(id);
            }
        });

        if (missing.length > 0) {
            SafeLogger.error('Missing required DOM elements:', missing);
            return false;
        }
        return true;
    }

    // Load and display context overflow data
    async function loadContextOverflowData() {
        try {
            const response = await fetch(`/metrics/api/research/${researchId}/context-overflow`);
            if (!response.ok) {
                SafeLogger.error('Failed to load context overflow data');
                return;
            }

            const result = await response.json();
            if (result.status === 'success' && result.data) {
                displayContextOverflow(result.data);
                document.getElementById('context-overflow-section').style.display = 'block';
            }
        } catch (error) {
            SafeLogger.error('Error loading context overflow data:', error);
        }
    }

    // Display context overflow data
    function displayContextOverflow(data) {
        const { overview, phase_stats, requests, model, provider } = data;

        // Update overview cards
        document.getElementById('co-total-tokens').textContent = formatNumber(overview.total_tokens);
        document.getElementById('co-context-limit').textContent = overview.context_limit ? formatNumber(overview.context_limit) : 'N/A';
        document.getElementById('co-max-tokens').textContent = formatNumber(overview.max_tokens_used);

        // Update truncation status — uses shared helper from context-overflow-shared.js
        const truncationStatus = document.getElementById('co-truncation-status');
        const truncatedCount = overview.truncation_occurred ? overview.truncated_count : 0;
        // bearer:disable javascript_lang_dangerous_insert_html
        // eslint-disable-next-line no-unsanitized/property -- helper output is numeric-coerced
        truncationStatus.innerHTML = window.contextOverflowShared.renderTruncationBadge(truncatedCount);

        // Display phase breakdown
        displayPhaseBreakdown(phase_stats);

        // Display requests table
        displayRequestsTable(requests);

        // Create usage chart
        if (requests && requests.length > 0) {
            createUsageChart(requests, overview.context_limit);
        }

        // Show performance warning if truncation occurred
        if (overview.truncation_occurred) {
            const perfWarning = document.getElementById('co-performance-warning');
            if (perfWarning) {
                perfWarning.style.display = 'flex';
            }
        }
    }

    // Display phase breakdown
    function displayPhaseBreakdown(phaseStats) {
        const container = document.getElementById('co-phase-breakdown');
        if (!phaseStats || Object.keys(phaseStats).length === 0) {
            container.innerHTML = '<p style="text-align: center; padding: 2rem; color: var(--text-secondary);">No phase data available</p>';
            return;
        }


        let html = '<div style="overflow-x: auto; background: var(--card-bg); border-radius: 0.5rem; padding: 0.5rem;">';
        html += '<table class="ldr-data-table">';
        html += '<thead><tr>';
        html += '<th>Phase</th>';
        html += '<th style="text-align: right;">Requests</th>';
        html += '<th style="text-align: right;">Prompt Tokens</th>';
        html += '<th style="text-align: right;">Completion Tokens</th>';
        html += '<th style="text-align: right;">Total Tokens</th>';
        html += '<th style="text-align: center;">Truncated</th>';
        html += '</tr></thead>';
        html += '<tbody>';

        for (const [phase, stats] of Object.entries(phaseStats)) {
            const truncatedBadge = stats.truncated_count > 0
                ? `<span class="ldr-badge ldr-badge-danger">${Number(stats.truncated_count) || 0}</span>`
                : '<span class="ldr-badge ldr-badge-success">0</span>';

            html += `
                <tr>
                    <td>${escapeHtml(phase)}</td>
                    <td style="text-align: right;">${Number(stats.count) || 0}</td>
                    <td style="text-align: right;">${formatNumber(stats.prompt_tokens)}</td>
                    <td style="text-align: right;">${formatNumber(stats.completion_tokens)}</td>
                    <td style="text-align: right; font-weight: bold;">${formatNumber(stats.total_tokens)}</td>
                    <td style="text-align: center;">${truncatedBadge}</td>
                </tr>
            `;
        }

        html += '</tbody></table>';
        html += '</div>';
        // bearer:disable javascript_lang_dangerous_insert_html
        // eslint-disable-next-line no-unsanitized/property -- audited 2026-03-28: variable built from escaped/numeric values above
        container.innerHTML = html;
    }

    // Display requests table
    function displayRequestsTable(requests) {
        const tbody = document.getElementById('co-requests-table');
        if (!requests || requests.length === 0) {
            tbody.innerHTML = '<tr><td colspan="9" style="text-align: center; padding: 2rem; color: var(--text-secondary);">No request data available</td></tr>';
            return;
        }


        let html = '';
        requests.forEach(req => {
            const timestamp = new Date(req.timestamp).toLocaleTimeString();
            const truncatedBadge = req.context_truncated
                ? '<span class="ldr-badge ldr-badge-danger">Yes</span>'
                : '<span class="ldr-badge ldr-badge-success">No</span>';
            const responseTime = req.response_time_ms ? `${Number(req.response_time_ms) || 0}ms` : 'N/A';

            html += `
                <tr>
                    <td style="white-space: nowrap;">${timestamp}</td>
                    <td>${escapeHtml(req.phase || 'N/A')}</td>
                    <td style="font-size: 0.85rem; max-width: 200px; overflow: hidden; text-overflow: ellipsis;">${escapeHtml(req.calling_function || 'N/A')}</td>
                    <td style="text-align: right;">${formatNumber(req.prompt_tokens)}</td>
                    <td style="text-align: right;">${formatNumber(req.completion_tokens)}</td>
                    <td style="text-align: right; font-weight: bold;">${formatNumber(req.total_tokens)}</td>
                    <td style="text-align: right;">${req.context_limit ? formatNumber(req.context_limit) : 'N/A'}</td>
                    <td style="text-align: center;">${truncatedBadge}</td>
                    <td style="white-space: nowrap;">${responseTime}</td>
                </tr>
            `;
        });

        // bearer:disable javascript_lang_dangerous_insert_html
        // eslint-disable-next-line no-unsanitized/property -- audited 2026-03-28: variable built from escaped/numeric values above
        tbody.innerHTML = html;
    }

    // Create usage chart
    function createUsageChart(requests, contextLimit) {
        const canvas = document.getElementById('co-usage-chart');
        if (!canvas) return;

        const ctx = canvas.getContext('2d');

        // Prepare data
        const labels = requests.map((req, idx) => `Request ${idx + 1}`);
        const promptData = requests.map(req => req.prompt_tokens || 0);
        const completionData = requests.map(req => req.completion_tokens || 0);
        const totalData = requests.map(req => (req.prompt_tokens || 0) + (req.completion_tokens || 0));

        // Create chart
        new Chart(ctx, {
            type: 'line',
            data: {
                labels,
                datasets: [
                    {
                        label: 'Total Tokens',
                        data: totalData,
                        borderColor: 'rgb(168, 85, 247)',
                        backgroundColor: 'rgba(168, 85, 247, 0.1)',
                        borderWidth: 2,
                        tension: 0.1
                    },
                    {
                        label: 'Input Tokens',
                        data: promptData,
                        borderColor: 'rgb(59, 130, 246)',
                        backgroundColor: 'rgba(59, 130, 246, 0.1)',
                        tension: 0.1
                    },
                    {
                        label: 'Output Tokens',
                        data: completionData,
                        borderColor: 'rgb(34, 197, 94)',
                        backgroundColor: 'rgba(34, 197, 94, 0.1)',
                        tension: 0.1
                    }
                ]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {
                    legend: {
                        position: 'top',
                    },
                    title: {
                        display: false
                    },
                    annotation: contextLimit ? {
                        annotations: {
                            line1: {
                                type: 'line',
                                yMin: contextLimit,
                                yMax: contextLimit,
                                borderColor: 'rgb(255, 99, 132)',
                                borderWidth: 2,
                                borderDash: [5, 5],
                                label: {
                                    content: `Context Limit (${contextLimit})`,
                                    enabled: true,
                                    position: 'end'
                                }
                            }
                        }
                    } : undefined
                },
                scales: {
                    y: {
                        beginAtZero: true,
                        title: {
                            display: true,
                            text: 'Tokens'
                        }
                    }
                }
            }
        });
    }

    // Initialize when DOM is ready
    document.addEventListener('DOMContentLoaded', function() {
        SafeLogger.log('DOM loaded, initializing details page');

        researchId = getResearchIdFromUrl();
        SafeLogger.log('Research ID from URL:', researchId);

        if (!researchId) {
            SafeLogger.error('No research ID found in URL');
            showError();
            return;
        }

        // Check if all required elements exist
        if (!checkRequiredElements()) {
            SafeLogger.error('Required DOM elements missing');
            showError();
            return;
        }

        // Update page title
        document.title = `Research Details #${researchId} - Deep Research System`;

        // Load research metrics
        loadResearchMetrics();

        // Load link analytics for this research
        loadLinkAnalytics();

        // Load context overflow data
        loadContextOverflowData();

        // View Results button
        const viewResultsBtn = document.getElementById('view-results-btn');
        if (viewResultsBtn) {
            viewResultsBtn.addEventListener('click', () => {
                // URLBuilder produces /results/{id}
                // bearer:disable javascript_lang_open_redirect
                window.location.href = URLBuilder.resultsPage(researchId);
            });
        }

        // View Journals button — opens the journal-quality dashboard
        // scoped to this research session via ?research_id=...
        const viewJournalsBtn = document.getElementById('view-journals-btn');
        if (viewJournalsBtn) {
            viewJournalsBtn.addEventListener('click', () => {
                // URLBuilder produces a same-origin path
                // bearer:disable javascript_lang_open_redirect
                window.location.href = URLBuilder.journalQualityPage(researchId);
            });
        }

        // Back button
        const backBtn = document.getElementById('back-to-history');
        if (backBtn) {
            backBtn.addEventListener('click', () => {
                window.location.href = URLS.PAGES.HISTORY;
            });
        }

        // Chart view toggle (bars ↔ cumulative line)
        const barsBtn = document.getElementById('chart-view-bars');
        const lineBtn = document.getElementById('chart-view-line');
        if (barsBtn && lineBtn) {
            barsBtn.addEventListener('click', () => {
                currentChartView = 'bars';
                barsBtn.classList.add('active');
                lineBtn.classList.remove('active');
                if (lastTimelineData) createTimelineChart(lastTimelineData, 'bars');
            });
            lineBtn.addEventListener('click', () => {
                currentChartView = 'line';
                lineBtn.classList.add('active');
                barsBtn.classList.remove('active');
                if (lastTimelineData) createTimelineChart(lastTimelineData, 'line');
            });
        }
    });

})();
