import React, { useState, useEffect, useMemo, useContext, useRef, useCallback } from 'react'
import { ObsidianImporter, ObsidianObsNote, ObsidianVault, FolderNode, ObsidianVaultRecord, ObsidianVaultRecordLight, ObsidianObsNoteMeta, FolderNodeMeta } from '@/lib/obsidianImporter'
import { AuthContext } from '@/context/AuthContext'
import { useEditor } from '@tldraw/tldraw'
import { useAutomergeHandle } from '@/context/AutomergeHandleContext'
import { saveVaultNoteContents, getVaultNoteContents, getNoteContent, deleteVaultNoteContents } from '@/lib/noteContentStore'

interface ObsidianVaultBrowserProps {
  onObsNoteSelect: (obs_note: ObsidianObsNote) => void
  onObsNotesSelect: (obs_notes: ObsidianObsNote[]) => void
  onClose: () => void
  className?: string
  autoOpenFolderPicker?: boolean
  showVaultBrowser?: boolean
  shapeMode?: boolean // When true, renders without modal overlay for use in shape
}

export const ObsidianVaultBrowser: React.FC<ObsidianVaultBrowserProps> = ({
  onObsNoteSelect,
  onObsNotesSelect,
  onClose,
  className = '',
  autoOpenFolderPicker = false,
  showVaultBrowser = true,
  shapeMode = false
}) => {
  // Safely get auth context - use useContext directly to avoid throwing error
  // This allows the component to work even when used outside AuthProvider (e.g., during SVG export)
  const authContext = useContext(AuthContext)
  const fallbackSession = {
    username: '',
    authed: false,
    loading: false,
    backupCreated: null,
    obsidianVaultPath: undefined,
    obsidianVaultName: undefined
  }
  const session = authContext?.session || fallbackSession
  const updateSession = authContext?.updateSession || (() => {})
  const [importer] = useState(() => new ObsidianImporter())
  const [vault, setVault] = useState<ObsidianVault | null>(null)
  const [searchQuery, setSearchQuery] = useState('')
  const [debouncedSearchQuery, setDebouncedSearchQuery] = useState('')
  const [isLoading, setIsLoading] = useState(() => {
    // Check if we have a vault configured and start loading immediately
    return !!(session.obsidianVaultPath && session.obsidianVaultPath !== 'folder-selected') ||
           !!(session.obsidianVaultPath === 'folder-selected' && session.obsidianVaultName)
  })
  const [error, setError] = useState<string | null>(null)
  const [selectedNotes, setSelectedNotes] = useState<Set<string>>(new Set())
  const [showVaultInput, setShowVaultInput] = useState(false)
  const [vaultPath, setVaultPath] = useState('')
  const [inputMethod, setInputMethod] = useState<'folder' | 'url' | 'quartz'>('folder')
  const [showFolderReselect, setShowFolderReselect] = useState(false)
  const [isLoadingVault, setIsLoadingVault] = useState(false)
  const [hasLoadedOnce, setHasLoadedOnce] = useState(false)
  const [folderTree, setFolderTree] = useState<FolderNode | null>(null)
  const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set())
  const [selectedFolder, setSelectedFolder] = useState<string | null>(null)
  const [viewMode, setViewMode] = useState<'grid' | 'list' | 'tree'>('tree')
  
  // Track previous vault path/name to prevent unnecessary reloads
  const previousVaultPathRef = useRef<string | undefined>(session.obsidianVaultPath)
  const previousVaultNameRef = useRef<string | undefined>(session.obsidianVaultName)
  
  const editor = useEditor()
  const automergeHandle = useAutomergeHandle()

  // Initialize debounced search query to match search query
  useEffect(() => {
    setDebouncedSearchQuery(searchQuery)
  }, [])

  // Update folder tree when vault changes
  useEffect(() => {
    if (vault && vault.folderTree) {
      setFolderTree(vault.folderTree)
      // Expand root folder by default
      setExpandedFolders(new Set(['']))
    }
  }, [vault])

  // Save vault to Automerge store (metadata only) and IndexedDB (content)
  const saveVaultToAutomerge = async (vault: ObsidianVault) => {
    // First, save full note content to IndexedDB (local-only, never synced)
    try {
      const noteContents = importer.extractNoteContents(vault)
      await saveVaultNoteContents(vault.name, noteContents)
      console.log(`Saved ${noteContents.length} note contents to IndexedDB for vault: ${vault.name}`)
    } catch (idbError) {
      console.error('Error saving note contents to IndexedDB:', idbError)
    }

    // Create light record (no content) for Automerge sync
    const lightRecord = importer.vaultToLightRecord(vault)

    if (!automergeHandle) {
      // No Automerge, just save light metadata to localStorage
      try {
        localStorage.setItem(`obsidian_vault_cache:${vault.name}`, JSON.stringify({
          ...lightRecord,
          lastImported: lightRecord.lastImported instanceof Date ? lightRecord.lastImported.toISOString() : lightRecord.lastImported
        }))
      } catch (localStorageError) {
        console.warn('Could not save vault metadata to localStorage:', localStorageError)
      }
      return
    }

    try {
      // Save LIGHT record (no content) to Automerge - this is safe and won't overflow
      automergeHandle.change((doc: any) => {
        if (!doc.store) {
          doc.store = {}
        }

        const recordToSave = {
          ...lightRecord,
          lastImported: lightRecord.lastImported instanceof Date
            ? lightRecord.lastImported.toISOString()
            : lightRecord.lastImported
        }

        doc.store[lightRecord.id] = recordToSave
      })

      console.log(`Saved light vault record to Automerge: ${lightRecord.id} (${lightRecord.totalObsNotes} notes, content stored locally)`)

      // Also save light metadata to localStorage as backup
      try {
        localStorage.setItem(`obsidian_vault_cache:${vault.name}`, JSON.stringify({
          ...lightRecord,
          lastImported: lightRecord.lastImported instanceof Date ? lightRecord.lastImported.toISOString() : lightRecord.lastImported
        }))
      } catch (localStorageError) {
        // Silent fail for backup
      }
    } catch (error) {
      console.error('Error saving vault to Automerge:', error)
      // Try localStorage as fallback
      try {
        localStorage.setItem(`obsidian_vault_cache:${vault.name}`, JSON.stringify({
          ...lightRecord,
          lastImported: lightRecord.lastImported instanceof Date ? lightRecord.lastImported.toISOString() : lightRecord.lastImported
        }))
      } catch (localStorageError) {
        console.warn('Could not save vault to localStorage:', localStorageError)
      }
    }
  }

  // Clear vault data from Automerge (for privacy - removes any synced vault data)
  const clearVaultFromAutomerge = useCallback((vaultName: string) => {
    const vaultId = `obsidian_vault:${vaultName}`

    if (automergeHandle) {
      try {
        automergeHandle.change((doc: any) => {
          if (doc.store && doc.store[vaultId]) {
            delete doc.store[vaultId]
            console.log(`Cleared vault from Automerge: ${vaultId}`)
          }
        })
      } catch (error) {
        console.error('Error clearing vault from Automerge:', error)
      }
    }

    // Also clear from localStorage
    try {
      localStorage.removeItem(`obsidian_vault_cache:${vaultName}`)
      console.log(`Cleared vault from localStorage: ${vaultName}`)
    } catch (e) {
      // Silent fail
    }

    // Clear from IndexedDB
    deleteVaultNoteContents(vaultName).then(() => {
      console.log(`Cleared vault content from IndexedDB: ${vaultName}`)
    }).catch(e => {
      console.error('Error clearing vault from IndexedDB:', e)
    })
  }, [automergeHandle])

  // Helper to check if a note has content (distinguishes light vs full records)
  const noteHasContent = (note: ObsidianObsNote | ObsidianObsNoteMeta): note is ObsidianObsNote => {
    return 'content' in note && typeof note.content === 'string' && note.content.length > 0
  }

  // Convert light note to full note with placeholder content
  const lightNoteToFullNote = (note: ObsidianObsNoteMeta, vaultName: string): ObsidianObsNote => {
    return {
      ...note,
      content: '', // Will be loaded on-demand from IndexedDB
      vaultPath: note.vaultPath || vaultName
    }
  }

  // Convert light folder tree to full folder tree (with empty content placeholders)
  const lightFolderTreeToFull = (node: FolderNodeMeta, vaultName: string): FolderNode => {
    return {
      name: node.name,
      path: node.path,
      children: node.children.map(child => lightFolderTreeToFull(child, vaultName)),
      notes: node.notes.map(note => lightNoteToFullNote(note, vaultName)),
      isExpanded: node.isExpanded,
      level: node.level
    }
  }

  // Load content for a note from IndexedDB
  const loadNoteContentFromIDB = async (noteId: string): Promise<string> => {
    try {
      const content = await getNoteContent(noteId)
      return content || ''
    } catch (e) {
      console.error('Failed to load note content from IndexedDB:', e)
      return ''
    }
  }

  // Load content for multiple notes from IndexedDB
  const loadVaultContentFromIDB = async (vaultName: string): Promise<Map<string, string>> => {
    try {
      return await getVaultNoteContents(vaultName)
    } catch (e) {
      console.error('Failed to load vault content from IndexedDB:', e)
      return new Map()
    }
  }

  // Load vault from Automerge store (handles both light and full records)
  const loadVaultFromAutomerge = (vaultName: string): ObsidianVault | null => {
    // Try loading from Automerge first
    if (automergeHandle) {
      try {
        const doc = automergeHandle.doc()
        if (doc && doc.store) {
          const vaultId = `obsidian_vault:${vaultName}`
          const vaultRecord = doc.store[vaultId] as (ObsidianVaultRecord | ObsidianVaultRecordLight) | undefined

          if (vaultRecord && vaultRecord.typeName === 'obsidian_vault') {
            const recordCopy = JSON.parse(JSON.stringify(vaultRecord))
            if (typeof recordCopy.lastImported === 'string') {
              recordCopy.lastImported = new Date(recordCopy.lastImported)
            }

            // Check if this is a light record (notes don't have content)
            const isLightRecord = recordCopy.obs_notes.length > 0 && !noteHasContent(recordCopy.obs_notes[0])

            if (isLightRecord) {
              // Convert light record to full vault with empty content placeholders
              return {
                name: recordCopy.name,
                path: recordCopy.path,
                obs_notes: recordCopy.obs_notes.map((n: ObsidianObsNoteMeta) => lightNoteToFullNote(n, vaultName)),
                totalObsNotes: recordCopy.totalObsNotes,
                lastImported: recordCopy.lastImported,
                folderTree: lightFolderTreeToFull(recordCopy.folderTree, vaultName)
              }
            }

            return importer.recordToVault(recordCopy)
          }
        }
      } catch (error) {
        console.error('Error loading from Automerge:', error)
        // Fall through to localStorage
      }
    }

    // Try localStorage as fallback
    try {
      const cached = localStorage.getItem(`obsidian_vault_cache:${vaultName}`)
      if (cached) {
        const vaultRecord = JSON.parse(cached) as (ObsidianVaultRecord | ObsidianVaultRecordLight)
        if (vaultRecord && vaultRecord.typeName === 'obsidian_vault') {
          if (typeof vaultRecord.lastImported === 'string') {
            vaultRecord.lastImported = new Date(vaultRecord.lastImported)
          }

          // Check if this is a light record
          const isLightRecord = vaultRecord.obs_notes.length > 0 && !noteHasContent(vaultRecord.obs_notes[0])

          if (isLightRecord) {
            return {
              name: vaultRecord.name,
              path: vaultRecord.path,
              obs_notes: vaultRecord.obs_notes.map((n: any) => lightNoteToFullNote(n, vaultName)),
              totalObsNotes: vaultRecord.totalObsNotes,
              lastImported: vaultRecord.lastImported,
              folderTree: lightFolderTreeToFull(vaultRecord.folderTree as FolderNodeMeta, vaultName)
            }
          }

          return importer.recordToVault(vaultRecord as ObsidianVaultRecord)
        }
      }
    } catch (e) {
      // Silent fail
    }

    return null
  }

  // Load vault on component mount - prioritize user's configured vault from session
  useEffect(() => {
    // Prevent multiple loads if already loading or already loaded once
    if (isLoadingVault || hasLoadedOnce) {
      return
    }

    // FIRST PRIORITY: Try to load from user's configured vault in session (user identity)
    if (session.obsidianVaultPath && session.obsidianVaultPath !== 'folder-selected') {
      // First try to load from Automerge cache for faster loading
      if (session.obsidianVaultName) {
        const cachedVault = loadVaultFromAutomerge(session.obsidianVaultName)
        if (cachedVault) {
          setVault(cachedVault)
          setIsLoading(false)
          setHasLoadedOnce(true)
          return
        }
      }

      // If not in cache, load from source (Quartz URL or local path)
      loadVault(session.obsidianVaultPath)
    } else if (session.obsidianVaultPath === 'folder-selected' && session.obsidianVaultName) {
      // For folder-selected vaults, we can't reload them, so show a special reselect interface
      setVault(null)
      setShowFolderReselect(true)
      setIsLoading(false)
      setHasLoadedOnce(true)
    } else {
      setVault(null)
      setIsLoading(false)
      setHasLoadedOnce(true)
    }
  }, []) // Remove dependencies to ensure this only runs once on mount

  // Handle session changes only if we haven't loaded yet AND values actually changed
  useEffect(() => {
    // Check if values actually changed (not just object reference)
    const vaultPathChanged = previousVaultPathRef.current !== session.obsidianVaultPath
    const vaultNameChanged = previousVaultNameRef.current !== session.obsidianVaultName

    // If vault is already loaded and values haven't changed, don't do anything
    if (hasLoadedOnce && !vaultPathChanged && !vaultNameChanged) {
      return
    }

    // Update refs to current values
    previousVaultPathRef.current = session.obsidianVaultPath
    previousVaultNameRef.current = session.obsidianVaultName

    // Only proceed if values actually changed and we haven't loaded yet
    if (!vaultPathChanged && !vaultNameChanged) {
      return
    }

    if (hasLoadedOnce || isLoadingVault) {
      return
    }

    if (session.obsidianVaultPath && session.obsidianVaultPath !== 'folder-selected') {
      loadVault(session.obsidianVaultPath)
    } else if (session.obsidianVaultPath === 'folder-selected' && session.obsidianVaultName) {
      setVault(null)
      setShowFolderReselect(true)
      setIsLoading(false)
      setHasLoadedOnce(true)
    }
  }, [session.obsidianVaultPath, session.obsidianVaultName, hasLoadedOnce, isLoadingVault])

  // Auto-open folder picker if requested
  useEffect(() => {
    if (autoOpenFolderPicker) {
      handleFolderPicker()
    }
  }, [autoOpenFolderPicker])

  // Reset loading state when component is closed (but not in shape mode)
  useEffect(() => {
    if (!showVaultBrowser && !shapeMode) {
      // Reset states when component is closed (only in modal mode, not shape mode)
      setHasLoadedOnce(false)
      setIsLoadingVault(false)
    }
  }, [showVaultBrowser, shapeMode])


  // Debounce search query for better performance
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedSearchQuery(searchQuery)
    }, 150) // 150ms delay

    return () => clearTimeout(timer)
  }, [searchQuery])

  // Handle ESC key to close the browser
  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'Escape') {
        onClose()
      }
    }

    document.addEventListener('keydown', handleKeyDown)
    return () => {
      document.removeEventListener('keydown', handleKeyDown)
    }
  }, [onClose])

  const loadVault = async (path?: string) => {
    // Prevent concurrent loading operations
    if (isLoadingVault) {
      return
    }

    setIsLoadingVault(true)
    setIsLoading(true)
    setError(null)

    try {
      if (path) {
        // Check if it's a Quartz URL
        if (path.startsWith('http') || path.includes('quartz') || path.includes('.xyz') || path.includes('.com')) {
          const loadedVault = await importer.importFromQuartzUrl(path)
          setVault(loadedVault)
          setShowVaultInput(false)
          setShowFolderReselect(false)
          updateSession({
            obsidianVaultPath: path,
            obsidianVaultName: loadedVault.name
          })
          saveVaultToAutomerge(loadedVault)
        } else {
          const loadedVault = await importer.importFromDirectory(path)
          setVault(loadedVault)
          setShowVaultInput(false)
          setShowFolderReselect(false)
          updateSession({
            obsidianVaultPath: path,
            obsidianVaultName: loadedVault.name
          })
          saveVaultToAutomerge(loadedVault)
        }
      } else {
        setVault(null)
        setShowVaultInput(false)
      }
    } catch (err) {
      console.error('Failed to load vault:', err)
      setError('Failed to load Obsidian vault. Please try again.')
      setVault(null)
      if (!session.obsidianVaultPath) {
        setShowVaultInput(true)
      }
    } finally {
      setIsLoading(false)
      setIsLoadingVault(false)
      setHasLoadedOnce(true)
    }
  }

  const handleVaultPathSubmit = async () => {
    if (!vaultPath.trim()) {
      setError('Please enter a vault path or URL')
      return
    }

    if (inputMethod === 'quartz') {
      try {
        setIsLoading(true)
        setError(null)
        const loadedVault = await importer.importFromQuartzUrl(vaultPath.trim())
        setVault(loadedVault)
        setShowVaultInput(false)
        setShowFolderReselect(false)
        updateSession({
          obsidianVaultPath: vaultPath.trim(),
          obsidianVaultName: loadedVault.name
        })
      } catch (error) {
        console.error('Error loading Quartz vault:', error)
        setError(error instanceof Error ? error.message : 'Failed to load Quartz vault')
      } finally {
        setIsLoading(false)
      }
    } else {
      loadVault(vaultPath.trim())
    }
  }

  const handleFolderPicker = async () => {
    if (!('showDirectoryPicker' in window)) {
      setError('File System Access API is not supported in this browser. Please use "Enter Path" instead.')
      setShowVaultInput(true)
      return
    }

    try {
      setIsLoading(true)
      setError(null)

      const loadedVault = await importer.importFromFileSystem()

      setVault(loadedVault)
      setShowVaultInput(false)
      setShowFolderReselect(false)

      updateSession({
        obsidianVaultPath: 'folder-selected',
        obsidianVaultName: loadedVault.name
      })

      saveVaultToAutomerge(loadedVault)
    } catch (err) {
      if ((err as any).name === 'AbortError') {
        setError(null)
      } else {
        console.error('Failed to load vault from folder picker:', err)
        setError('Failed to load Obsidian vault. Please try again.')
      }
    } finally {
      setIsLoading(false)
    }
  }

  // Filter obs_notes based on search query and folder selection
  const filteredObsNotes = useMemo(() => {
    if (!vault) return []

    let obs_notes = vault.obs_notes

    // Filter out any undefined or null notes first
    obs_notes = obs_notes.filter(obs_note => obs_note != null)

    // Filter by search query - use debounced query for better performance
    // When no search query, show all notes
    if (debouncedSearchQuery && debouncedSearchQuery.trim()) {
      const lowercaseQuery = debouncedSearchQuery.toLowerCase().trim()
      obs_notes = obs_notes.filter(obs_note => 
        obs_note && (
          (obs_note.title && obs_note.title.toLowerCase().includes(lowercaseQuery)) ||
          (obs_note.content && obs_note.content.toLowerCase().includes(lowercaseQuery)) ||
          (obs_note.tags && obs_note.tags.some(tag => tag.toLowerCase().includes(lowercaseQuery))) ||
          (obs_note.filePath && obs_note.filePath.toLowerCase().includes(lowercaseQuery))
        )
      )
    }

    // Filter by selected folder if in tree view
    if (viewMode === 'tree' && selectedFolder !== null && folderTree) {
      const folder = importer.findFolderByPath(folderTree, selectedFolder)
      if (folder) {
        const folderNotes = importer.getAllNotesFromTree(folder)
        obs_notes = obs_notes.filter(note => folderNotes.some(folderNote => folderNote.id === note.id))
      }
    }

    return obs_notes
  }, [vault, debouncedSearchQuery, viewMode, selectedFolder, folderTree, importer])

  // Listen for trigger-obsnote-creation event from CustomToolbar
  useEffect(() => {
    const handleTriggerCreation = () => {
      if (selectedNotes.size > 0) {
        const selectedObsNotes = filteredObsNotes.filter(obs_note => selectedNotes.has(obs_note.id))
        onObsNotesSelect(selectedObsNotes)
      } else {
        const allVisibleNotes = filteredObsNotes
        if (allVisibleNotes.length > 0) {
          onObsNotesSelect(allVisibleNotes)
        }
      }
    }

    window.addEventListener('trigger-obsnote-creation', handleTriggerCreation as EventListener)

    return () => {
      window.removeEventListener('trigger-obsnote-creation', handleTriggerCreation as EventListener)
    }
  }, [selectedNotes, filteredObsNotes, onObsNotesSelect])

  // Helper function to get a better title for display
  const getDisplayTitle = (obs_note: ObsidianObsNote): string => {
    // Safety check for undefined obs_note
    if (!obs_note) {
      return 'Untitled'
    }
    
    // Use frontmatter title if available, otherwise use filename without extension
    if (obs_note.frontmatter && obs_note.frontmatter.title) {
      return obs_note.frontmatter.title
    }
    
    // For Quartz URLs, use the title property which should be clean
    if (obs_note.filePath && obs_note.filePath.startsWith('http')) {
      return obs_note.title || 'Untitled'
    }
    
    // Clean up filename for display
    return obs_note.filePath
      .replace(/\.md$/, '')
      .replace(/[-_]/g, ' ')
      .replace(/\b\w/g, l => l.toUpperCase())
  }

  // Helper function to get content preview
  const getContentPreview = (obs_note: ObsidianObsNote, maxLength: number = 200): string => {
    // Safety check for undefined obs_note
    if (!obs_note) {
      return 'No content available'
    }
    
    let content = obs_note.content || ''
    
    // Remove frontmatter if present
    content = content.replace(/^---\n[\s\S]*?\n---\n/, '')
    
    // Remove markdown headers for cleaner preview
    content = content.replace(/^#+\s+/gm, '')
    
    // Clean up and truncate
    content = content
      .replace(/\n+/g, ' ')
      .replace(/\s+/g, ' ')
      .trim()
    
    if (content.length > maxLength) {
      content = content.substring(0, maxLength) + '...'
    }
    
    return content || 'No content preview available'
  }

  // Helper function to get file path, checking session for quartz link if blank
  const getFilePath = (obs_note: ObsidianObsNote): string => {
    // If filePath exists and is not blank, use it
    if (obs_note.filePath && obs_note.filePath.trim() !== '') {
      if (obs_note.filePath.startsWith('http')) {
        try {
          return new URL(obs_note.filePath).pathname.replace(/^\//, '') || 'Home'
        } catch (e) {
          return obs_note.filePath
        }
      }
      return obs_note.filePath
    }
    
    // If filePath is blank, check session for quartz link (user API)
    if (session.obsidianVaultPath && 
        session.obsidianVaultPath !== 'folder-selected' &&
        (session.obsidianVaultPath.startsWith('http') || 
         session.obsidianVaultPath.includes('quartz') || 
         session.obsidianVaultPath.includes('.xyz') || 
         session.obsidianVaultPath.includes('.com'))) {
      // Construct file path from quartz URL and note title/ID
      try {
        const baseUrl = new URL(session.obsidianVaultPath)
        // Use note title or ID to construct a path
        const notePath = obs_note.title || obs_note.id || 'Untitled'
        // Clean up the note path to make it URL-friendly
        const cleanPath = notePath.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()
        return `${baseUrl.hostname}${baseUrl.pathname}/${cleanPath}`
      } catch (e) {
        // If URL parsing fails, just return the vault path
        return session.obsidianVaultPath
      }
    }
    
    // If no quartz link found in session, return a fallback based on note info
    return obs_note.title || obs_note.id || 'Untitled'
  }

  // Helper function to highlight search matches
  const highlightSearchMatches = (text: string, query: string): string => {
    if (!query.trim()) return text
    
    try {
      const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi')
      return text.replace(regex, '<mark>$1</mark>')
    } catch (error) {
      console.error('Error highlighting search matches:', error)
      return text
    }
  }

  const handleObsNoteClick = async (obs_note: ObsidianObsNote) => {
    // Load content from IndexedDB if not already loaded
    if (!obs_note.content && vault) {
      const content = await loadNoteContentFromIDB(obs_note.id)
      onObsNoteSelect({ ...obs_note, content })
    } else {
      onObsNoteSelect(obs_note)
    }
  }

  const handleObsNoteToggle = (obs_note: ObsidianObsNote) => {
    const newSelected = new Set(selectedNotes)
    if (newSelected.has(obs_note.id)) {
      newSelected.delete(obs_note.id)
    } else {
      newSelected.add(obs_note.id)
    }
    setSelectedNotes(newSelected)
  }

  const handleBulkImport = async () => {
    const selectedObsNotes = filteredObsNotes.filter(obs_note => selectedNotes.has(obs_note.id))

    // Load content from IndexedDB for all selected notes
    if (vault) {
      const contentMap = await loadVaultContentFromIDB(vault.name)
      const notesWithContent = selectedObsNotes.map(note => ({
        ...note,
        content: note.content || contentMap.get(note.id) || ''
      }))
      onObsNotesSelect(notesWithContent)
    } else {
      onObsNotesSelect(selectedObsNotes)
    }

    setSelectedNotes(new Set())
  }

  const handleSelectAll = () => {
    if (selectedNotes.size === filteredObsNotes.length) {
      setSelectedNotes(new Set())
    } else {
      setSelectedNotes(new Set(filteredObsNotes.map(obs_note => obs_note.id)))
    }
  }

  const clearFilters = () => {
    setSearchQuery('')
    setDebouncedSearchQuery('')
    setSelectedNotes(new Set())
  }

  // Folder management functions
  const toggleFolderExpansion = (folderPath: string) => {
    const newExpanded = new Set(expandedFolders)
    if (newExpanded.has(folderPath)) {
      newExpanded.delete(folderPath)
    } else {
      newExpanded.add(folderPath)
    }
    setExpandedFolders(newExpanded)
  }

  const selectFolder = (folderPath: string) => {
    setSelectedFolder(folderPath)
  }

  const getNotesFromFolder = (folder: FolderNode): ObsidianObsNote[] => {
    if (!folder) return []
    
    let notes = [...folder.notes]
    
    // If folder is expanded, include notes from subfolders
    if (expandedFolders.has(folder.path)) {
      folder.children.forEach(child => {
        notes.push(...getNotesFromFolder(child))
      })
    }
    
    return notes
  }


  const handleDisconnectVault = () => {
    // Clear vault from Automerge/sync when disconnecting
    if (vault) {
      clearVaultFromAutomerge(vault.name)
    }

    updateSession({
      obsidianVaultPath: undefined,
      obsidianVaultName: undefined
    })

    setVault(null)
    setSearchQuery('')
    setDebouncedSearchQuery('')
    setSelectedNotes(new Set())
    setShowVaultInput(false)
    setShowFolderReselect(false)
    setError(null)
    setHasLoadedOnce(false)
    setIsLoadingVault(false)
  }

  // Clear vault data from Automerge without disconnecting (for privacy)
  const handleClearVaultFromSync = () => {
    if (vault) {
      clearVaultFromAutomerge(vault.name)
      alert(`Cleared "${vault.name}" from sync. Your vault data will no longer be visible to others.`)
    }
  }

  const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
    // Only close if clicking on the backdrop, not on the modal content
    if (e.target === e.currentTarget) {
      onClose()
    }
  }

  if (isLoading) {
    return (
      <div className={`obsidian-browser ${className}`} onClick={handleBackdropClick}>
        <div className="loading-container">
          <div className="loading-spinner"></div>
          <p>Loading Obsidian vault...</p>
        </div>
      </div>
    )
  }

  if (error) {
    return (
      <div className={`obsidian-browser ${className}`} onClick={handleBackdropClick}>
        <div className="error-container">
          <h3>Error Loading Vault</h3>
          <p>{error}</p>
          <button onClick={() => loadVault()} className="retry-button">
            Try Again
          </button>
          <button onClick={onClose} className="close-button">
            Close
          </button>
        </div>
      </div>
    )
  }

  if (!vault && !showVaultInput && !isLoading) {
    // Check if user has a folder-selected vault that needs reselection
    if (showFolderReselect && session.obsidianVaultPath === 'folder-selected' && session.obsidianVaultName) {
      return (
        <div className={`obsidian-browser ${className}`} onClick={handleBackdropClick}>
          <div className="folder-reselect-container">
            <h3>Reselect Obsidian Vault</h3>
            <p>Your vault "<strong>{session.obsidianVaultName}</strong>" was previously selected via folder picker.</p>
            <p>Due to browser security restrictions, we need you to reselect the folder to access your notes.</p>
            <div className="vault-options">
              <button onClick={handleFolderPicker} className="load-vault-button primary">
                📁 Reselect Folder
              </button>
              <button onClick={() => setShowVaultInput(true)} className="load-vault-button secondary">
                📝 Enter Path Instead
              </button>
            </div>
            <p className="help-text">
              Select the same folder again to continue using your Obsidian vault, or enter the path manually.
            </p>
          </div>
        </div>
      )
    }
    
    // Check if user has a vault configured but it failed to load
    if (session.obsidianVaultPath && session.obsidianVaultPath !== 'folder-selected') {
      return (
        <div className={`obsidian-browser ${className}`} onClick={handleBackdropClick}>
          <div className="error-container">
            <h3>Vault Loading Failed</h3>
            <p>Failed to load your configured Obsidian vault at: <code>{session.obsidianVaultPath}</code></p>
            <p>This might be because the path has changed or the vault is no longer accessible.</p>
            <div className="vault-options">
              <button onClick={() => loadVault(session.obsidianVaultPath)} className="retry-button">
                🔄 Retry Loading
              </button>
              <button onClick={() => setShowVaultInput(true)} className="load-vault-button secondary">
                📝 Change Path
              </button>
              <button onClick={handleFolderPicker} className="load-vault-button primary">
                📁 Select New Folder
              </button>
            </div>
          </div>
        </div>
      )
    }
    
    // No vault configured at all
    return (
      <div className={`obsidian-browser ${className}`} onClick={handleBackdropClick}>
        <div className="no-vault-container">
          <h3>Load Obsidian Vault</h3>
          <p>Choose how you'd like to load your Obsidian vault:</p>
          <div className="vault-options">
            <button
              onClick={handleFolderPicker}
              className="load-vault-button primary"
            >
              📁 Select Folder
            </button>
            <button
              onClick={() => {
                if (session.obsidianVaultPath && session.obsidianVaultPath !== 'folder-selected') {
                  setVaultPath(session.obsidianVaultPath)
                }
                setShowVaultInput(true)
              }}
              className="load-vault-button secondary"
            >
              📝 Enter Path
            </button>
          </div>
          <p className="help-text">
            Select a folder containing your Obsidian vault, or enter the path manually.
          </p>
        </div>
      </div>
    )
  }

  if (showVaultInput) {
    return (
      <div className={`obsidian-browser ${className}`} onClick={handleBackdropClick}>
        <div className="vault-input-container">
          <h3>Enter Vault Path</h3>
          <div className="input-method-selector">
            <button
              onClick={() => setInputMethod('folder')}
              className={`method-button ${inputMethod === 'folder' ? 'active' : ''}`}
            >
              📁 Local Folder
            </button>
            <button
              onClick={() => setInputMethod('url')}
              className={`method-button ${inputMethod === 'url' ? 'active' : ''}`}
            >
              🌐 URL/Path
            </button>
            <button
              onClick={() => setInputMethod('quartz')}
              className={`method-button ${inputMethod === 'quartz' ? 'active' : ''}`}
            >
              💎 Quartz Site
            </button>
          </div>
          
          <div className="path-input-section">
            <input
              type="text"
              placeholder={
                inputMethod === 'folder' 
                  ? 'Enter folder path (e.g., /Users/username/Documents/MyVault)' 
                  : inputMethod === 'quartz'
                  ? 'Enter Quartz URL (e.g., https://quartz.jzhao.xyz)'
                  : 'Enter URL or path'
              }
              value={vaultPath}
              onChange={(e) => setVaultPath(e.target.value)}
              className="path-input"
              onKeyPress={(e) => e.key === 'Enter' && handleVaultPathSubmit()}
            />
            <button onClick={handleVaultPathSubmit} className="submit-button">
              Load Vault
            </button>
          </div>
          
          <div className="input-help">
            {inputMethod === 'folder' ? (
              <p>Enter the full path to your Obsidian vault folder on your computer.</p>
            ) : inputMethod === 'quartz' ? (
              <p>Enter a Quartz site URL to import content as Obsidian notes (e.g., https://quartz.jzhao.xyz).</p>
            ) : (
              <p>Enter a URL or path to your Obsidian vault (if accessible via web).</p>
            )}
          </div>
          
          <div className="input-actions">
            <button onClick={() => setShowVaultInput(false)} className="back-button">
              ← Back
            </button>
            <button onClick={handleFolderPicker} className="folder-picker-button">
              📁 Browse Folder
            </button>
          </div>
        </div>
      </div>
    )
  }

  // Helper function to check if a folder has content (notes or subfolders with content)
  const hasContent = (folder: FolderNode): boolean => {
    if (folder.notes.length > 0) return true
    return folder.children.some(child => hasContent(child))
  }

  // Folder tree component - skips Root and content folders, shows only files from content
  const renderFolderTree = (folder: FolderNode, level: number = 0) => {
    if (!folder) return null

    // Skip Root folder - look for content folder inside it
    if (folder.name === 'Root') {
      // Find the "content" folder
      const contentFolder = folder.children.find(child => child.name === 'content' || child.name.toLowerCase() === 'content')
      
      if (contentFolder) {
        // Skip both Root and content folders, render content folder's children and notes directly
        return (
          <div className="folder-children">
            {contentFolder.children
              .filter(child => hasContent(child))
              .map(child => renderFolderTree(child, level))}
            {contentFolder.notes.map(note => (
              <div 
                key={note.id}
                className={`note-item ${selectedNotes.has(note.id) ? 'selected' : ''}`}
                style={{ paddingLeft: `${level * 20}px` }}
                onClick={(e) => {
                  e.stopPropagation()
                  handleObsNoteToggle(note)
                }}
              >
                <span className="note-icon">📄</span>
                <span className="note-name">{getDisplayTitle(note)}</span>
              </div>
            ))}
          </div>
        )
      } else {
        // No content folder found, render root's children (excluding root itself)
        return (
          <div className="folder-children">
            {folder.children
              .filter(child => hasContent(child) && child.name !== 'content')
              .map(child => renderFolderTree(child, level))}
            {folder.notes.map(note => (
              <div 
                key={note.id}
                className={`note-item ${selectedNotes.has(note.id) ? 'selected' : ''}`}
                style={{ paddingLeft: `${level * 20}px` }}
                onClick={(e) => {
                  e.stopPropagation()
                  handleObsNoteToggle(note)
                }}
              >
                <span className="note-icon">📄</span>
                <span className="note-name">{getDisplayTitle(note)}</span>
              </div>
            ))}
          </div>
        )
      }
    }

    // Skip "content" folder - render its children and notes directly
    if (folder.name === 'content' || folder.name.toLowerCase() === 'content') {
      return (
        <div className="folder-children">
          {folder.children
            .filter(child => hasContent(child))
            .map(child => renderFolderTree(child, level))}
          {folder.notes.map(note => (
            <div 
              key={note.id}
              className={`note-item ${selectedNotes.has(note.id) ? 'selected' : ''}`}
              style={{ paddingLeft: `${level * 20}px` }}
              onClick={(e) => {
                e.stopPropagation()
                handleObsNoteToggle(note)
              }}
            >
              <span className="note-icon">📄</span>
              <span className="note-name">{getDisplayTitle(note)}</span>
            </div>
          ))}
        </div>
      )
    }

    // Render normal folders (not Root or content)
    const isExpanded = expandedFolders.has(folder.path)
    const isSelected = selectedFolder === folder.path
    const hasChildren = folder.children.length > 0 || folder.notes.length > 0

    return (
      <div key={folder.path} className="folder-tree-item">
        <div 
          className={`folder-item ${isSelected ? 'selected' : ''}`}
          style={{ paddingLeft: `${level * 20}px` }}
          onClick={() => selectFolder(folder.path)}
        >
          {hasChildren && (
            <button
              className="folder-toggle"
              onClick={(e) => {
                e.stopPropagation()
                toggleFolderExpansion(folder.path)
              }}
            >
              {isExpanded ? '▼' : '▶'}
            </button>
          )}
          <span className="folder-icon">📁</span>
          <span className="folder-name">{folder.name}</span>
          <span className="folder-count">
            ({folder.notes.length + folder.children.reduce((acc, child) => acc + child.notes.length, 0)})
          </span>
        </div>
        
        {isExpanded && (
          <div className="folder-children">
            {folder.children
              .filter(child => hasContent(child) && child.name !== 'content')
              .map(child => renderFolderTree(child, level + 1))}
            {folder.notes.map(note => (
              <div 
                key={note.id}
                className={`note-item ${selectedNotes.has(note.id) ? 'selected' : ''}`}
                style={{ paddingLeft: `${(level + 1) * 20}px` }}
                onClick={(e) => {
                  e.stopPropagation()
                  handleObsNoteToggle(note)
                }}
              >
                <span className="note-icon">📄</span>
                <span className="note-name">{getDisplayTitle(note)}</span>
              </div>
            ))}
          </div>
        )}
      </div>
    )
  }

  // Shape mode: render without modal overlay
  if (shapeMode) {
    return (
      <div 
        className={`obsidian-browser shape-mode ${className}`}
        onClick={(e) => {
          // Only stop propagation for interactive elements (buttons, inputs, note items, etc.)
          const target = e.target as HTMLElement
          const isInteractive = target.tagName === 'BUTTON' || 
                                target.tagName === 'INPUT' || 
                                target.tagName === 'TEXTAREA' ||
                                target.tagName === 'SELECT' ||
                                target.closest('button') ||
                                target.closest('input') ||
                                target.closest('textarea') ||
                                target.closest('select') ||
                                target.closest('[role="button"]') ||
                                target.closest('a') ||
                                target.closest('.note-item') || // Obsidian note items in list view
                                target.closest('.note-card') // Obsidian note cards in grid/list view
          if (isInteractive) {
            e.stopPropagation()
          }
          // Don't stop propagation for white space - let tldraw handle dragging
        }}
        onPointerDown={(e) => {
          // Only stop propagation for interactive elements to allow tldraw to handle dragging on white space
          const target = e.target as HTMLElement
          const isInteractive = target.tagName === 'BUTTON' || 
                                target.tagName === 'INPUT' || 
                                target.tagName === 'TEXTAREA' ||
                                target.tagName === 'SELECT' ||
                                target.closest('button') ||
                                target.closest('input') ||
                                target.closest('textarea') ||
                                target.closest('select') ||
                                target.closest('[role="button"]') ||
                                target.closest('a') ||
                                target.closest('.note-item') || // Obsidian note items in list view
                                target.closest('.note-card') // Obsidian note cards in grid/list view
          if (isInteractive) {
            e.stopPropagation()
          }
          // Don't stop propagation for white space - let tldraw handle dragging
        }}
        style={{ 
          width: '100%', 
          height: '100%', 
          overflow: 'hidden',
          display: 'flex',
          flexDirection: 'column',
          pointerEvents: 'auto'
        }}
      >
        <div className="browser-content" style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
          {/* Close button removed - using StandardizedToolWrapper header instead */}
        <div className="vault-title">
          <h2>
            {vault ? `Obsidian Vault: ${vault.name}` : 'No Obsidian Vault Connected'}
          </h2>
        </div>

        {vault && (
          <div className="browser-controls">
            <div className="search-container">
              <div className="search-input-wrapper">
                <input
                  type="text"
                  placeholder="Search notes by title, content, or tags..."
                  value={searchQuery}
                  onChange={(e) => setSearchQuery(e.target.value)}
                  className="search-input"
                />
                {searchQuery && (
                  <button
                    onClick={() => setSearchQuery('')}
                    className="clear-search-button"
                    title="Clear search"
                  >
                    ×
                  </button>
                )}
              </div>
              <div className="search-stats">
                <span className="search-results-count">
                  {searchQuery ? (
                    searchQuery !== debouncedSearchQuery ? (
                      <span className="search-loading">Searching...</span>
                    ) : (
                      `${filteredObsNotes.length} result${filteredObsNotes.length !== 1 ? 's' : ''} found`
                    )
                  ) : (
                    `Showing all ${filteredObsNotes.length} notes`
                  )}
                </span>
              </div>
            </div>
            
            <div className="view-controls">
              <div className="view-mode-toggle">
                <button
                  onClick={() => setViewMode('tree')}
                  className={`view-button ${viewMode === 'tree' ? 'active' : ''}`}
                  title="Tree View"
                >
                  🌳
                </button>
                <button
                  onClick={() => setViewMode('grid')}
                  className={`view-button ${viewMode === 'grid' ? 'active' : ''}`}
                  title="Grid View"
                >
                  ⊞
                </button>
                <button
                  onClick={() => setViewMode('list')}
                  className={`view-button ${viewMode === 'list' ? 'active' : ''}`}
                  title="List View"
                >
                  ☰
                </button>
              </div>
              <button
                onClick={handleDisconnectVault}
                className="disconnect-vault-button"
                title="Disconnect Vault"
              >
                🔌 Disconnect Vault
              </button>
              <button
                onClick={handleClearVaultFromSync}
                className="clear-sync-button"
                title="Clear vault data from sync (keeps local data)"
                style={{ marginLeft: '8px', fontSize: '0.85em', opacity: 0.8 }}
              >
                🗑️ Clear from Sync
              </button>
            </div>

            <div className="selection-controls">
              <button
                onClick={handleSelectAll}
                className="select-all-button"
                disabled={filteredObsNotes.length === 0}
              >
                {selectedNotes.size === filteredObsNotes.length && filteredObsNotes.length > 0 ? 'Deselect All' : 'Select All'}
              </button>
              {selectedNotes.size > 0 && (
                <button
                  onClick={handleBulkImport}
                  className="bulk-import-button primary"
                >
                  🎯 Pull to Canvas ({selectedNotes.size})
                </button>
              )}
            </div>
          </div>
        )}

        {vault && (
          <div className="notes-container">
            <div className="notes-header">
              <span>
                {debouncedSearchQuery && debouncedSearchQuery.trim() 
                  ? `${filteredObsNotes.length} notes found for "${debouncedSearchQuery}"`
                  : `All ${filteredObsNotes.length} notes`
                }
              </span>
              {vault && (
                <span className="debug-info">
                  (Total: {vault.obs_notes.length}, Search: "{debouncedSearchQuery}")
                </span>
              )}
              {vault && vault.lastImported && (
                <span className="last-imported">
                  Last imported: {vault.lastImported.toLocaleString()}
                </span>
              )}
            </div>

            <div className={`notes-display ${viewMode}`}>
              {viewMode === 'tree' ? (
                <div className="folder-tree-container">
                  {folderTree ? (
                    <div className="folder-tree">
                      {renderFolderTree(folderTree)}
                    </div>
                  ) : (
                    <div className="no-folder-tree">
                      <p>No folder structure available</p>
                    </div>
                  )}
                </div>
              ) : filteredObsNotes.length === 0 ? (
                <div className="no-notes">
                  <p>No notes found. {vault ? `Vault has ${vault.obs_notes.length} notes.` : 'Vault not loaded.'}</p>
                  <p>Search query: "{debouncedSearchQuery}"</p>
                </div>
              ) : (
                filteredObsNotes.map(obs_note => {
                  // Safety check for undefined obs_note
                  if (!obs_note) {
                    return null
                  }
                  
                  const isSelected = selectedNotes.has(obs_note.id)
                  const displayTitle = getDisplayTitle(obs_note)
                  const contentPreview = getContentPreview(obs_note, viewMode === 'grid' ? 120 : 200)
                
                  return (
                    <div
                      key={obs_note.id}
                      className={`note-card ${isSelected ? 'selected' : ''}`}
                      onClick={() => handleObsNoteToggle(obs_note)}
                    >
                      <div className="note-card-header">
                        <div className="note-card-checkbox">
                          <input
                            type="checkbox"
                            checked={isSelected}
                            onChange={() => handleObsNoteToggle(obs_note)}
                            onClick={(e) => e.stopPropagation()}
                          />
                        </div>
                        <div className="note-card-title-section">
                          <h3 
                            className="note-card-title" 
                            title={displayTitle}
                            dangerouslySetInnerHTML={{
                              __html: highlightSearchMatches(displayTitle, debouncedSearchQuery)
                            }}
                          />
                          <span className="note-card-date">
                            {obs_note.modified ? 
                              (obs_note.modified instanceof Date ? 
                                obs_note.modified.toLocaleDateString() : 
                                new Date(obs_note.modified).toLocaleDateString()
                              ) : 'Unknown date'}
                          </span>
                        </div>
                        <button
                          className="note-card-quick-add"
                          onClick={(e) => {
                            e.stopPropagation()
                            handleObsNoteClick(obs_note)
                          }}
                          title="Add to Canvas"
                        >
                          +
                        </button>
                      </div>
                      
                      <div className="note-card-content">
                        <p 
                          className="note-card-preview"
                          dangerouslySetInnerHTML={{
                            __html: highlightSearchMatches(contentPreview, debouncedSearchQuery)
                          }}
                        />
                      </div>

                      {obs_note.tags.length > 0 && (
                        <div className="note-card-tags">
                          {obs_note.tags.slice(0, viewMode === 'grid' ? 2 : 4).map(tag => (
                            <span key={tag} className="note-card-tag">
                              {tag.replace('#', '')}
                            </span>
                          ))}
                          {obs_note.tags.length > (viewMode === 'grid' ? 2 : 4) && (
                            <span className="note-card-tag-more">
                              +{obs_note.tags.length - (viewMode === 'grid' ? 2 : 4)}
                            </span>
                          )}
                        </div>
                      )}

                      <div className="note-card-meta">
                        <span className="note-card-path" title={obs_note.filePath || getFilePath(obs_note)}>
                          {getFilePath(obs_note)}
                        </span>
                        {obs_note.links.length > 0 && (
                          <span className="note-card-links">
                            {obs_note.links.length} links
                          </span>
                        )}
                      </div>
                    </div>
                  )
                })
              )}
            </div>
          </div>
        )}
        </div>
      </div>
    )
  }

  // Modal mode: render with overlay
  return (
    <div className={`obsidian-browser ${className}`} onClick={handleBackdropClick}>
      <div className="browser-content">
        <button onClick={onClose} className="close-button">
          ×
        </button>
        <div className="vault-title">
          <h2>
            {vault ? `Obsidian Vault: ${vault.name}` : 'No Obsidian Vault Connected'}
          </h2>
          {!vault && (
            <div className="vault-connect-section">
              <p className="vault-connect-message">
                Connect your Obsidian vault to browse and add notes to the canvas.
              </p>
              <button
                onClick={handleFolderPicker}
                className="connect-vault-button"
                disabled={isLoading}
              >
                {isLoading ? 'Connecting...' : 'Connect Vault'}
              </button>
            </div>
          )}
        </div>

        {vault && (
          <div className="browser-controls">
            <div className="search-container">
              <div className="search-input-wrapper">
                <input
                  type="text"
                  placeholder="Search notes by title, content, or tags..."
                  value={searchQuery}
                  onChange={(e) => setSearchQuery(e.target.value)}
                  className="search-input"
                />
                {searchQuery && (
                  <button
                    onClick={() => setSearchQuery('')}
                    className="clear-search-button"
                    title="Clear search"
                  >
                    ×
                  </button>
                )}
              </div>
              <div className="search-stats">
                <span className="search-results-count">
                  {searchQuery ? (
                    searchQuery !== debouncedSearchQuery ? (
                      <span className="search-loading">Searching...</span>
                    ) : (
                      `${filteredObsNotes.length} result${filteredObsNotes.length !== 1 ? 's' : ''} found`
                    )
                  ) : (
                    `Showing all ${filteredObsNotes.length} notes`
                  )}
                </span>
              </div>
            </div>
            
            <div className="view-controls">
              <div className="view-mode-toggle">
                <button
                  onClick={() => setViewMode('tree')}
                  className={`view-button ${viewMode === 'tree' ? 'active' : ''}`}
                  title="Tree View"
                >
                  🌳
                </button>
                <button
                  onClick={() => setViewMode('grid')}
                  className={`view-button ${viewMode === 'grid' ? 'active' : ''}`}
                  title="Grid View"
                >
                  ⊞
                </button>
                <button
                  onClick={() => setViewMode('list')}
                  className={`view-button ${viewMode === 'list' ? 'active' : ''}`}
                  title="List View"
                >
                  ☰
                </button>
              </div>
              <button
                onClick={handleDisconnectVault}
                className="disconnect-vault-button"
                title="Disconnect Vault"
              >
                🔌 Disconnect Vault
              </button>
              <button
                onClick={handleClearVaultFromSync}
                className="clear-sync-button"
                title="Clear vault data from sync (keeps local data)"
                style={{ marginLeft: '8px', fontSize: '0.85em', opacity: 0.8 }}
              >
                🗑️ Clear from Sync
              </button>
            </div>

            <div className="selection-controls">
              <button
                onClick={handleSelectAll}
                className="select-all-button"
                disabled={filteredObsNotes.length === 0}
              >
                {selectedNotes.size === filteredObsNotes.length && filteredObsNotes.length > 0 ? 'Deselect All' : 'Select All'}
              </button>
              {selectedNotes.size > 0 && (
                <button
                  onClick={handleBulkImport}
                  className="bulk-import-button primary"
                >
                  🎯 Pull to Canvas ({selectedNotes.size})
                </button>
              )}
            </div>
          </div>
        )}

        {vault && (
          <div className="notes-container">
            <div className="notes-header">
              <span>
                {debouncedSearchQuery && debouncedSearchQuery.trim() 
                  ? `${filteredObsNotes.length} notes found for "${debouncedSearchQuery}"`
                  : `All ${filteredObsNotes.length} notes`
                }
              </span>
              {vault && (
                <span className="debug-info">
                  (Total: {vault.obs_notes.length}, Search: "{debouncedSearchQuery}")
                </span>
              )}
              {vault && vault.lastImported && (
                <span className="last-imported">
                  Last imported: {vault.lastImported.toLocaleString()}
                </span>
              )}
            </div>

            <div className={`notes-display ${viewMode}`}>
              {viewMode === 'tree' ? (
                <div className="folder-tree-container">
                  {folderTree ? (
                    <div className="folder-tree">
                      {renderFolderTree(folderTree)}
                    </div>
                  ) : (
                    <div className="no-folder-tree">
                      <p>No folder structure available</p>
                    </div>
                  )}
                </div>
              ) : filteredObsNotes.length === 0 ? (
                <div className="no-notes">
                  <p>No notes found. {vault ? `Vault has ${vault.obs_notes.length} notes.` : 'Vault not loaded.'}</p>
                  <p>Search query: "{debouncedSearchQuery}"</p>
                </div>
              ) : (
                filteredObsNotes.map(obs_note => {
                  // Safety check for undefined obs_note
                  if (!obs_note) {
                    return null
                  }
                  
                  const isSelected = selectedNotes.has(obs_note.id)
                  const displayTitle = getDisplayTitle(obs_note)
                  const contentPreview = getContentPreview(obs_note, viewMode === 'grid' ? 120 : 200)
                
                  return (
                    <div
                      key={obs_note.id}
                      className={`note-card ${isSelected ? 'selected' : ''}`}
                      onClick={() => handleObsNoteToggle(obs_note)}
                    >
                      <div className="note-card-header">
                        <div className="note-card-checkbox">
                          <input
                            type="checkbox"
                            checked={isSelected}
                            onChange={() => handleObsNoteToggle(obs_note)}
                            onClick={(e) => e.stopPropagation()}
                          />
                        </div>
                        <div className="note-card-title-section">
                          <h3 
                            className="note-card-title" 
                            title={displayTitle}
                            dangerouslySetInnerHTML={{
                              __html: highlightSearchMatches(displayTitle, debouncedSearchQuery)
                            }}
                          />
                          <span className="note-card-date">
                            {obs_note.modified ? 
                              (obs_note.modified instanceof Date ? 
                                obs_note.modified.toLocaleDateString() : 
                                new Date(obs_note.modified).toLocaleDateString()
                              ) : 'Unknown date'}
                          </span>
                        </div>
                        <button
                          className="note-card-quick-add"
                          onClick={(e) => {
                            e.stopPropagation()
                            handleObsNoteClick(obs_note)
                          }}
                          title="Add to Canvas"
                        >
                          +
                        </button>
                      </div>
                      
                      <div className="note-card-content">
                        <p 
                          className="note-card-preview"
                          dangerouslySetInnerHTML={{
                            __html: highlightSearchMatches(contentPreview, debouncedSearchQuery)
                          }}
                        />
                      </div>

                      {obs_note.tags.length > 0 && (
                        <div className="note-card-tags">
                          {obs_note.tags.slice(0, viewMode === 'grid' ? 2 : 4).map(tag => (
                            <span key={tag} className="note-card-tag">
                              {tag.replace('#', '')}
                            </span>
                          ))}
                          {obs_note.tags.length > (viewMode === 'grid' ? 2 : 4) && (
                            <span className="note-card-tag-more">
                              +{obs_note.tags.length - (viewMode === 'grid' ? 2 : 4)}
                            </span>
                          )}
                        </div>
                      )}

                      <div className="note-card-meta">
                        <span className="note-card-path" title={obs_note.filePath || getFilePath(obs_note)}>
                          {getFilePath(obs_note)}
                        </span>
                        {obs_note.links.length > 0 && (
                          <span className="note-card-links">
                            {obs_note.links.length} links
                          </span>
                        )}
                      </div>
                    </div>
                  )
                })
              )}
            </div>
          </div>
        )}
      </div>
    </div>
  )
}

export default ObsidianVaultBrowser