// ────────────────────────────── static/script.js ──────────────────────────────
(function() {
  // DOM elements
  const fileDropZone = document.getElementById('file-drop-zone');
  const fileInput = document.getElementById('files');
  const fileList = document.getElementById('file-list');
  const fileItems = document.getElementById('file-items');
  const uploadBtn = document.getElementById('upload-btn');
  const uploadProgress = document.getElementById('upload-progress');
  const progressStatus = document.getElementById('progress-status');
  const progressFill = document.getElementById('progress-fill');
  const progressLog = document.getElementById('progress-log');
  const questionInput = document.getElementById('question');
  const sendBtn = document.getElementById('send-btn');
  const chatHint = document.getElementById('chat-hint');
  const messages = document.getElementById('messages');
  const reportLink = document.getElementById('report-link');
  const searchLink = document.getElementById('search-link');
  const loadingOverlay = document.getElementById('loading-overlay');
  const loadingMessage = document.getElementById('loading-message');
  
  // State
  let selectedFiles = [];
  let isUploading = false;
  let isProcessing = false;
  // Initialize
  init();
  function init() {
    setupFileDropZone();
    setupEventListeners();
    checkUserAuth();
    
    // Listen for project changes
    document.addEventListener('projectChanged', () => {
      updateUploadButton();
    });
    
    // Initial button state update
    updateUploadButton();
  }
  function setupFileDropZone() {
    // Click to browse
    fileDropZone.addEventListener('click', () => fileInput.click());
    
    // Drag and drop
    fileDropZone.addEventListener('dragover', (e) => {
      e.preventDefault();
      fileDropZone.classList.add('dragover');
    });
    
    fileDropZone.addEventListener('dragleave', () => {
      fileDropZone.classList.remove('dragover');
    });
    
    fileDropZone.addEventListener('drop', (e) => {
      e.preventDefault();
      fileDropZone.classList.remove('dragover');
      const files = Array.from(e.dataTransfer.files);
      handleFileSelection(files);
    });
    // File input change
    fileInput.addEventListener('change', (e) => {
      const files = Array.from(e.target.files);
      handleFileSelection(files);
    });
  }
  function setupEventListeners() {
    // Upload form
    document.getElementById('upload-form').addEventListener('submit', handleUpload);
    
    // Chat
    sendBtn.addEventListener('click', handleAsk);
    // Convert to textarea behavior: Enter submits, Shift+Enter for newline
    questionInput.addEventListener('keydown', (e) => {
      if (e.key === 'Enter' && !e.shiftKey) {
        e.preventDefault();
        handleAsk();
      }
    });
    questionInput.addEventListener('input', autoGrowTextarea);
    // Clear chat history
    const clearBtn = document.getElementById('clear-chat-btn');
    if (clearBtn) {
      clearBtn.addEventListener('click', async () => {
        const user = window.__sb_get_user();
        const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
        const currentSession = window.__sb_get_current_session && window.__sb_get_current_session();
        if (!user || !currentProject || !currentSession) {
          alert('Please select a session first');
          return;
        }
        if (!confirm('Clear chat history for this session?')) return;
        try {
          const res = await fetch(`/chat/history?user_id=${encodeURIComponent(user.user_id)}&project_id=${encodeURIComponent(currentProject.project_id)}&session_id=${encodeURIComponent(currentSession)}`, { method: 'DELETE' });
          if (res.ok) {
            document.getElementById('messages').innerHTML = '';
            // Also clear session-specific memory
            try {
              await fetch('/sessions/clear-memory', {
                method: 'POST',
                body: new FormData().append('user_id', user.user_id)
                  .append('project_id', currentProject.project_id)
                  .append('session_id', currentSession)
              });
            } catch (e) {
              console.warn('Failed to clear session memory:', e);
            }
          } else {
            alert('Failed to clear history');
          }
        } catch {}
      });
    }
    // Report link toggle
    if (reportLink) {
      reportLink.addEventListener('click', (e) => {
        e.preventDefault();
        toggleReportMode();
      });
    }
    // Search link toggle (enables web search augmentation)
    if (searchLink) {
      searchLink.addEventListener('click', (e) => {
        e.preventDefault();
        // Visual toggle; can be active concurrently with report mode
        searchLink.classList.toggle('active');
      });
    }
    
    // Quiz link toggle
    const quizLink = document.getElementById('quiz-link');
    if (quizLink) {
      quizLink.addEventListener('click', (e) => {
        e.preventDefault();
        // Open quiz setup modal
        if (window.__sb_open_quiz_setup) {
          window.__sb_open_quiz_setup();
        }
      });
    }
  }
  function handleFileSelection(files) {
    const validFiles = files.filter(file => {
      const isValid = file.type === 'application/pdf' || 
                     file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
      if (!isValid) {
        alert(`Unsupported file type: ${file.name}. Please upload PDF or DOCX files only.`);
      }
      return isValid;
    });
    if (validFiles.length === 0) return;
    selectedFiles = validFiles;
    updateFileList();
    updateUploadButton();
  }
  function updateFileList() {
    if (selectedFiles.length === 0) {
      fileList.style.display = 'none';
      return;
    }
    fileList.style.display = 'block';
    fileItems.innerHTML = '';
    selectedFiles.forEach((file, index) => {
      const fileItem = document.createElement('div');
      fileItem.className = 'file-item';
      
      const icon = document.createElement('span');
      icon.className = 'file-item-icon';
      icon.textContent = file.type.includes('pdf') ? '📄' : '📝';
      
      const name = document.createElement('span');
      name.className = 'file-item-name';
      name.textContent = file.name;
      
      const size = document.createElement('span');
      size.className = 'file-item-size';
      size.textContent = formatFileSize(file.size);
      
      const remove = document.createElement('button');
      remove.className = 'file-item-remove';
      remove.textContent = '×';
      remove.title = 'Remove file';
      remove.addEventListener('click', () => removeFile(index));
      
      fileItem.appendChild(icon);
      fileItem.appendChild(name);
      fileItem.appendChild(size);
      fileItem.appendChild(remove);
      fileItems.appendChild(fileItem);
    });
  }
  // Stored files view
  async function loadStoredFiles() {
    const user = window.__sb_get_user();
    const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
    if (!user || !currentProject) return;
    try {
      const res = await fetch(`/files?user_id=${encodeURIComponent(user.user_id)}&project_id=${encodeURIComponent(currentProject.project_id)}`);
      if (!res.ok) return;
      const data = await res.json();
      const files = data.files || [];
      renderStoredFiles(files);
      // Enable Report button when at least one file exists
      if (reportLink) {
        // Disable visually by muted color when no files
        reportLink.style.pointerEvents = (files.length === 0) ? 'none' : 'auto';
        reportLink.title = 'Generate report from selected document';
      }
      window.__sb_current_filenames = new Set((data.filenames || []).map(f => (f || '').toLowerCase()));
    } catch {}
  }
  function renderStoredFiles(files) {
    const container = document.getElementById('stored-file-items');
    if (container) {
      if (!files || files.length === 0) {
        container.innerHTML = '
No files stored yet.
';
      } else {
        container.innerHTML = files.map(f => `No files in this project.
';
      return;
    }
    list.innerHTML = files.map((f, idx) => `
      
        
        ${(f.summary || '').replace(/
        
      
    `).join('');
    // bind deletes
    list.querySelectorAll('.file-del').forEach(btn => {
      btn.addEventListener('click', async () => {
        const filename = decodeURIComponent(btn.getAttribute('data-fn'));
        if (!confirm(`Delete ${filename}? This will remove all related chunks.`)) return;
        const user = window.__sb_get_user();
        const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
        if (!user || !currentProject) return;
        try {
          const res = await fetch(`/files?user_id=${encodeURIComponent(user.user_id)}&project_id=${encodeURIComponent(currentProject.project_id)}&filename=${encodeURIComponent(filename)}`, { method: 'DELETE' });
          if (res.ok) {
            await loadStoredFiles();
          } else {
            alert('Failed to delete file');
          }
        } catch {}
      });
    });
    // bind see more/less
    list.querySelectorAll('.see-more-btn').forEach(btn => {
      btn.addEventListener('click', () => {
        const idx = btn.getAttribute('data-idx');
        const summary = document.getElementById(`file-summary-${idx}`);
        if (!summary) return;
        const expanded = summary.classList.toggle('expanded');
        btn.textContent = expanded ? 'See less' : '… See more';
      });
    });
  }
  // Expose show files section
  window.__sb_show_files_section = async () => {
    await loadStoredFiles();
  };
  // Duplicate detection: returns {toUpload, replace, renameMap}
  async function resolveDuplicates(files) {
    const existing = window.__sb_current_filenames || new Set();
    const toUpload = [];
    const replace = [];
    const renameMap = {};
    for (const f of files) {
      const name = f.name;
      if (existing.has(name.toLowerCase())) {
        const choice = await promptDuplicateChoice(name);
        if (choice === 'cancel') {
          // skip this file
        } else if (choice === 'replace') {
          replace.push(name);
          toUpload.push(f);
        } else if (choice && choice.startsWith('rename:')) {
          const newName = choice.slice(7);
          // create a new File with newName
          const renamed = new File([f], newName, { type: f.type, lastModified: f.lastModified });
          renameMap[name] = newName;
          toUpload.push(renamed);
        }
      } else {
        toUpload.push(f);
      }
    }
    return { toUpload, replace, renameMap };
  }
  function promptDuplicateChoice(filename) {
    return new Promise((resolve) => {
      // Minimal UX: use confirm/prompt; can be replaced with real modal later
      const msg = `A similar file named ${filename} already exists.\nChoose: [Cancel] to skip, [OK] to choose Replace or Rename.`;
      if (!confirm(msg)) { resolve('cancel'); return; }
      const answer = prompt('Type "replace" to overwrite, or enter a new filename to rename:', 'replace');
      if (!answer) { resolve('cancel'); return; }
      if (answer.trim().toLowerCase() === 'replace') { resolve('replace'); return; }
      resolve('rename:' + answer.trim());
    });
  }
  function removeFile(index) {
    selectedFiles.splice(index, 1);
    updateFileList();
    updateUploadButton();
  }
  function updateUploadButton() {
    const hasFiles = selectedFiles.length > 0;
    const hasProject = window.__sb_get_current_project && window.__sb_get_current_project();
    uploadBtn.disabled = !hasFiles || !hasProject || isUploading;
    
    if (hasFiles && hasProject) {
      uploadBtn.querySelector('.btn-text').textContent = `Upload ${selectedFiles.length} Document${selectedFiles.length > 1 ? 's' : ''}`;
    } else if (!hasProject) {
      uploadBtn.querySelector('.btn-text').textContent = 'Select a Project First';
    } else {
      uploadBtn.querySelector('.btn-text').textContent = 'Upload Documents';
    }
  }
  function formatFileSize(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
  async function handleUpload(e) {
    e.preventDefault();
    
    if (selectedFiles.length === 0) {
      alert('Please select files to upload');
      return;
    }
    const user = window.__sb_get_user();
    if (!user) {
      alert('Please sign in to upload files');
      window.__sb_show_auth_modal();
      return;
    }
    const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
    if (!currentProject) {
      alert('Please select a project first');
      return;
    }
    isUploading = true;
    updateUploadButton();
    showUploadProgress();
    try {
      // Check duplicates against server list first
      await loadStoredFiles();
      const { toUpload, replace, renameMap } = await resolveDuplicates(selectedFiles);
      if (toUpload.length === 0) {
        updateProgressStatus('No files to upload');
        setTimeout(() => hideUploadProgress(), 1000);
        return;
      }
      const formData = new FormData();
      formData.append('user_id', user.user_id);
      formData.append('project_id', currentProject.project_id);
      toUpload.forEach(file => formData.append('files', file));
      if (replace.length > 0) {
        formData.append('replace_filenames', JSON.stringify(replace));
      }
      if (Object.keys(renameMap).length > 0) {
        formData.append('rename_map', JSON.stringify(renameMap));
      }
      const response = await fetch('/upload', { method: 'POST', body: formData });
      const data = await response.json();
      if (response.ok) {
        updateProgressStatus('Upload successful! Processing documents...');
        updateProgressFill(0);
        // Friendly, non-technical messages only
        logProgress('Files uploaded successfully');
        
        // Poll backend for real progress
        startUploadStatusPolling(data.job_id, data.total_files || toUpload.length);
        // Refresh stored files list after a short delay
        setTimeout(loadStoredFiles, 2000);
      } else {
        throw new Error(data.detail || 'Upload failed');
      }
    } catch (error) {
      logProgress(`Error: ${error.message}`);
      updateProgressStatus('Upload failed');
      setTimeout(() => hideUploadProgress(), 3000);
    } finally {
      isUploading = false;
      updateUploadButton();
    }
  }
  function showUploadProgress() {
    uploadProgress.style.display = 'block';
    updateProgressStatus('Uploading files... (DO NOT REFRESH)');
    updateProgressFill(0);
    progressLog.innerHTML = '';
  }
  function hideUploadProgress() {
    uploadProgress.style.display = 'none';
    selectedFiles = [];
    updateFileList();
    updateUploadButton();
  }
  function updateProgressStatus(status) {
    progressStatus.textContent = status;
  }
  function updateProgressFill(percentage) {
    progressFill.style.width = `${percentage}%`;
  }
  function logProgress(message) {
    const timestamp = new Date().toLocaleTimeString();
    progressLog.innerHTML += `[${timestamp}] ${message}\n`;
    progressLog.scrollTop = progressLog.scrollHeight;
  }
  function startUploadStatusPolling(jobId, totalFiles) {
    let stopped = false;
    let failCount = 0;
    const maxFailsBeforeSilentStop = 30; // ~36s at 1200ms
    const interval = setInterval(async () => {
      if (stopped) return;
      try {
        const res = await fetch(`/upload/status?job_id=${encodeURIComponent(jobId)}`);
        if (!res.ok) { failCount++; return; }
        const status = await res.json();
        const percent = Math.max(0, Math.min(100, parseInt(status.percent || 0, 10)));
        const completed = status.completed || 0;
        const total = status.total || totalFiles || 1;
        updateProgressFill(percent);
        updateProgressStatus(percent >= 100 ? 'Finalizing...' : `Processing documents (${completed}/${total}) · ${percent}%`);
        if (status.status === 'completed' || percent >= 100) {
          clearInterval(interval);
          stopped = true;
          updateProgressFill(100);
          updateProgressStatus('Processing complete!');
          logProgress('You can now start chatting with your documents');
          setTimeout(() => hideUploadProgress(), 1500);
          enableChat();
          // Final refresh of stored files
          setTimeout(loadStoredFiles, 1000);
        }
      } catch (e) {
        // Swallow transient errors; update a friendly spinner-like status
        failCount++;
        if (failCount >= maxFailsBeforeSilentStop) {
          clearInterval(interval);
          stopped = true;
          updateProgressStatus('Still working...');
        }
      }
    }, 1200);
  }
  function enableChat() {
    questionInput.disabled = false;
    sendBtn.disabled = false;
    chatHint.style.display = 'none';
    autoGrowTextarea();
  }
  async function handleAsk() {
    const question = questionInput.value.trim();
    if (!question) return;
    const user = window.__sb_get_user();
    if (!user) {
      alert('Please sign in to chat');
      window.__sb_show_auth_modal();
      return;
    }
    const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
    if (!currentProject) {
      alert('Please select a project first');
      return;
    }
    // Get current session ID from session management
    const sessionId = window.__sb_get_current_session && window.__sb_get_current_session();
    if (!sessionId) {
      alert('Please select a session first');
      return;
    }
    // Add user message
    appendMessage('user', question);
    questionInput.value = '';
    autoGrowTextarea();
    // Save user message to chat history
    await saveChatMessage(user.user_id, currentProject.project_id, 'user', question, null, sessionId);
    
    // Add thinking message with dynamic status
    const thinkingMsg = appendMessage('thinking', 'Receiving request...');
    
    // Disable input during processing
    questionInput.disabled = true;
    sendBtn.disabled = true;
    showButtonLoading(sendBtn, true);
    
    // Start status polling
    const statusInterval = startStatusPolling(sessionId, thinkingMsg);
    try {
      // Branch: if report mode is active → call /report with textarea as instructions
      if (isReportModeActive()) {
        const filename = pickActiveFilename();
        if (!filename) throw new Error('Please select a document to generate a report');
        const form = new FormData();
        form.append('user_id', user.user_id);
        form.append('project_id', currentProject.project_id);
        form.append('filename', filename);
        form.append('outline_words', '200');
        form.append('report_words', '1200');
        form.append('instructions', question);
        form.append('session_id', sessionId);
        // If Search is toggled on, enable web augmentation for report
        const useWeb = searchLink && searchLink.classList.contains('active');
        if (useWeb) {
          form.append('use_web', '1');
          form.append('max_web', '20');
        }
        const response = await fetch('/report', { method: 'POST', body: form });
        const data = await response.json();
        if (response.ok) {
          thinkingMsg.remove();
          appendMessage('assistant', data.report_markdown || 'No report', true); // isReport = true
          if (data.sources && data.sources.length) appendSources(data.sources);
          // Save assistant report to chat history for persistence
          try { await saveChatMessage(user.user_id, currentProject.project_id, 'assistant', data.report_markdown || 'No report', null, sessionId); } catch {}
        } else {
          throw new Error(data.detail || 'Failed to generate report');
        }
      } else {
        const formData = new FormData();
        formData.append('user_id', user.user_id);
        formData.append('project_id', currentProject.project_id);
        formData.append('question', question);
        formData.append('k', '6');
        formData.append('session_id', sessionId);
        // If Search is toggled on, enable web augmentation
        const useWeb = searchLink && searchLink.classList.contains('active');
        if (useWeb) {
          formData.append('use_web', '1');
          formData.append('max_web', '30');
        }
        const response = await fetch('/chat', { method: 'POST', body: formData });
        const data = await response.json();
        if (response.ok) {
          thinkingMsg.remove();
          appendMessage('assistant', data.answer || 'No answer received');
          if (data.sources && data.sources.length > 0) {
            appendSources(data.sources);
          }
          
          // Handle session auto-naming if returned
          if (data.session_name && data.session_id) {
            console.log(`[FRONTEND] 🎯 Auto-naming received: session_id=${data.session_id}, name='${data.session_name}'`);
            // Update the session name in the UI immediately
            if (window.__sb_update_session_name) {
              console.log(`[FRONTEND] 🔄 Calling updateSessionName function...`);
              window.__sb_update_session_name(data.session_id, data.session_name);
            } else {
              console.warn(`[FRONTEND] ❌ updateSessionName function not available`);
            }
          } else {
            console.log(`[FRONTEND] ℹ️ No auto-naming data received:`, { session_name: data.session_name, session_id: data.session_id });
          }
          
          await saveChatMessage(
            user.user_id,
            currentProject.project_id,
            'assistant',
            data.answer || 'No answer received',
            (data.sources && data.sources.length > 0) ? data.sources : null,
            sessionId
          );
        } else {
          throw new Error(data.detail || 'Failed to get answer');
        }
      }
    } catch (error) {
      thinkingMsg.remove();
      const errorMsg = `⚠️ Error: ${error.message}`;
      appendMessage('assistant', errorMsg);
      await saveChatMessage(user.user_id, currentProject.project_id, 'assistant', errorMsg, null, sessionId);
    } finally {
      // Stop status polling
      if (statusInterval) {
        clearInterval(statusInterval);
      }
      // Re-enable input
      questionInput.disabled = false;
      sendBtn.disabled = false;
      showButtonLoading(sendBtn, false);
      questionInput.focus();
    }
  }
  function toggleReportMode() {
    if (!reportLink) return;
    reportLink.classList.toggle('active');
  }
  function isReportModeActive() {
    return reportLink && reportLink.classList.contains('active');
  }
  function pickActiveFilename() {
    const candidates = Array.from(document.querySelectorAll('#stored-file-items .pill'));
    let active = candidates.find(el => el.classList.contains('active'));
    if (!active && candidates.length) active = candidates[0];
    return active ? active.textContent.trim() : '';
  }
  function autoGrowTextarea() {
    if (!questionInput) return;
    // Reset height to measure content size
    questionInput.style.height = 'auto';
    const style = window.getComputedStyle(questionInput);
    const borderTop = parseInt(style.borderTopWidth) || 0;
    const borderBottom = parseInt(style.borderBottomWidth) || 0;
    const paddingTop = parseInt(style.paddingTop) || 0;
    const paddingBottom = parseInt(style.paddingBottom) || 0;
    const boxExtras = borderTop + borderBottom + paddingTop + paddingBottom;
    const contentHeight = questionInput.scrollHeight - boxExtras;
    const lineHeight = 22; // approx for 16px font
    const minRows = 2;
    const maxRows = 7;
    // Compute rows required based on content height, clamped
    const neededRows = Math.ceil(contentHeight / lineHeight);
    const clamped = Math.min(maxRows, Math.max(minRows, neededRows));
    questionInput.rows = clamped;
    // Prevent jumpy growth for long single lines by restricting until wrap actually occurs
    // If no wrap (scrollWidth <= clientWidth), keep at least minRows
    const isWrapping = questionInput.scrollWidth > questionInput.clientWidth;
    if (!isWrapping) questionInput.rows = Math.min(questionInput.rows, minRows);
  }
  async function handleGenerateReport() {
    const user = window.__sb_get_user();
    const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
    if (!user || !currentProject) {
      alert('Please sign in and select a project');
      return;
    }
    // Determine selected/active file from files section; fallback to first
    const candidates = Array.from(document.querySelectorAll('#stored-file-items .pill'));
    let active = candidates.find(el => el.classList.contains('active'));
    if (!active && candidates.length) active = candidates[0];
    if (!active) { alert('Please upload and select a document first'); return; }
    const filename = active.textContent.trim();
    const instructions = (questionInput && questionInput.value || '').trim();
    showLoading('Generating report...');
    try {
      const form = new FormData();
      form.append('user_id', user.user_id);
      form.append('project_id', currentProject.project_id);
      form.append('filename', filename);
      form.append('outline_words', '200');
      form.append('report_words', '1200');
      form.append('instructions', instructions);
      // Respect Search toggle when using quick report button
      const useWeb = searchLink && searchLink.classList.contains('active');
      if (useWeb) {
        form.append('use_web', '1');
        form.append('max_web', '20');
      }
      const res = await fetch('/report', { method: 'POST', body: form });
      const data = await res.json();
      if (!res.ok) throw new Error(data.detail || 'Report failed');
      // Append as assistant message
      appendMessage('assistant', data.report_markdown || 'No report');
      if (data.sources && data.sources.length) {
        appendSources(data.sources);
      }
    } catch (e) {
      alert(e.message || 'Failed to generate report');
    } finally {
      hideLoading();
    }
  }
  // Toggle active file pill selection
  document.addEventListener('click', (e) => {
    const tgt = e.target;
    if (tgt && tgt.classList && tgt.classList.contains('pill') && tgt.parentElement && tgt.parentElement.id === 'stored-file-items') {
      document.querySelectorAll('#stored-file-items .pill').forEach(el => el.classList.remove('active'));
      tgt.classList.add('active');
      // Enable link visually
      if (reportLink) reportLink.classList.add('active');
    }
  });
  async function saveChatMessage(userId, projectId, role, content, sources = null, sessionId = null) {
    try {
      const formData = new FormData();
      formData.append('user_id', userId);
      formData.append('project_id', projectId);
      formData.append('role', role);
      formData.append('content', content);
      formData.append('timestamp', Date.now() / 1000);
      if (sources) {
        try { formData.append('sources', JSON.stringify(sources)); } catch {}
      }
      if (sessionId) {
        formData.append('session_id', sessionId);
      }
      
      await fetch('/chat/save', { method: 'POST', body: formData });
    } catch (error) {
      console.error('Failed to save chat message:', error);
    }
  }
  
  function renderAssistantMarkdown(container, markdown, isReport) {
    try {
      // Configure marked to keep code blocks for highlight.js
      const htmlContent = marked.parse(markdown);
      container.innerHTML = htmlContent;
      // Normalize heading numbering (H1/H2/H3) without double-numbering
      try { renumberHeadings(container); } catch {}
      // Render Mermaid if present
      renderMermaidInElement(container);
      // Add copy buttons to code blocks
      addCopyButtonsToCodeBlocks(container);
      // Syntax highlight code blocks
      try {
        container.querySelectorAll('pre code').forEach((block) => {
          if (window.hljs && window.hljs.highlightElement) {
            window.hljs.highlightElement(block);
          }
        });
      } catch {}
      // Add download PDF button for reports
      if (isReport) addDownloadPdfButton(container, markdown);
    } catch (e) {
      container.textContent = markdown;
    }
  }
  function renumberHeadings(root) {
    const h1s = Array.from(root.querySelectorAll('h1'));
    const h2s = Array.from(root.querySelectorAll('h2'));
    const h3s = Array.from(root.querySelectorAll('h3'));
    let s1 = 0;
    let s2 = 0;
    let s3 = 0;
    const headers = Array.from(root.querySelectorAll('h1, h2, h3'));
    headers.forEach(h => {
      const text = h.textContent.trim();
      // Strip any existing numeric prefix like "1. ", "1.2 ", "1.2.3 "
      const stripped = text.replace(/^\d+(?:\.\d+){0,2}\s+/, '');
      if (h.tagName === 'H1') {
        s1 += 1; s2 = 0; s3 = 0;
        h.textContent = `${s1}. ${stripped}`;
      } else if (h.tagName === 'H2') {
        if (s1 === 0) { s1 = 1; }
        s2 += 1; s3 = 0;
        h.textContent = `${s1}.${s2} ${stripped}`;
      } else if (h.tagName === 'H3') {
        if (s1 === 0) { s1 = 1; }
        if (s2 === 0) { s2 = 1; }
        s3 += 1;
        h.textContent = `${s1}.${s2}.${s3} ${stripped}`;
      }
    });
  }
  // Dynamically load Mermaid and render mermaid code blocks
  async function ensureMermaidLoaded() {
    if (window.mermaid && window.mermaid.initialize) return true;
    return new Promise((resolve) => {
      const existing = document.querySelector('script[data-sb-mermaid]');
      if (existing) { existing.addEventListener('load', () => resolve(true)); return; }
      const s = document.createElement('script');
      s.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
      s.async = true;
      s.dataset.sbMermaid = '1';
      s.onload = () => {
        try {
          if (window.mermaid && window.mermaid.initialize) {
            window.mermaid.initialize({ startOnLoad: false, securityLevel: 'loose', theme: 'default' });
          }
        } catch {}
        resolve(true);
      };
      document.head.appendChild(s);
    });
  }
  async function renderMermaidInElement(el) {
    const mermaidBlocks = el.querySelectorAll('code.language-mermaid, pre code.language-mermaid');
    if (!mermaidBlocks.length) return;
    await ensureMermaidLoaded();
    const isV10 = !!(window.mermaid && window.mermaid.render && typeof window.mermaid.render === 'function');
    for (let idx = 0; idx < mermaidBlocks.length; idx++) {
      const codeBlock = mermaidBlocks[idx];
      let graph = codeBlock.textContent || '';
      const wrapper = document.createElement('div');
      const id = `mermaid-${Date.now()}-${idx}`;
      wrapper.className = 'mermaid';
      wrapper.id = id;
      const replaceTarget = codeBlock.parentElement && codeBlock.parentElement.tagName.toLowerCase() === 'pre' ? codeBlock.parentElement : codeBlock;
      replaceTarget.replaceWith(wrapper);
      
      // Try to render with retry logic
      let renderSuccess = false;
      let attempt = 0;
      const maxAttempts = 3;
      
      while (!renderSuccess && attempt < maxAttempts) {
        try {
          if (isV10) {
            // Pass wrapper as container to avoid document.createElementNS undefined errors
            const out = await window.mermaid.render(id + '-svg', graph, wrapper);
            if (out && out.svg) {
              wrapper.innerHTML = out.svg;
              if (out.bindFunctions) { out.bindFunctions(wrapper); }
              renderSuccess = true;
            }
          } else if (window.mermaid && window.mermaid.init) {
            // Legacy fallback
            wrapper.textContent = graph;
            window.mermaid.init(undefined, wrapper);
            renderSuccess = true;
          }
        } catch (e) {
          console.warn(`Mermaid render failed (attempt ${attempt + 1}):`, e);
          
          // If this isn't the last attempt, try to fix the mermaid code using AI
          if (attempt < maxAttempts - 1) {
            try {
              console.log('Attempting to fix Mermaid syntax using AI...');
              const fixedCode = await fixMermaidWithAI(graph, e.message || e.toString());
              if (fixedCode && fixedCode !== graph) {
                graph = fixedCode;
                console.log('AI provided fixed Mermaid code, retrying...');
              } else {
                console.warn('AI could not provide fixed Mermaid code');
                break;
              }
            } catch (aiError) {
              console.warn('AI Mermaid fix failed:', aiError);
              break;
            }
          }
        }
        attempt++;
      }
      
      // If all attempts failed, show the original code
      if (!renderSuccess) {
        console.warn('All Mermaid render attempts failed, showing original code');
        wrapper.textContent = graph;
      }
    }
  }
  async function fixMermaidWithAI(mermaidCode, errorMessage) {
    try {
      const formData = new FormData();
      formData.append('mermaid_code', mermaidCode);
      formData.append('error_message', errorMessage);
      
      const response = await fetch('/mermaid/fix', {
        method: 'POST',
        body: formData
      });
      
      if (response.ok) {
        const result = await response.json();
        if (result.success && result.was_fixed) {
          console.log('Mermaid syntax fixed by AI');
          return result.fixed_code;
        }
      }
    } catch (error) {
      console.warn('Failed to fix Mermaid with AI:', error);
    }
    return null;
  }
  // Expose markdown-aware appenders for use after refresh (projects.js)
  window.appendMessage = appendMessage;
  window.appendSources = appendSources;
  function addCopyButtonsToCodeBlocks(messageDiv) {
    const codeBlocks = messageDiv.querySelectorAll('pre code');
    codeBlocks.forEach((codeBlock, index) => {
      const pre = codeBlock.parentElement;
      if (!pre || pre.dataset.sbWrapped === '1') return;
      const isMermaid = codeBlock.classList.contains('language-mermaid');
      // Do not wrap mermaid blocks; they will be replaced by SVGs
      if (isMermaid) return;
      const language = codeBlock.className.match(/language-(\w+)/)?.[1] || 'code';
      // Ensure syntax highlighting is applied before moving the node
      try {
        if (window.hljs && window.hljs.highlightElement) {
          window.hljs.highlightElement(codeBlock);
        }
      } catch {}
      // Create wrapper
      const wrapper = document.createElement('div');
      wrapper.className = 'code-block-wrapper';
      // Create header with language and action buttons
      const header = document.createElement('div');
      header.className = 'code-block-header';
      
      // Check if code is long enough to warrant an expand button
      const codeText = codeBlock.textContent || '';
      const isLongCode = codeText.split('\n').length > 15 || codeText.length > 500;
      
      header.innerHTML = `
        
${language}
        
          ${isLongCode ? `
            
          ` : ''}
          
        
      `;
      // Create content wrapper and move the original 
 inside (preserves highlighting)
      const content = document.createElement('div');
      content.className = 'code-block-content';
      if (isLongCode) {
        content.classList.add('collapsed');
      }
      pre.dataset.sbWrapped = '1';
      content.appendChild(pre);
      // Assemble wrapper
      wrapper.appendChild(header);
      wrapper.appendChild(content);
      // Insert wrapper where the original pre was
      const parent = content.parentNode || messageDiv; // safety
      if (pre.parentNode !== content) {
        // pre has been moved into content; parent should be original parent of pre
        const insertionParent = parent === messageDiv ? messageDiv : pre.parentNode;
      }
      // Replace in DOM: pre has been moved; place wrapper where pre used to be
      const originalParent = content.parentNode ? content.parentNode : messageDiv;
      // If pre had a previous sibling, insert wrapper before it; else append
      const ref = wrapper.querySelector('.code-block-content pre');
      const oldParent = wrapper.querySelector('.code-block-content pre').parentNode;
      // oldParent is content; we need to place wrapper at the original location of pre
      const originalPlaceholder = document.createComment('code-block-wrapper');
      const preOriginalParent = wrapper.querySelector('.code-block-content pre').parentElement; // content
      // Since we already moved pre, we can't auto-place; use previousSibling stored before move
      // Simpler: insert wrapper after content creation at the position of 'content' parent
      // If messageDiv contains multiple elements, just append wrapper now
      messageDiv.appendChild(wrapper);
      // Add click handlers for copy and expand buttons
      const copyBtn = wrapper.querySelector('.copy-code-btn');
      const expandBtn = wrapper.querySelector('.expand-code-btn');
      
      if (copyBtn) {
        copyBtn.addEventListener('click', () => copyCodeToClipboard(codeBlock.textContent, copyBtn));
      }
      
      if (expandBtn) {
        expandBtn.addEventListener('click', () => toggleCodeExpansion(content, expandBtn));
      }
    });
  }
  function toggleCodeExpansion(content, button) {
    const isCollapsed = content.classList.contains('collapsed');
    
    if (isCollapsed) {
      content.classList.remove('collapsed');
      button.innerHTML = `
        
        Collapse
      `;
    } else {
      content.classList.add('collapsed');
      button.innerHTML = `
        
        Expand
      `;
    }
  }
  function copyCodeToClipboard(code, button) {
    navigator.clipboard.writeText(code).then(() => {
      const originalText = button.innerHTML;
      button.innerHTML = `
        
        Copied!
      `;
      button.classList.add('copied');
      
      setTimeout(() => {
        button.innerHTML = originalText;
        button.classList.remove('copied');
      }, 2000);
    }).catch(err => {
      console.error('Failed to copy code:', err);
      // Fallback for older browsers
      const textArea = document.createElement('textarea');
      textArea.value = code;
      document.body.appendChild(textArea);
      textArea.select();
      try {
        document.execCommand('copy');
        button.innerHTML = `
          
          Copied!
        `;
        button.classList.add('copied');
        setTimeout(() => {
          button.innerHTML = `
            
            Copy
          `;
          button.classList.remove('copied');
        }, 2000);
      } catch (fallbackErr) {
        console.error('Fallback copy failed:', fallbackErr);
      }
      document.body.removeChild(textArea);
    });
  }
  function addDownloadPdfButton(messageDiv, reportContent) {
    const downloadBtn = document.createElement('button');
    downloadBtn.className = 'download-pdf-btn';
    downloadBtn.innerHTML = `
      
      Download PDF
    `;
    
    downloadBtn.addEventListener('click', () => downloadReportAsPdf(reportContent, downloadBtn));
    messageDiv.appendChild(downloadBtn);
  }
  async function downloadReportAsPdf(reportContent, button) {
    const user = window.__sb_get_user();
    const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
    
    if (!user || !currentProject) {
      alert('Please sign in and select a project');
      return;
    }
    button.disabled = true;
    button.innerHTML = `
      
      Generating PDF...
    `;
    try {
      // Find sources from the current message or recent sources
      const sources = findCurrentSources();
      
      const formData = new FormData();
      formData.append('user_id', user.user_id);
      formData.append('project_id', currentProject.project_id);
      formData.append('report_content', reportContent);
      formData.append('sources', JSON.stringify(sources));
      const response = await fetch('/report/pdf', {
        method: 'POST',
        body: formData
      });
      if (response.ok) {
        const blob = await response.blob();
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `report-${new Date().toISOString().split('T')[0]}.pdf`;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
        
        button.innerHTML = `
          
          Downloaded!
        `;
        setTimeout(() => {
          button.innerHTML = `
            
            Download PDF
          `;
          button.disabled = false;
        }, 2000);
      } else {
        throw new Error('Failed to generate PDF');
      }
    } catch (error) {
      console.error('PDF generation failed:', error);
      alert('Failed to generate PDF. Please try again.');
      button.innerHTML = `
        
        Download PDF
      `;
      button.disabled = false;
    }
  }
  
  function appendSources(sources) {
    const sourcesDiv = document.createElement('div');
    sourcesDiv.className = 'sources';
    
    const sourcesList = sources.map(source => {
      const filename = source.filename || 'unknown';
      const topic = source.topic_name ? ` • ${source.topic_name}` : '';
      const pages = source.page_span ? ` [pp. ${source.page_span.join('-')}]` : '';
      const score = source.score ? ` (${source.score.toFixed(2)})` : '';
      
      return `${filename}${topic}${pages}${score}`;
    }).join(' ');
    
    sourcesDiv.innerHTML = `Sources: ${sourcesList}`;
    messages.appendChild(sourcesDiv);
    
    // Store sources for PDF generation
    window.__sb_current_sources = sources;
    
    requestAnimationFrame(() => {
      sourcesDiv.scrollIntoView({ behavior: 'smooth', block: 'end' });
    });
  }
  function findCurrentSources() {
    // Try to get sources from the current message context
    if (window.__sb_current_sources) {
      return window.__sb_current_sources;
    }
    
    // Fallback: look for sources in the last assistant message
    const lastAssistantMsg = Array.from(messages.children).reverse().find(msg => 
      msg.classList.contains('msg') && msg.classList.contains('assistant')
    );
    
    if (lastAssistantMsg) {
      const sourcesDiv = lastAssistantMsg.nextElementSibling;
      if (sourcesDiv && sourcesDiv.classList.contains('sources')) {
        // Extract sources from the DOM (this is a fallback)
        const pills = sourcesDiv.querySelectorAll('.pill');
        const sources = Array.from(pills).map(pill => {
          const text = pill.textContent;
          const parts = text.split(' • ');
          return {
            filename: parts[0] || 'Unknown',
            topic_name: parts[1] || '',
            score: 0.0
          };
        });
        return sources;
      }
    }
    
    return [];
  }
  function showButtonLoading(button, isLoading) {
    const textSpan = button.querySelector('.btn-text');
    const loadingSpan = button.querySelector('.btn-loading');
    
    // Handle buttons with only loading state (like send button)
    if (!textSpan && loadingSpan) {
      if (isLoading) {
        loadingSpan.style.display = 'inline-flex';
        button.disabled = true;
      } else {
        loadingSpan.style.display = 'none';
        button.disabled = false;
      }
      return;
    }
    
    // Handle buttons with both text and loading states (like upload button)
    if (textSpan && loadingSpan) {
      if (isLoading) {
        textSpan.style.display = 'none';
        loadingSpan.style.display = 'inline-flex';
        button.disabled = true;
      } else {
        textSpan.style.display = 'inline';
        loadingSpan.style.display = 'none';
        button.disabled = false;
      }
      return;
    }
    
    // Fallback for buttons without proper loading structure
    if (isLoading) {
      button.disabled = true;
    } else {
      button.disabled = false;
    }
  }
  function showLoading(message = 'Processing...') {
    loadingMessage.textContent = message;
    loadingOverlay.classList.remove('hidden');
  }
  function hideLoading() {
    loadingOverlay.classList.add('hidden');
  }
  function checkUserAuth() {
    const user = window.__sb_get_user();
    if (user) {
      // Check if we have a current project
      const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
      if (currentProject) {
        enableChat();
      }
    }
    // Always update upload button state
    updateUploadButton();
  }
  // Public API
  window.__sb_update_upload_button = updateUploadButton;
  window.__sb_enable_chat = enableChat;
  // Listen for project changes
  window.addEventListener('projectChanged', () => {
    updateUploadButton();
    const currentProject = window.__sb_get_current_project && window.__sb_get_current_project();
    if (currentProject) {
      enableChat();
    }
  });
  // Load stored files whenever project changes
  document.addEventListener('projectChanged', () => {
    loadStoredFiles();
  });
  // Expose to other scripts
  window.__sb_load_stored_files = loadStoredFiles;
  // Also attempt loading stored files after initial auth/project load
  window.addEventListener('load', () => {
    setTimeout(loadStoredFiles, 500);
  });
  // Reveal animations
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        entry.target.classList.add('in');
        observer.unobserve(entry.target);
      }
    });
  }, { threshold: 0.1 });
  document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
  // Status polling function for real-time updates
  function startStatusPolling(sessionId, thinkingMsg) {
    const isReportMode = isReportModeActive();
    const statusEndpoint = isReportMode ? `/report/status/${sessionId}` : `/chat/status/${sessionId}`;
    
    const interval = setInterval(async () => {
      try {
        const response = await fetch(statusEndpoint);
        if (response.ok) {
          const status = await response.json();
          updateThinkingMessage(thinkingMsg, status.message, status.progress);
          
          // Stop polling when complete or error
          if (status.status === 'complete' || status.status === 'error') {
            clearInterval(interval);
          }
        }
      } catch (error) {
        console.warn('Status polling failed:', error);
      }
    }, 500); // Poll every 500ms
    
    return interval;
  }
  function updateThinkingMessage(thinkingMsg, message, progress) {
    if (thinkingMsg && thinkingMsg.querySelector) {
      const progressBar = thinkingMsg.querySelector('.progress-bar');
      const statusText = thinkingMsg.querySelector('.status-text');
      
      if (statusText) {
        statusText.textContent = message;
      }
      
      if (progressBar && progress !== undefined) {
        progressBar.style.width = `${progress}%`;
      }
    }
  }
  // Enhanced thinking message with progress bar
  function appendMessage(role, text, isReport = false) {
    const messageDiv = document.createElement('div');
    messageDiv.className = `msg ${role}`;
    if (role === 'thinking') {
      messageDiv.innerHTML = `
        
      `;
    } else if (role === 'assistant') {
      renderAssistantMarkdown(messageDiv, text, isReport);
    } else {
      messageDiv.textContent = text;
    }
    messages.appendChild(messageDiv);
    requestAnimationFrame(() => {
      messageDiv.scrollIntoView({ behavior: 'smooth', block: 'end' });
    });
    return messageDiv;
  }
})();