import React, { useEffect, useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faClock, faSpinner } from '@fortawesome/free-solid-svg-icons'
import { Hooks } from 'applesauce-react'
import { Accounts } from 'applesauce-accounts'
import { NostrConnectSigner } from 'applesauce-signers'
import { RelayPool } from 'applesauce-relay'
import { Helpers, IEventStore } from 'applesauce-core'
import { nip19 } from 'nostr-tools'
import { getDefaultBunkerPermissions } from '../services/nostrConnect'
import { DebugBus, type DebugLogEntry } from '../utils/debugBus'
import ThreePaneLayout from './ThreePaneLayout'
import { KINDS } from '../config/kinds'
import type { NostrEvent } from '../services/bookmarkHelpers'
import { Bookmark } from '../types/bookmarks'
import { useBookmarksUI } from '../hooks/useBookmarksUI'
import { useSettings } from '../hooks/useSettings'
import { fetchHighlights, fetchHighlightsFromAuthors } from '../services/highlightService'
import { contactsController } from '../services/contactsController'
import { writingsController } from '../services/writingsController'
import { readingProgressController } from '../services/readingProgressController'
import { fetchBlogPostsFromAuthors, BlogPostPreview } from '../services/exploreService'

const defaultPayload = 'The quick brown fox jumps over the lazy dog.'

interface DebugProps {
  relayPool: RelayPool | null
  eventStore: IEventStore | null
  bookmarks: Bookmark[]
  bookmarksLoading: boolean
  onRefreshBookmarks: () => Promise<void>
  onLogout: () => void
}

const Debug: React.FC<DebugProps> = ({ 
  relayPool, 
  eventStore,
  bookmarks, 
  bookmarksLoading, 
  onRefreshBookmarks,
  onLogout 
}) => {
  const navigate = useNavigate()
  const activeAccount = Hooks.useActiveAccount()
  const accountManager = Hooks.useAccountManager()
  
  const { settings, saveSettings } = useSettings({
    relayPool,
    eventStore: eventStore!,
    pubkey: activeAccount?.pubkey,
    accountManager
  })
  
  const {
    isMobile,
    isCollapsed,
    setIsCollapsed,
    viewMode,
    setViewMode
  } = useBookmarksUI({ settings })
  const [payload, setPayload] = useState<string>(defaultPayload)
  const [cipher44, setCipher44] = useState<string>('')
  const [cipher04, setCipher04] = useState<string>('')
  const [plain44, setPlain44] = useState<string>('')
  const [plain04, setPlain04] = useState<string>('')
  const [tEncrypt44, setTEncrypt44] = useState<number | null>(null)
  const [tEncrypt04, setTEncrypt04] = useState<number | null>(null)
  const [tDecrypt44, setTDecrypt44] = useState<number | null>(null)
  const [tDecrypt04, setTDecrypt04] = useState<number | null>(null)
  const [logs, setLogs] = useState<DebugLogEntry[]>(DebugBus.snapshot())
  const [debugEnabled, setDebugEnabled] = useState<boolean>(() => localStorage.getItem('debug') === '*')
  
  // Bunker login state
  const [bunkerUri, setBunkerUri] = useState<string>('')
  const [isBunkerLoading, setIsBunkerLoading] = useState<boolean>(false)
  const [bunkerError, setBunkerError] = useState<string | null>(null)
  
  // Bookmark loading state
  const [bookmarkEvents, setBookmarkEvents] = useState<NostrEvent[]>([])
  const [isLoadingBookmarks, setIsLoadingBookmarks] = useState(false)
  const [bookmarkStats, setBookmarkStats] = useState<{ public: number; private: number } | null>(null)
  const [tLoadBookmarks, setTLoadBookmarks] = useState<number | null>(null)
  const [tDecryptBookmarks, setTDecryptBookmarks] = useState<number | null>(null)
  const [tFirstBookmark, setTFirstBookmark] = useState<number | null>(null)
  
  // Individual event decryption results
  const [decryptedEvents, setDecryptedEvents] = useState<Map<string, { public: number; private: number }>>(new Map())
  
  // Highlight loading state
  const [highlightMode, setHighlightMode] = useState<'article' | 'url' | 'author'>('author')
  const [highlightArticleCoord, setHighlightArticleCoord] = useState<string>('')
  const [highlightUrl, setHighlightUrl] = useState<string>('')
  const [highlightAuthor, setHighlightAuthor] = useState<string>('')
  const [isLoadingHighlights, setIsLoadingHighlights] = useState(false)
  const [highlightEvents, setHighlightEvents] = useState<NostrEvent[]>([])
  const [tLoadHighlights, setTLoadHighlights] = useState<number | null>(null)
  const [tFirstHighlight, setTFirstHighlight] = useState<number | null>(null)
  
  // Writings loading state
  const [isLoadingWritings, setIsLoadingWritings] = useState(false)
  const [writingPosts, setWritingPosts] = useState<BlogPostPreview[]>([])
  const [tLoadWritings, setTLoadWritings] = useState<number | null>(null)
  const [tFirstWriting, setTFirstWriting] = useState<number | null>(null)
  
  // Reading Progress loading state
  const [isLoadingReadingProgress, setIsLoadingReadingProgress] = useState(false)
  const [readingProgressEvents, setReadingProgressEvents] = useState<NostrEvent[]>([])
  const [tLoadReadingProgress, setTLoadReadingProgress] = useState<number | null>(null)
  const [tFirstReadingProgress, setTFirstReadingProgress] = useState<number | null>(null)
  
  // Mark-as-read reactions loading state
  const [isLoadingMarkAsRead, setIsLoadingMarkAsRead] = useState(false)
  const [markAsReadReactions, setMarkAsReadReactions] = useState<NostrEvent[]>([])
  const [tLoadMarkAsRead, setTLoadMarkAsRead] = useState<number | null>(null)
  const [tFirstMarkAsRead, setTFirstMarkAsRead] = useState<number | null>(null)

  // Relay list loading state
  const [isLoadingRelayList, setIsLoadingRelayList] = useState(false)
  const [relayListEvents, setRelayListEvents] = useState<NostrEvent[]>([])
  const [tLoadRelayList, setTLoadRelayList] = useState<number | null>(null)
  const [tFirstRelayList, setTFirstRelayList] = useState<number | null>(null)
  
  // Deduplicated reading progress from controller
  const [deduplicatedProgressMap, setDeduplicatedProgressMap] = useState<Map<string, number>>(new Map())
  
  // Live timing state
  const [liveTiming, setLiveTiming] = useState<{
    nip44?: { type: 'encrypt' | 'decrypt'; startTime: number }
    nip04?: { type: 'encrypt' | 'decrypt'; startTime: number }
    loadBookmarks?: { startTime: number }
    decryptBookmarks?: { startTime: number }
    loadHighlights?: { startTime: number }
    loadReadingProgress?: { startTime: number }
    loadMarkAsRead?: { startTime: number }
    loadRelayList?: { startTime: number }
  }>({})
  
  // Web of Trust state
  const [friendsPubkeys, setFriendsPubkeys] = useState<Set<string>>(new Set())
  const [friendsButtonLoading, setFriendsButtonLoading] = useState(false)

  useEffect(() => {
    return DebugBus.subscribe((e) => setLogs(prev => [...prev, e].slice(-300)))
  }, [])

  // Live timer effect - triggers re-renders for live timing updates
  useEffect(() => {
    const interval = setInterval(() => {
      // Force re-render to update live timing display
      setLiveTiming(prev => prev)
    }, 16) // ~60fps for smooth updates
    return () => clearInterval(interval)
  }, [])

  const signer = useMemo(() => (activeAccount as unknown as { signer?: unknown })?.signer, [activeAccount])
  const pubkey = (activeAccount as unknown as { pubkey?: string })?.pubkey

  const hasNip04 = typeof (signer as { nip04?: { encrypt?: unknown; decrypt?: unknown } } | undefined)?.nip04?.encrypt === 'function'
  const hasNip44 = typeof (signer as { nip44?: { encrypt?: unknown; decrypt?: unknown } } | undefined)?.nip44?.encrypt === 'function'

  const getKindName = (kind: number): string => {
    switch (kind) {
      case KINDS.ListSimple: return 'Simple List (10003)'
      case KINDS.ListReplaceable: return 'Replaceable List (30003)'
      case KINDS.List: return 'List (30001)'
      case KINDS.WebBookmark: return 'Web Bookmark (39701)'
      default: return `Kind ${kind}`
    }
  }

  const getEventSize = (evt: NostrEvent): number => {
    const content = evt.content || ''
    const tags = JSON.stringify(evt.tags || [])
    return content.length + tags.length
  }

  const hasEncryptedContent = (evt: NostrEvent): boolean => {
    // Check for NIP-44 encrypted content (detected by Helpers)
    if (Helpers.hasHiddenContent(evt)) return true
    
    // Check for NIP-04 encrypted content (base64 with ?iv= suffix)
    if (evt.content && evt.content.includes('?iv=')) return true
    
    // Check for encrypted tags
    if (Helpers.hasHiddenTags(evt) && !Helpers.isHiddenTagsUnlocked(evt)) return true
    
    return false
  }

  const getBookmarkCount = (evt: NostrEvent): { public: number; private: number } => {
    const publicTags = (evt.tags || []).filter((t: string[]) => t[0] === 'e' || t[0] === 'a')
    const hasEncrypted = hasEncryptedContent(evt)
    return {
      public: publicTags.length,
      private: hasEncrypted ? 1 : 0 // Can't know exact count until decrypted
    }
  }

  const formatBytes = (bytes: number): string => {
    if (bytes < 1024) return `${bytes} B`
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
    return `${(bytes / (1024 * 1024)).toFixed(2)} MB`
  }

  const getEventKey = (evt: NostrEvent): string => {
    if (evt.kind === 30003 || evt.kind === 30001) {
      // Replaceable: kind:pubkey:dtag
      const dTag = evt.tags?.find((t: string[]) => t[0] === 'd')?.[1] || ''
      return `${evt.kind}:${evt.pubkey}:${dTag}`
    } else if (evt.kind === 10003) {
      // Simple list: kind:pubkey
      return `${evt.kind}:${evt.pubkey}`
    }
    // Web bookmarks: use event id (no deduplication)
    return evt.id
  }

  const doEncrypt = async (mode: 'nip44' | 'nip04') => {
    if (!signer || !pubkey) return
    try {
      const api = (signer as { [key: string]: { encrypt: (pubkey: string, message: string) => Promise<string> } })[mode]
      DebugBus.info('debug', `encrypt start ${mode}`, { pubkey, len: payload.length })
      
      // Start live timing
      const start = performance.now()
      setLiveTiming(prev => ({ ...prev, [mode]: { type: 'encrypt', startTime: start } }))
      
      const cipher = await api.encrypt(pubkey, payload)
      const ms = Math.round(performance.now() - start)
      
      // Stop live timing
      setLiveTiming(prev => ({ ...prev, [mode]: undefined }))
      
      DebugBus.info('debug', `encrypt done ${mode}`, { len: typeof cipher === 'string' ? cipher.length : -1, ms })
      if (mode === 'nip44') setCipher44(cipher)
      else setCipher04(cipher)
      if (mode === 'nip44') setTEncrypt44(ms)
      else setTEncrypt04(ms)
    } catch (e) {
      // Stop live timing on error
      setLiveTiming(prev => ({ ...prev, [mode]: undefined }))
      DebugBus.error('debug', `encrypt error ${mode}`, e instanceof Error ? e.message : String(e))
    }
  }

  const doDecrypt = async (mode: 'nip44' | 'nip04') => {
    if (!signer || !pubkey) return
    try {
      const api = (signer as { [key: string]: { decrypt: (pubkey: string, ciphertext: string) => Promise<string> } })[mode]
      const cipher = mode === 'nip44' ? cipher44 : cipher04
      if (!cipher) {
        DebugBus.warn('debug', `no cipher to decrypt for ${mode}`)
        return
      }
      DebugBus.info('debug', `decrypt start ${mode}`, { len: cipher.length })
      
      // Start live timing
      const start = performance.now()
      setLiveTiming(prev => ({ ...prev, [mode]: { type: 'decrypt', startTime: start } }))
      
      const plain = await api.decrypt(pubkey, cipher)
      const ms = Math.round(performance.now() - start)
      
      // Stop live timing
      setLiveTiming(prev => ({ ...prev, [mode]: undefined }))
      
      DebugBus.info('debug', `decrypt done ${mode}`, { len: typeof plain === 'string' ? plain.length : -1, ms })
      if (mode === 'nip44') setPlain44(String(plain))
      else setPlain04(String(plain))
      if (mode === 'nip44') setTDecrypt44(ms)
      else setTDecrypt04(ms)
    } catch (e) {
      // Stop live timing on error
      setLiveTiming(prev => ({ ...prev, [mode]: undefined }))
      DebugBus.error('debug', `decrypt error ${mode}`, e instanceof Error ? e.message : String(e))
    }
  }

  const toggleDebug = () => {
    const next = !debugEnabled
    setDebugEnabled(next)
    if (next) localStorage.setItem('debug', '*')
    else localStorage.removeItem('debug')
  }

  const handleLoadBookmarks = async () => {
    if (!relayPool || !activeAccount) {
      DebugBus.warn('debug', 'Cannot load bookmarks: missing relayPool or activeAccount')
      return
    }

    try {
      setIsLoadingBookmarks(true)
      setBookmarkStats(null)
      setBookmarkEvents([]) // Clear existing events
      setDecryptedEvents(new Map())
      setTFirstBookmark(null)
      DebugBus.info('debug', 'Loading bookmark events...')

      // Start timing
      const start = performance.now()
      let firstEventTime: number | null = null
      setLiveTiming(prev => ({ ...prev, loadBookmarks: { startTime: start } }))

      // Import controller at runtime to avoid circular dependencies
      const { bookmarkController } = await import('../services/bookmarkController')
      
      // Subscribe to raw events for Debug UI display
      const unsubscribeRaw = bookmarkController.onRawEvent((evt) => {
        // Track time to first event
        if (firstEventTime === null) {
          firstEventTime = performance.now() - start
          setTFirstBookmark(Math.round(firstEventTime))
        }
        
        // Add event immediately with live deduplication
        setBookmarkEvents(prev => {
          const key = getEventKey(evt)
          const existingIdx = prev.findIndex(e => getEventKey(e) === key)
          
          if (existingIdx >= 0) {
            const existing = prev[existingIdx]
            if ((evt.created_at || 0) > (existing.created_at || 0)) {
              const newEvents = [...prev]
              newEvents[existingIdx] = evt
              return newEvents
            }
            return prev
          }
          
          return [...prev, evt]
        })
      })

      // Subscribe to decrypt complete events for Debug UI display
      const unsubscribeDecrypt = bookmarkController.onDecryptComplete((eventId, publicCount, privateCount) => {
        setDecryptedEvents(prev => new Map(prev).set(eventId, { 
          public: publicCount, 
          private: privateCount 
        }))
      })

      // Start the controller (triggers app bookmark population too)
      bookmarkController.reset()
      await bookmarkController.start({ relayPool, activeAccount, accountManager })
      
      // Clean up subscriptions
      unsubscribeRaw()
      unsubscribeDecrypt()

      const ms = Math.round(performance.now() - start)
      setLiveTiming(prev => ({ ...prev, loadBookmarks: undefined }))
      setTLoadBookmarks(ms)

      DebugBus.info('debug', `Loaded bookmark events`, { ms })
    } catch (error) {
      setLiveTiming(prev => ({ ...prev, loadBookmarks: undefined }))
      DebugBus.error('debug', 'Failed to load bookmarks', error instanceof Error ? error.message : String(error))
    } finally {
      setIsLoadingBookmarks(false)
    }
  }

  const handleClearBookmarks = () => {
    setBookmarkEvents([])
    setBookmarkStats(null)
    setTLoadBookmarks(null)
    setTDecryptBookmarks(null)
    setTFirstBookmark(null)
    setDecryptedEvents(new Map())
    DebugBus.info('debug', 'Cleared bookmark data')
  }

  const handleLoadHighlights = async () => {
    if (!relayPool) {
      DebugBus.warn('debug', 'Cannot load highlights: missing relayPool')
      return
    }

    // Default to logged-in user's highlights if no specific query provided
    const getValue = () => {
      if (highlightMode === 'article') return highlightArticleCoord.trim()
      if (highlightMode === 'url') return highlightUrl.trim()
      const authorValue = highlightAuthor.trim()
      return authorValue || pubkey || ''
    }

    const value = getValue()
    if (!value) {
      DebugBus.warn('debug', 'Please provide a value to query or log in')
      return
    }

    try {
      setIsLoadingHighlights(true)
      setHighlightEvents([])
      setTFirstHighlight(null)
      DebugBus.info('debug', `Loading highlights (${highlightMode}: ${value})...`)

      const start = performance.now()
      setLiveTiming(prev => ({ ...prev, loadHighlights: { startTime: start } }))

      let firstEventTime: number | null = null
      const seenIds = new Set<string>()

      // Import highlight services
      const { queryEvents } = await import('../services/dataFetch')
      const { KINDS } = await import('../config/kinds')

      // Build filter based on mode
      let filter: { kinds: number[]; '#a'?: string[]; '#r'?: string[]; authors?: string[] }
      if (highlightMode === 'article') {
        filter = { kinds: [KINDS.Highlights], '#a': [value] }
      } else if (highlightMode === 'url') {
        filter = { kinds: [KINDS.Highlights], '#r': [value] }
      } else {
        filter = { kinds: [KINDS.Highlights], authors: [value] }
      }

      const events = await queryEvents(relayPool, filter, {
        onEvent: (evt) => {
          if (seenIds.has(evt.id)) return
          seenIds.add(evt.id)

          if (firstEventTime === null) {
            firstEventTime = performance.now() - start
            setTFirstHighlight(Math.round(firstEventTime))
          }

          setHighlightEvents(prev => [...prev, evt])
        }
      })

      const elapsed = Math.round(performance.now() - start)
      setTLoadHighlights(elapsed)
      setLiveTiming(prev => ({ ...prev, loadHighlights: undefined }))

      DebugBus.info('debug', `Loaded ${events.length} highlight events in ${elapsed}ms`)
    } catch (err) {
      console.error('Failed to load highlights:', err)
      DebugBus.error('debug', `Failed to load highlights: ${err instanceof Error ? err.message : String(err)}`)
    } finally {
      setIsLoadingHighlights(false)
    }
  }

  const handleClearHighlights = () => {
    setHighlightEvents([])
    setTLoadHighlights(null)
    setTFirstHighlight(null)
    DebugBus.info('debug', 'Cleared highlight data')
  }

  const handleLoadMyHighlights = async () => {
    if (!relayPool || !activeAccount?.pubkey) {
      DebugBus.warn('debug', 'Please log in to load your highlights')
      return
    }
    const start = performance.now()
    setHighlightEvents([])
    setIsLoadingHighlights(true)
    setTLoadHighlights(null)
    setTFirstHighlight(null)
    DebugBus.info('debug', 'Loading my highlights...')
    try {
      let firstEventTime: number | null = null
      await fetchHighlights(relayPool, activeAccount.pubkey, (h) => {
        if (firstEventTime === null) {
          firstEventTime = performance.now() - start
          setTFirstHighlight(Math.round(firstEventTime))
        }
        setHighlightEvents(prev => {
          if (prev.some(x => x.id === h.id)) return prev
          const next = [...prev, { ...h, pubkey: h.pubkey, created_at: h.created_at, id: h.id, kind: 9802, tags: [], content: h.content, sig: '' } as NostrEvent]
          return next.sort((a, b) => b.created_at - a.created_at)
        })
      }, settings, false, eventStore || undefined)
    } finally {
      setIsLoadingHighlights(false)
      const elapsed = Math.round(performance.now() - start)
      setTLoadHighlights(elapsed)
      DebugBus.info('debug', `Loaded my highlights in ${elapsed}ms`)
    }
  }

  const handleLoadFriendsHighlights = async () => {
    if (!relayPool || !activeAccount?.pubkey) {
      DebugBus.warn('debug', 'Please log in to load friends highlights')
      return
    }
    
    // Get contacts from centralized controller (should already be loaded by App.tsx)
    const contacts = contactsController.getContacts()
    if (contacts.size === 0) {
      DebugBus.warn('debug', 'No friends found. Make sure you have contacts loaded.')
      return
    }
    
    const start = performance.now()
    setHighlightEvents([])
    setIsLoadingHighlights(true)
    setTLoadHighlights(null)
    setTFirstHighlight(null)
    DebugBus.info('debug', `Loading highlights from ${contacts.size} friends (using cached contacts)...`)
    
    let firstEventTime: number | null = null
    
    try {
      await fetchHighlightsFromAuthors(relayPool, Array.from(contacts), (h) => {
        if (firstEventTime === null) {
          firstEventTime = performance.now() - start
          setTFirstHighlight(Math.round(firstEventTime))
        }
        setHighlightEvents(prev => {
          if (prev.some(x => x.id === h.id)) return prev
          const next = [...prev, { ...h, pubkey: h.pubkey, created_at: h.created_at, id: h.id, kind: 9802, tags: [], content: h.content, sig: '' } as NostrEvent]
          return next.sort((a, b) => b.created_at - a.created_at)
        })
      }, eventStore || undefined)
    } finally {
      setIsLoadingHighlights(false)
      const elapsed = Math.round(performance.now() - start)
      setTLoadHighlights(elapsed)
      DebugBus.info('debug', `Loaded friends highlights in ${elapsed}ms`)
    }
  }

  const handleLoadNostrverseHighlights = async () => {
    if (!relayPool) {
      DebugBus.warn('debug', 'Relay pool not available')
      return
    }
    const start = performance.now()
    setHighlightEvents([])
    setIsLoadingHighlights(true)
    setTLoadHighlights(null)
    setTFirstHighlight(null)
    DebugBus.info('debug', 'Loading nostrverse highlights (kind:9802)...')
    try {
      let firstEventTime: number | null = null
      const seenIds = new Set<string>()
      const { queryEvents } = await import('../services/dataFetch')
      
      const events = await queryEvents(relayPool, { kinds: [9802], limit: 500 }, {
        onEvent: (evt) => {
          if (seenIds.has(evt.id)) return
          seenIds.add(evt.id)
          if (firstEventTime === null) {
            firstEventTime = performance.now() - start
            setTFirstHighlight(Math.round(firstEventTime))
          }
          setHighlightEvents(prev => [...prev, evt])
        }
      })
      
      DebugBus.info('debug', `Loaded ${events.length} nostrverse highlights`)
    } finally {
      setIsLoadingHighlights(false)
      const elapsed = Math.round(performance.now() - start)
      setTLoadHighlights(elapsed)
      DebugBus.info('debug', `Loaded nostrverse highlights in ${elapsed}ms`)
    }
  }

  const handleLoadMyWritings = async () => {
    if (!relayPool || !activeAccount?.pubkey || !eventStore) {
      DebugBus.warn('debug', 'Please log in to load your writings')
      return
    }
    const start = performance.now()
    setWritingPosts([])
    setIsLoadingWritings(true)
    setTLoadWritings(null)
    setTFirstWriting(null)
    DebugBus.info('debug', 'Loading my writings via writingsController...')
    try {
      let firstEventTime: number | null = null
      const unsub = writingsController.onWritings((posts) => {
        if (firstEventTime === null && posts.length > 0) {
          firstEventTime = performance.now() - start
          setTFirstWriting(Math.round(firstEventTime))
        }
        setWritingPosts(posts)
      })
      
      await writingsController.start({
        relayPool,
        eventStore,
        pubkey: activeAccount.pubkey,
        force: true
      })
      
      unsub()
      const currentWritings = writingsController.getWritings()
      setWritingPosts(currentWritings)
      DebugBus.info('debug', `Loaded ${currentWritings.length} writings via controller`)
    } finally {
      setIsLoadingWritings(false)
      const elapsed = Math.round(performance.now() - start)
      setTLoadWritings(elapsed)
      DebugBus.info('debug', `Loaded my writings in ${elapsed}ms`)
    }
  }

  const handleLoadFriendsWritings = async () => {
    if (!relayPool || !activeAccount?.pubkey) {
      DebugBus.warn('debug', 'Please log in to load friends writings')
      return
    }
    const start = performance.now()
    setWritingPosts([])
    setIsLoadingWritings(true)
    setTLoadWritings(null)
    setTFirstWriting(null)
    DebugBus.info('debug', 'Loading friends writings...')
    try {
      // Get contacts first
      await contactsController.start({ relayPool, pubkey: activeAccount.pubkey })
      const friends = contactsController.getContacts()
      const friendsArray = Array.from(friends)
      DebugBus.info('debug', `Found ${friendsArray.length} friends`)
      
      if (friendsArray.length === 0) {
        DebugBus.warn('debug', 'No friends found to load writings from')
        return
      }
      
      let firstEventTime: number | null = null
      const relayUrls = Array.from(relayPool.relays.values()).map(relay => relay.url)
      const posts = await fetchBlogPostsFromAuthors(
        relayPool,
        friendsArray,
        relayUrls,
        (post) => {
          if (firstEventTime === null) {
            firstEventTime = performance.now() - start
            setTFirstWriting(Math.round(firstEventTime))
          }
          setWritingPosts(prev => {
            const dTag = post.event.tags.find(t => t[0] === 'd')?.[1] || ''
            const key = `${post.author}:${dTag}`
            const exists = prev.find(p => {
              const pDTag = p.event.tags.find(t => t[0] === 'd')?.[1] || ''
              return `${p.author}:${pDTag}` === key
            })
            if (exists) return prev
            return [...prev, post].sort((a, b) => {
              const timeA = a.published || a.event.created_at
              const timeB = b.published || b.event.created_at
              return timeB - timeA
            })
          })
        }
      )
      
      setWritingPosts(posts)
      DebugBus.info('debug', `Loaded ${posts.length} friend writings`)
    } finally {
      setIsLoadingWritings(false)
      const elapsed = Math.round(performance.now() - start)
      setTLoadWritings(elapsed)
      DebugBus.info('debug', `Loaded friend writings in ${elapsed}ms`)
    }
  }

  const handleLoadNostrverseWritings = async () => {
    if (!relayPool) {
      DebugBus.warn('debug', 'Relay pool not available')
      return
    }
    const start = performance.now()
    setWritingPosts([])
    setIsLoadingWritings(true)
    setTLoadWritings(null)
    setTFirstWriting(null)
    DebugBus.info('debug', 'Loading nostrverse writings (kind:30023)...')
    try {
      let firstEventTime: number | null = null
      const relayUrls = Array.from(relayPool.relays.values()).map(relay => relay.url)
      
      const { queryEvents } = await import('../services/dataFetch')
      const { Helpers } = await import('applesauce-core')
      const { getArticleTitle, getArticleSummary, getArticleImage, getArticlePublished } = Helpers
      
      const uniqueEvents = new Map<string, NostrEvent>()
      await queryEvents(relayPool, { kinds: [30023], limit: 50 }, {
        relayUrls,
        onEvent: (evt) => {
          const dTag = evt.tags.find(t => t[0] === 'd')?.[1] || ''
          const key = `${evt.pubkey}:${dTag}`
          const existing = uniqueEvents.get(key)
          if (!existing || evt.created_at > existing.created_at) {
            uniqueEvents.set(key, evt)
            
            if (firstEventTime === null) {
              firstEventTime = performance.now() - start
              setTFirstWriting(Math.round(firstEventTime))
            }
            
            const posts = Array.from(uniqueEvents.values()).map(event => ({
              event,
              title: getArticleTitle(event) || 'Untitled',
              summary: getArticleSummary(event),
              image: getArticleImage(event),
              published: getArticlePublished(event),
              author: event.pubkey
            } as BlogPostPreview)).sort((a, b) => {
              const timeA = a.published || a.event.created_at
              const timeB = b.published || b.event.created_at
              return timeB - timeA
            })
            
            setWritingPosts(posts)
          }
        }
      })
      
      const finalPosts = Array.from(uniqueEvents.values()).map(event => ({
        event,
        title: getArticleTitle(event) || 'Untitled',
        summary: getArticleSummary(event),
        image: getArticleImage(event),
        published: getArticlePublished(event),
        author: event.pubkey
      } as BlogPostPreview)).sort((a, b) => {
        const timeA = a.published || a.event.created_at
        const timeB = b.published || b.event.created_at
        return timeB - timeA
      })
      
      setWritingPosts(finalPosts)
      DebugBus.info('debug', `Loaded ${finalPosts.length} nostrverse writings`)
    } finally {
      setIsLoadingWritings(false)
      const elapsed = Math.round(performance.now() - start)
      setTLoadWritings(elapsed)
      DebugBus.info('debug', `Loaded nostrverse writings in ${elapsed}ms`)
    }
  }

  const handleClearWritings = () => {
    setWritingPosts([])
    setTLoadWritings(null)
    setTFirstWriting(null)
  }

  const handleLoadReadingProgress = async () => {
    if (!relayPool || !eventStore || !activeAccount?.pubkey) {
      DebugBus.warn('debug', 'Please log in to load reading progress')
      return
    }

    try {
      setIsLoadingReadingProgress(true)
      setReadingProgressEvents([])
      setTLoadReadingProgress(null)
      setTFirstReadingProgress(null)
      setDeduplicatedProgressMap(new Map())
      DebugBus.info('debug', 'Loading reading progress events...')

      const start = performance.now()
      let firstEventTime: number | null = null
      setLiveTiming(prev => ({ ...prev, loadReadingProgress: { startTime: start } }))

      const { queryEvents } = await import('../services/dataFetch')
      const { KINDS } = await import('../config/kinds')

      // Load raw events for display
      const rawEvents: NostrEvent[] = []
      const rawQueryPromise = queryEvents(relayPool, { kinds: [KINDS.ReadingProgress], authors: [activeAccount.pubkey] }, {
        onEvent: (evt) => {
          if (firstEventTime === null) {
            firstEventTime = performance.now() - start
            setTFirstReadingProgress(Math.round(firstEventTime))
          }
          rawEvents.push(evt)
          setReadingProgressEvents([...rawEvents])
        }
      })

      // Load deduplicated results via controller
      const unsubProgress = readingProgressController.onProgress((progressMap) => {
        setDeduplicatedProgressMap(new Map(progressMap))
      })

      // Run both in parallel
      await Promise.all([
        rawQueryPromise,
        readingProgressController.start({ relayPool, eventStore, pubkey: activeAccount.pubkey, force: true })
      ])
      
      unsubProgress()

      const elapsed = Math.round(performance.now() - start)
      setTLoadReadingProgress(elapsed)
      setLiveTiming(prev => ({ ...prev, loadReadingProgress: undefined }))

      const finalMap = readingProgressController.getProgressMap()
      DebugBus.info('debug', `Loaded ${rawEvents.length} raw events, deduplicated to ${finalMap.size} articles in ${elapsed}ms`)
    } catch (err) {
      console.error('Failed to load reading progress:', err)
      DebugBus.error('debug', `Failed to load reading progress: ${err instanceof Error ? err.message : String(err)}`)
    } finally {
      setIsLoadingReadingProgress(false)
    }
  }

  const handleClearReadingProgress = () => {
    setReadingProgressEvents([])
    setTLoadReadingProgress(null)
    setTFirstReadingProgress(null)
    setDeduplicatedProgressMap(new Map())
    DebugBus.info('debug', 'Cleared reading progress data')
  }

  const handleLoadMarkAsReadReactions = async () => {
    if (!relayPool || !activeAccount?.pubkey) {
      DebugBus.warn('debug', 'Please log in to load mark-as-read reactions')
      return
    }

    try {
      setIsLoadingMarkAsRead(true)
      setMarkAsReadReactions([])
      setTLoadMarkAsRead(null)
      setTFirstMarkAsRead(null)
      DebugBus.info('debug', 'Loading mark-as-read reactions...')

      const start = performance.now()
      let firstEventTime: number | null = null
      setLiveTiming(prev => ({ ...prev, loadMarkAsRead: { startTime: start } }))

      const { queryEvents } = await import('../services/dataFetch')
      const { ARCHIVE_EMOJI } = await import('../services/reactionService')

      // Load both kind:7 (reactions to events) and kind:17 (reactions to URLs)
      const [kind7Events, kind17Events] = await Promise.all([
        queryEvents(relayPool, { kinds: [7], authors: [activeAccount.pubkey] }, {
          onEvent: (evt) => {
            if (evt.content === ARCHIVE_EMOJI) {
              if (firstEventTime === null) {
                firstEventTime = performance.now() - start
                setTFirstMarkAsRead(Math.round(firstEventTime))
              }
              setMarkAsReadReactions(prev => [...prev, evt])
            }
          }
        }),
        queryEvents(relayPool, { kinds: [17], authors: [activeAccount.pubkey] }, {
          onEvent: (evt) => {
            if (evt.content === ARCHIVE_EMOJI) {
              if (firstEventTime === null) {
                firstEventTime = performance.now() - start
                setTFirstMarkAsRead(Math.round(firstEventTime))
              }
              setMarkAsReadReactions(prev => [...prev, evt])
            }
          }
        })
      ])

      const totalEvents = kind7Events.length + kind17Events.length
      const elapsed = Math.round(performance.now() - start)
      setTLoadMarkAsRead(elapsed)
      setLiveTiming(prev => ({ ...prev, loadMarkAsRead: undefined }))

      DebugBus.info('debug', `Loaded ${totalEvents} mark-as-read reactions in ${elapsed}ms`)
    } catch (err) {
      console.error('Failed to load mark-as-read reactions:', err)
      DebugBus.error('debug', `Failed to load mark-as-read reactions: ${err instanceof Error ? err.message : String(err)}`)
    } finally {
      setIsLoadingMarkAsRead(false)
    }
  }

  const handleClearMarkAsRead = () => {
    setMarkAsReadReactions([])
    setTLoadMarkAsRead(null)
    setTFirstMarkAsRead(null)
    DebugBus.info('debug', 'Cleared mark-as-read reactions data')
  }

  const handleLoadRelayList = async () => {
    if (!relayPool || !activeAccount?.pubkey) {
      DebugBus.warn('debug', 'Please log in to load relay list')
      return
    }

    try {
      setIsLoadingRelayList(true)
      setRelayListEvents([])
      setTLoadRelayList(null)
      setTFirstRelayList(null)
      DebugBus.info('debug', 'Loading relay list (kind 10002)...')

      const start = performance.now()
      let firstEventTime: number | null = null
      setLiveTiming(prev => ({ ...prev, loadRelayList: { startTime: start } }))

      const { queryEvents } = await import('../services/dataFetch')

      // Query for kind:10002 (relay list)
      const events = await queryEvents(relayPool, { 
        kinds: [10002], 
        authors: [activeAccount.pubkey],
        limit: 10
      }, {
        onEvent: (evt) => {
          if (firstEventTime === null) {
            firstEventTime = performance.now() - start
            setTFirstRelayList(Math.round(firstEventTime))
          }
          setRelayListEvents(prev => [...prev, evt])
        }
      })
      
      const elapsed = Math.round(performance.now() - start)
      setTLoadRelayList(elapsed)
      setLiveTiming(prev => ({ ...prev, loadRelayList: undefined }))

      DebugBus.info('debug', `Loaded ${events.length} relay list events in ${elapsed}ms`)
      
      // Log details about the events
      events.forEach((event, index) => {
        const relayCount = event.tags.filter(tag => tag[0] === 'r').length
        DebugBus.info('debug', `Event ${index + 1}: ${relayCount} relays, created ${new Date(event.created_at * 1000).toISOString()}`)
      })
    } catch (err) {
      console.error('Failed to load relay list:', err)
      DebugBus.error('debug', `Failed to load relay list: ${err instanceof Error ? err.message : String(err)}`)
    } finally {
      setIsLoadingRelayList(false)
    }
  }

  const handleClearRelayList = () => {
    setRelayListEvents([])
    setTLoadRelayList(null)
    setTFirstRelayList(null)
    DebugBus.info('debug', 'Cleared relay list data')
  }

  const handleLoadFriendsList = async () => {
    if (!relayPool || !activeAccount?.pubkey) {
      DebugBus.warn('debug', 'Please log in to load friends list')
      return
    }
    
    setFriendsButtonLoading(true)
    DebugBus.info('debug', 'Loading friends list via controller...')
    
    // Clear current list
    setFriendsPubkeys(new Set())
    
    // Subscribe to controller updates to see streaming
    const unsubscribe = contactsController.onContacts((contacts) => {
      setFriendsPubkeys(new Set(contacts))
    })
    
    try {
      // Force reload to see streaming behavior
      await contactsController.start({ relayPool, pubkey: activeAccount.pubkey, force: true })
      const final = contactsController.getContacts()
      setFriendsPubkeys(new Set(final))
      DebugBus.info('debug', `Loaded ${final.size} friends from controller`)
    } catch (err) {
      console.error('[debug] Failed to load friends:', err)
      DebugBus.error('debug', `Failed to load friends: ${err instanceof Error ? err.message : String(err)}`)
    } finally {
      unsubscribe()
      setFriendsButtonLoading(false)
    }
  }

  const friendsNpubs = useMemo(() => {
    return Array.from(friendsPubkeys).map(pk => nip19.npubEncode(pk))
  }, [friendsPubkeys])

  const handleBunkerLogin = async () => {
    if (!bunkerUri.trim()) {
      setBunkerError('Please enter a bunker URI')
      return
    }

    if (!bunkerUri.startsWith('bunker://')) {
      setBunkerError('Invalid bunker URI. Must start with bunker://')
      return
    }

    try {
      setIsBunkerLoading(true)
      setBunkerError(null)
      
      // Create signer from bunker URI with default permissions
      const permissions = getDefaultBunkerPermissions()
      const signer = await NostrConnectSigner.fromBunkerURI(bunkerUri, { permissions })
      
      // Get pubkey from signer
      const pubkey = await signer.getPublicKey()
      
      // Create account from signer
      const account = new Accounts.NostrConnectAccount(pubkey, signer)
      
      // Add to account manager and set active
      accountManager.addAccount(account)
      accountManager.setActive(account)
      
      // Clear input on success
      setBunkerUri('')
    } catch (err) {
      console.error('[bunker] Login failed:', err)
      const errorMessage = err instanceof Error ? err.message : 'Failed to connect to bunker'
      
      // Check for permission-related errors
      if (errorMessage.toLowerCase().includes('permission') || errorMessage.toLowerCase().includes('unauthorized')) {
        setBunkerError('Your bunker connection is missing signing permissions. Reconnect and approve signing.')
      } else {
        setBunkerError(errorMessage)
      }
    } finally {
      setIsBunkerLoading(false)
    }
  }

  const CodeBox = ({ value }: { value: string }) => (
    <div className="h-20 overflow-y-auto font-mono text-xs leading-relaxed p-2 bg-gray-100 dark:bg-gray-800 rounded whitespace-pre-wrap break-all">
      {value || '—'}
    </div>
  )

  const getLiveTiming = (mode: 'nip44' | 'nip04', type: 'encrypt' | 'decrypt') => {
    const timing = liveTiming[mode]
    if (timing && timing.type === type) {
      const elapsed = Math.round(performance.now() - timing.startTime)
      return elapsed
    }
    return null
  }

  const getBookmarkLiveTiming = (operation: 'loadBookmarks' | 'decryptBookmarks' | 'loadHighlights') => {
    const timing = liveTiming[operation]
    if (timing) {
      const elapsed = Math.round(performance.now() - timing.startTime)
      return elapsed
    }
    return null
  }

  const Stat = ({ label, value, mode, type, bookmarkOp }: { 
    label: string; 
    value?: string | number | null;
    mode?: 'nip44' | 'nip04';
    type?: 'encrypt' | 'decrypt';
    bookmarkOp?: 'loadBookmarks' | 'decryptBookmarks' | 'loadHighlights';
  }) => {
    const liveValue = bookmarkOp ? getBookmarkLiveTiming(bookmarkOp) : (mode && type ? getLiveTiming(mode, type) : null)
    const isLive = !!liveValue
    
    let displayValue: string
    if (isLive) {
      displayValue = ''
    } else if (value !== null && value !== undefined) {
      displayValue = `${value}ms`
    } else {
      displayValue = '—'
    }
    
    return (
      <span className="badge" style={{ marginRight: 8 }}>
        <FontAwesomeIcon icon={faClock} style={{ marginRight: 4, fontSize: '0.8em' }} />
        {label}: {isLive ? (
          <FontAwesomeIcon icon={faSpinner} className="animate-spin" style={{ fontSize: '0.8em' }} />
        ) : (
          displayValue
        )}
      </span>
    )
  }

  const debugContent = (
    <div className="settings-view">
      <div className="settings-header">
        <h2>Debug</h2>
        <div className="settings-header-actions">
          <span className="opacity-70">Active pubkey:</span> <code className="text-sm">{pubkey || 'none'}</code>
        </div>
      </div>

      <div className="settings-content">

        {/* Account Connection Section */}
        <div className="settings-section">
          <h3 className="section-title">
            {activeAccount 
              ? activeAccount.type === 'extension' 
                ? 'Browser Extension' 
                : activeAccount.type === 'nostr-connect'
                ? 'Bunker Connection'
                : 'Account Connection'
              : 'Account Connection'}
          </h3>
          {!activeAccount ? (
            <div>
              <div className="text-sm opacity-70 mb-3">Connect to your bunker (Nostr Connect signer) to enable encryption/decryption testing</div>
              <div className="flex gap-2 mb-3">
                <input
                  type="text"
                  className="input flex-1"
                  placeholder="bunker://..."
                  value={bunkerUri}
                  onChange={(e) => setBunkerUri(e.target.value)}
                  disabled={isBunkerLoading}
                />
                <button 
                  className="btn btn-primary" 
                  onClick={handleBunkerLogin}
                  disabled={isBunkerLoading || !bunkerUri.trim()}
                >
                  {isBunkerLoading ? 'Connecting...' : 'Connect'}
                </button>
              </div>
              {bunkerError && (
                <div className="text-sm text-red-600 dark:text-red-400 mb-2">{bunkerError}</div>
              )}
            </div>
          ) : (
            <div className="flex items-center justify-between">
              <div>
                <div className="text-sm opacity-70">
                  {activeAccount.type === 'extension' 
                    ? 'Connected via browser extension' 
                    : activeAccount.type === 'nostr-connect'
                    ? 'Connected to bunker'
                    : 'Connected'}
                </div>
                <div className="text-sm font-mono">{pubkey}</div>
              </div>
                  <button
                    className="btn"
                    style={{ 
                      background: 'rgb(220 38 38)', 
                      color: 'white', 
                      border: '1px solid rgb(220 38 38)',
                      padding: '0.75rem 1.5rem',
                      borderRadius: '6px',
                      fontSize: '1rem',
                      cursor: 'pointer',
                      transition: 'background-color 0.2s'
                    }}
                    onMouseEnter={(e) => e.currentTarget.style.background = 'rgb(185 28 28)'}
                    onMouseLeave={(e) => e.currentTarget.style.background = 'rgb(220 38 38)'}
                    onClick={() => accountManager.removeAccount(activeAccount)}
                  >
                    Disconnect
                  </button>
            </div>
          )}
        </div>

        {/* Encryption Tools Section */}
        <div className="settings-section">
          <h3 className="section-title">Encryption Tools</h3>
          <div className="setting-group">
            <label className="setting-label">Payload</label>
                <textarea 
                  className="textarea w-full bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700" 
                  value={payload} 
                  onChange={e => setPayload(e.target.value)} 
                  rows={3} 
                />
            <div className="flex gap-2 mt-3 justify-end">
              <button className="btn btn-secondary" onClick={() => setPayload(defaultPayload)}>Reset</button>
              <button className="btn btn-secondary" onClick={() => { setCipher44(''); setCipher04(''); setPlain44(''); setPlain04(''); setTEncrypt44(null); setTEncrypt04(null); setTDecrypt44(null); setTDecrypt04(null) }}>Clear</button>
            </div>
          </div>
          
          <div className="grid" style={{ gap: 12, gridTemplateColumns: 'minmax(0,1fr) minmax(0,1fr)' }}>
            <div className="setting-group">
              <label className="setting-label">NIP-44</label>
              <div className="flex gap-2 mb-3">
                <button className="btn btn-primary" onClick={() => doEncrypt('nip44')} disabled={!hasNip44}>Encrypt</button>
                <button className="btn btn-secondary" onClick={() => doDecrypt('nip44')} disabled={!cipher44}>Decrypt</button>
              </div>
              <label className="block text-sm opacity-70 mb-2">Encrypted:</label>
              <CodeBox value={cipher44} />
              <div className="mt-3">
                <span className="text-sm opacity-70">Plain:</span>
                <CodeBox value={plain44} />
              </div>
            </div>

            <div className="setting-group">
              <label className="setting-label">NIP-04</label>
              <div className="flex gap-2 mb-3">
                <button className="btn btn-primary" onClick={() => doEncrypt('nip04')} disabled={!hasNip04}>Encrypt</button>
                <button className="btn btn-secondary" onClick={() => doDecrypt('nip04')} disabled={!cipher04}>Decrypt</button>
              </div>
              <label className="block text-sm opacity-70 mb-2">Encrypted:</label>
              <CodeBox value={cipher04} />
              <div className="mt-3">
                <span className="text-sm opacity-70">Plain:</span>
                <CodeBox value={plain04} />
              </div>
            </div>
          </div>
        </div>

        {/* Performance Timing Section */}
        <div className="settings-section">
          <h3 className="section-title">Performance Timing</h3>
          <div className="text-sm opacity-70 mb-3">Encryption and decryption operation durations</div>
          <div className="grid" style={{ gap: 12, gridTemplateColumns: 'minmax(0,1fr) minmax(0,1fr)' }}>
            <div className="setting-group">
              <label className="setting-label">NIP-44</label>
              <div className="flex flex-wrap items-center gap-2">
                <Stat label="enc" value={tEncrypt44} mode="nip44" type="encrypt" />
                <Stat label="dec" value={tDecrypt44} mode="nip44" type="decrypt" />
              </div>
            </div>
            <div className="setting-group">
              <label className="setting-label">NIP-04</label>
              <div className="flex flex-wrap items-center gap-2">
                <Stat label="enc" value={tEncrypt04} mode="nip04" type="encrypt" />
                <Stat label="dec" value={tDecrypt04} mode="nip04" type="decrypt" />
              </div>
            </div>
          </div>
        </div>

        {/* Bookmark Loading Section */}
        <div className="settings-section">
          <h3 className="section-title">Bookmark Loading</h3>
          <div className="text-sm opacity-70 mb-3">Test bookmark loading with auto-decryption (kinds: 10003, 30003, 30001, 39701)</div>
          
          <div className="flex gap-2 mb-3 items-center">
            <button 
              className="btn btn-primary" 
              onClick={handleLoadBookmarks}
              disabled={isLoadingBookmarks || !relayPool || !activeAccount}
            >
              {isLoadingBookmarks ? (
                <>
                  <FontAwesomeIcon icon={faSpinner} className="animate-spin mr-2" />
                  Loading...
                </>
              ) : (
                'Load Bookmarks'
              )}
            </button>
            <button 
              className="btn btn-secondary ml-auto" 
              onClick={handleClearBookmarks}
              disabled={bookmarkEvents.length === 0 && !bookmarkStats}
            >
              Clear
            </button>
          </div>

          <div className="mb-3 flex gap-2 flex-wrap">
            <Stat label="total" value={tLoadBookmarks} bookmarkOp="loadBookmarks" />
            <Stat label="first event" value={tFirstBookmark} />
            <Stat label="decrypt" value={tDecryptBookmarks} bookmarkOp="decryptBookmarks" />
          </div>

          {bookmarkStats && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Decrypted Bookmarks:</div>
              <div className="font-mono text-xs p-2 bg-gray-100 dark:bg-gray-800 rounded">
                <div>Public: {bookmarkStats.public}</div>
                <div>Private: {bookmarkStats.private}</div>
                <div className="font-semibold mt-1">Total: {bookmarkStats.public + bookmarkStats.private}</div>
              </div>
            </div>
          )}

          {bookmarkEvents.length > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Loaded Events ({bookmarkEvents.length}):</div>
              <div className="space-y-2">
                {bookmarkEvents.map((evt, idx) => {
                  const dTag = evt.tags?.find((t: string[]) => t[0] === 'd')?.[1]
                  const titleTag = evt.tags?.find((t: string[]) => t[0] === 'title')?.[1]
                  const size = getEventSize(evt)
                  const counts = getBookmarkCount(evt)
                  const hasEncrypted = hasEncryptedContent(evt)
                  const decryptResult = decryptedEvents.get(evt.id)
                  
                  return (
                    <div key={idx} className="font-mono text-xs p-2 bg-gray-100 dark:bg-gray-800 rounded">
                      <div className="font-semibold mb-1">{getKindName(evt.kind)}</div>
                      {dTag && <div className="opacity-70">d-tag: {dTag}</div>}
                      {titleTag && <div className="opacity-70">title: {titleTag}</div>}
                      <div className="mt-1">
                        <div>Size: {formatBytes(size)}</div>
                        <div>Public: {counts.public}</div>
                        {hasEncrypted && <div>🔒 Has encrypted content</div>}
                      </div>
                      {decryptResult && (
                        <div className="mt-1 text-[11px] opacity-80">
                          <div>✓ Decrypted: {decryptResult.public} public, {decryptResult.private} private</div>
                        </div>
                      )}
                      <div className="opacity-50 mt-1 text-[10px] break-all">ID: {evt.id}</div>
                    </div>
                  )
                })}
              </div>
            </div>
          )}
        </div>

        {/* Highlight Loading Section */}
        <div className="settings-section">
          <h3 className="section-title">Highlight Loading</h3>
          <div className="text-sm opacity-70 mb-3">Test highlight loading with EOSE-based queryEvents (kind: 9802). Author mode defaults to your highlights.</div>
          
          <div className="mb-3">
            <div className="text-sm opacity-70 mb-2">Query Mode:</div>
            <div className="flex gap-2">
              <label className="flex items-center gap-2 cursor-pointer">
                <input
                  type="radio"
                  checked={highlightMode === 'article'}
                  onChange={() => setHighlightMode('article')}
                />
                <span>Article (#a)</span>
              </label>
              <label className="flex items-center gap-2 cursor-pointer">
                <input
                  type="radio"
                  checked={highlightMode === 'url'}
                  onChange={() => setHighlightMode('url')}
                />
                <span>URL (#r)</span>
              </label>
              <label className="flex items-center gap-2 cursor-pointer">
                <input
                  type="radio"
                  checked={highlightMode === 'author'}
                  onChange={() => setHighlightMode('author')}
                />
                <span>Author</span>
              </label>
            </div>
          </div>

          <div className="mb-3">
            {highlightMode === 'article' && (
              <input
                type="text"
                className="input w-full"
                placeholder="30023:pubkey:identifier"
                value={highlightArticleCoord}
                onChange={(e) => setHighlightArticleCoord(e.target.value)}
                disabled={isLoadingHighlights}
              />
            )}
            {highlightMode === 'url' && (
              <input
                type="text"
                className="input w-full"
                placeholder="https://example.com/article"
                value={highlightUrl}
                onChange={(e) => setHighlightUrl(e.target.value)}
                disabled={isLoadingHighlights}
              />
            )}
            {highlightMode === 'author' && (
              <input
                type="text"
                className="input w-full"
                placeholder={pubkey ? `${pubkey.slice(0, 16)}... (logged-in user)` : 'pubkey (hex)'}
                value={highlightAuthor}
                onChange={(e) => setHighlightAuthor(e.target.value)}
                disabled={isLoadingHighlights}
              />
            )}
          </div>

          <div className="flex gap-2 mb-3 items-center">
            <button 
              className="btn btn-primary" 
              onClick={handleLoadHighlights}
              disabled={isLoadingHighlights || !relayPool}
            >
              {isLoadingHighlights ? (
                <>
                  <FontAwesomeIcon icon={faSpinner} className="animate-spin mr-2" />
                  Loading...
                </>
              ) : (
                'Load Highlights'
              )}
            </button>
            <button 
              className="btn btn-secondary ml-auto" 
              onClick={handleClearHighlights}
              disabled={highlightEvents.length === 0}
            >
              Clear
            </button>
          </div>

          <div className="mb-3 text-sm opacity-70">Quick load options:</div>
          <div className="flex gap-2 mb-3 flex-wrap">
            <button 
              className="btn btn-secondary text-sm" 
              onClick={handleLoadMyHighlights}
              disabled={isLoadingHighlights || !relayPool || !activeAccount}
            >
              Load My Highlights
            </button>
            <button 
              className="btn btn-secondary text-sm" 
              onClick={handleLoadFriendsHighlights}
              disabled={isLoadingHighlights || !relayPool || !activeAccount}
            >
              Load Friends Highlights
            </button>
            <button 
              className="btn btn-secondary text-sm" 
              onClick={handleLoadNostrverseHighlights}
              disabled={isLoadingHighlights || !relayPool}
            >
              Load Nostrverse Highlights
            </button>
          </div>

          <div className="mb-3 flex gap-2 flex-wrap">
            <Stat label="total" value={tLoadHighlights} bookmarkOp="loadHighlights" />
            <Stat label="first event" value={tFirstHighlight} />
          </div>

          {highlightEvents.length > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Loaded Highlights ({highlightEvents.length}):</div>
              <div className="space-y-2 max-h-96 overflow-y-auto">
                {highlightEvents.map((evt, idx) => {
                  const content = evt.content || ''
                  const shortContent = content.length > 100 ? content.substring(0, 100) + '...' : content
                  const aTag = evt.tags?.find((t: string[]) => t[0] === 'a')?.[1]
                  const rTag = evt.tags?.find((t: string[]) => t[0] === 'r')?.[1]
                  const eTag = evt.tags?.find((t: string[]) => t[0] === 'e')?.[1]
                  const contextTag = evt.tags?.find((t: string[]) => t[0] === 'context')?.[1]
                  
                  return (
                    <div key={idx} className="font-mono text-xs p-2 bg-gray-100 dark:bg-gray-800 rounded">
                      <div className="font-semibold mb-1">Highlight #{idx + 1}</div>
                      <div className="opacity-70 mb-1">
                        <div>Author: {evt.pubkey.slice(0, 16)}...</div>
                        <div>Created: {new Date(evt.created_at * 1000).toLocaleString()}</div>
                      </div>
                      <div className="mt-1">
                        <div className="font-semibold text-[11px]">Content:</div>
                        <div className="italic">&quot;{shortContent}&quot;</div>
                      </div>
                      {contextTag && (
                        <div className="mt-1 text-[11px] opacity-70">
                          <div>Context: {contextTag.substring(0, 60)}...</div>
                        </div>
                      )}
                      {aTag && <div className="mt-1 text-[11px] opacity-70">#a: {aTag}</div>}
                      {rTag && <div className="mt-1 text-[11px] opacity-70">#r: {rTag}</div>}
                      {eTag && <div className="mt-1 text-[11px] opacity-70">#e: {eTag.slice(0, 16)}...</div>}
                      <div className="opacity-50 mt-1 text-[10px] break-all">ID: {evt.id}</div>
                    </div>
                  )
                })}
              </div>
            </div>
          )}
        </div>

        {/* Writings Loading Section */}
        <div className="settings-section">
          <h3 className="section-title">Writings Loading</h3>
          
          <div className="mb-3 text-sm opacity-70">Quick load options:</div>
          <div className="flex gap-2 mb-3 flex-wrap">
            <button 
              className="btn btn-secondary text-sm" 
              onClick={handleLoadMyWritings}
              disabled={isLoadingWritings || !relayPool || !activeAccount || !eventStore}
            >
              {isLoadingWritings ? (
                <FontAwesomeIcon icon={faSpinner} className="animate-spin" />
              ) : (
                'Load My Writings'
              )}
            </button>
            <button 
              className="btn btn-secondary text-sm" 
              onClick={handleLoadFriendsWritings}
              disabled={isLoadingWritings || !relayPool || !activeAccount}
            >
              {isLoadingWritings ? (
                <FontAwesomeIcon icon={faSpinner} className="animate-spin" />
              ) : (
                'Load Friends Writings'
              )}
            </button>
            <button 
              className="btn btn-secondary text-sm" 
              onClick={handleLoadNostrverseWritings}
              disabled={isLoadingWritings || !relayPool}
            >
              {isLoadingWritings ? (
                <FontAwesomeIcon icon={faSpinner} className="animate-spin" />
              ) : (
                'Load Nostrverse Writings'
              )}
            </button>
            <button 
              className="btn btn-secondary text-sm ml-auto" 
              onClick={handleClearWritings}
              disabled={writingPosts.length === 0}
            >
              Clear
            </button>
          </div>

          <div className="mb-3 flex gap-2 flex-wrap">
            <Stat label="total" value={tLoadWritings} />
            <Stat label="first event" value={tFirstWriting} />
          </div>

          {writingPosts.length > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Loaded Writings ({writingPosts.length}):</div>
              <div className="space-y-2 max-h-96 overflow-y-auto">
                {writingPosts.map((post, idx) => {
                  const title = post.title
                  const summary = post.summary
                  const dTag = post.event.tags.find(t => t[0] === 'd')?.[1] || ''
                  
                  return (
                    <div key={idx} className="font-mono text-xs p-2 bg-gray-100 dark:bg-gray-800 rounded">
                      <div className="font-semibold mb-1">Writing #{idx + 1}</div>
                      <div className="opacity-70 mb-1">
                        <div>Author: {post.author.slice(0, 16)}...</div>
                        <div>Published: {post.published ? new Date(post.published * 1000).toLocaleString() : new Date(post.event.created_at * 1000).toLocaleString()}</div>
                        <div>d-tag: {dTag || '(empty)'}</div>
                      </div>
                      <div className="mt-1">
                        <div className="font-semibold text-[11px]">Title:</div>
                        <div>&quot;{title}&quot;</div>
                      </div>
                      {summary && (
                        <div className="mt-1 text-[11px] opacity-70">
                          <div>Summary: {summary.substring(0, 100)}{summary.length > 100 ? '...' : ''}</div>
                        </div>
                      )}
                      {post.image && (
                        <div className="mt-1 text-[11px] opacity-70">
                          <div>Image: {post.image.substring(0, 40)}...</div>
                        </div>
                      )}
                      <div className="opacity-50 mt-1 text-[10px] break-all">ID: {post.event.id}</div>
                    </div>
                  )
                })}
              </div>
            </div>
          )}
        </div>

        {/* Reading Progress Loading Section */}
        <div className="settings-section">
          <h3 className="section-title">Reading Progress Loading</h3>
          <div className="text-sm opacity-70 mb-3">Test reading progress loading (kind: 39802) for the logged-in user</div>
          <div className="flex gap-2 mb-3 items-center">
            <button 
              className="btn btn-primary" 
              onClick={handleLoadReadingProgress}
              disabled={isLoadingReadingProgress || !relayPool || !activeAccount}
            >
              {isLoadingReadingProgress ? (
                <>
                  <FontAwesomeIcon icon={faSpinner} className="animate-spin mr-2" />
                  Loading...
                </>
              ) : (
                'Load Reading Progress'
              )}
            </button>
            <button 
              className="btn btn-secondary ml-auto" 
              onClick={handleClearReadingProgress}
              disabled={readingProgressEvents.length === 0}
            >
              Clear
            </button>
          </div>
          <div className="mb-3 flex gap-2 flex-wrap">
            <Stat label="total" value={tLoadReadingProgress} />
            <Stat label="first event" value={tFirstReadingProgress} />
          </div>
          {readingProgressEvents.length > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Loaded Reading Progress ({readingProgressEvents.length}):</div>
              <div className="space-y-2 max-h-96 overflow-y-auto">
                {readingProgressEvents.map((evt, idx) => {
                  const dTag = evt.tags?.find((t: string[]) => t[0] === 'd')?.[1]
                  const aTag = evt.tags?.find((t: string[]) => t[0] === 'a')?.[1]
                  const content = evt.content || ''
                  
                  return (
                    <div key={idx} className="font-mono text-xs p-2 bg-gray-100 dark:bg-gray-800 rounded">
                      <div className="font-semibold mb-1">Reading Progress #{idx + 1}</div>
                      <div className="opacity-70 mb-1">
                        <div>Author: {evt.pubkey.slice(0, 16)}...</div>
                        <div>Created: {new Date(evt.created_at * 1000).toLocaleString()}</div>
                      </div>
                      <div className="mt-1">
                        {dTag && <div>d-tag: {dTag}</div>}
                        {aTag && <div className="text-[11px] opacity-70">#a: {aTag}</div>}
                        {content && <div>Progress: {content}</div>}
                      </div>
                      <div className="opacity-50 mt-1 text-[10px] break-all">ID: {evt.id}</div>
                    </div>
                  )
                })}
              </div>
            </div>
          )}

          {deduplicatedProgressMap.size > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Deduplicated Reading Progress ({deduplicatedProgressMap.size} articles):</div>
              
              {/* Category breakdown */}
              <div className="mb-3 p-2 bg-purple-50 dark:bg-purple-900/20 rounded border border-purple-200 dark:border-purple-700">
                <div className="text-sm font-semibold mb-2">Breakdown by Category:</div>
                <div className="space-y-1">
                  {(() => {
                    let unopened = 0, started = 0, reading = 0, completed = 0
                    for (const progress of deduplicatedProgressMap.values()) {
                      if (progress === 0) unopened++
                      else if (progress > 0 && progress <= 0.10) started++
                      else if (progress > 0.10 && progress <= 0.94) reading++
                      else if (progress >= 0.95) completed++
                    }
                    return (
                      <>
                        <div className="flex justify-between text-xs">
                          <span>Unopened (0%):</span>
                          <span className="font-semibold">{unopened}</span>
                        </div>
                        <div className="flex justify-between text-xs">
                          <span>Started (0% &lt; progress ≤ 10%):</span>
                          <span className="font-semibold">{started}</span>
                        </div>
                        <div className="flex justify-between text-xs bg-green-100 dark:bg-green-900/30 px-1 py-0.5 rounded">
                          <span>Reading (10% &lt; progress ≤ 94%) ✓:</span>
                          <span className="font-semibold text-green-700 dark:text-green-400">{reading}</span>
                        </div>
                        <div className="flex justify-between text-xs">
                          <span>Completed (≥ 95%):</span>
                          <span className="font-semibold">{completed}</span>
                        </div>
                      </>
                    )
                  })()}
                </div>
              </div>
              
              <div className="space-y-2 max-h-96 overflow-y-auto">
                {Array.from(deduplicatedProgressMap.entries()).map(([articleId, progress], idx) => {
                  return (
                    <div key={idx} className="font-mono text-xs p-2 bg-blue-50 dark:bg-blue-900/20 rounded border border-blue-200 dark:border-blue-700">
                      <div className="font-semibold mb-1">Article #{idx + 1}</div>
                      <div className="mt-1">
                        <div className="break-all">ID: {articleId}</div>
                        <div className="mt-1">
                          <div className="text-[11px] opacity-70">Progress: {(progress * 100).toFixed(1)}%</div>
                          <div className="w-full bg-gray-300 dark:bg-gray-700 rounded-full h-1.5 mt-1 overflow-hidden">
                            <div 
                              className="bg-blue-600 h-full" 
                              style={{ width: `${progress * 100}%` }}
                            />
                          </div>
                        </div>
                      </div>
                    </div>
                  )
                })}
              </div>
            </div>
          )}
        </div>

        {/* Mark-as-read Reactions Loading Section */}
        <div className="settings-section">
          <h3 className="section-title">Mark-as-read Reactions Loading</h3>
          <div className="text-sm opacity-70 mb-3">Test loading mark-as-read reactions (kind: 7 and 17) with the ARCHIVE_EMOJI for the logged-in user</div>
          <div className="flex gap-2 mb-3 items-center">
            <button 
              className="btn btn-primary" 
              onClick={handleLoadMarkAsReadReactions}
              disabled={isLoadingMarkAsRead || !relayPool || !activeAccount}
            >
              {isLoadingMarkAsRead ? (
                <>
                  <FontAwesomeIcon icon={faSpinner} className="animate-spin mr-2" />
                  Loading...
                </>
              ) : (
                'Load Mark-as-read Reactions'
              )}
            </button>
            <button 
              className="btn btn-secondary ml-auto" 
              onClick={handleClearMarkAsRead}
              disabled={markAsReadReactions.length === 0}
            >
              Clear
            </button>
          </div>
          <div className="mb-3 flex gap-2 flex-wrap">
            <Stat label="total" value={tLoadMarkAsRead} />
            <Stat label="first event" value={tFirstMarkAsRead} />
          </div>
          {markAsReadReactions.length > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Loaded Mark-as-read Reactions ({markAsReadReactions.length}):</div>
              <div className="space-y-2 max-h-96 overflow-y-auto">
                {markAsReadReactions.map((evt, idx) => {
                  const eTag = evt.tags?.find((t: string[]) => t[0] === 'e')?.[1]
                  const rTag = evt.tags?.find((t: string[]) => t[0] === 'r')?.[1]
                  const pTag = evt.tags?.find((t: string[]) => t[0] === 'p')?.[1]
                  
                  return (
                    <div key={idx} className="font-mono text-xs p-2 bg-gray-100 dark:bg-gray-800 rounded">
                      <div className="font-semibold mb-1">Mark-as-read Reaction #{idx + 1}</div>
                      <div className="opacity-70 mb-1">
                        <div>Kind: {evt.kind}</div>
                        <div>Author: {evt.pubkey.slice(0, 16)}...</div>
                        <div>Created: {new Date(evt.created_at * 1000).toLocaleString()}</div>
                      </div>
                      <div className="mt-1">
                        <div>Emoji: {evt.content}</div>
                        {eTag && <div className="text-[11px] opacity-70">#e: {eTag.slice(0, 16)}...</div>}
                        {rTag && <div className="text-[11px] opacity-70">#r: {rTag.length > 60 ? rTag.substring(0, 60) + '...' : rTag}</div>}
                        {pTag && <div className="text-[11px] opacity-70">#p: {pTag.slice(0, 16)}...</div>}
                      </div>
                      <div className="opacity-50 mt-1 text-[10px] break-all">ID: {evt.id}</div>
                    </div>
                  )
                })}
              </div>
            </div>
          )}
        </div>

        {/* Relay List Loading Section */}
        <div className="settings-section">
          <h3 className="section-title">Relay List Loading (kind 10002)</h3>
          <div className="text-sm opacity-70 mb-3">Load your relay list to debug dynamic relay integration:</div>
          
          <div className="flex gap-2 mb-3 items-center">
            <button 
              className="btn btn-primary" 
              onClick={handleLoadRelayList}
              disabled={isLoadingRelayList || !relayPool || !activeAccount}
            >
              {isLoadingRelayList ? (
                <>
                  <FontAwesomeIcon icon={faSpinner} className="animate-spin mr-2" />
                  Loading...
                </>
              ) : (
                'Load Relay List'
              )}
            </button>
            <button 
              className="btn btn-secondary ml-auto" 
              onClick={handleClearRelayList}
              disabled={relayListEvents.length === 0}
            >
              Clear
            </button>
          </div>
          
          <div className="flex gap-4 mb-3 text-sm">
            <Stat label="total" value={tLoadRelayList} />
            <Stat label="first event" value={tFirstRelayList} />
          </div>
          {relayListEvents.length > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Loaded Relay List Events ({relayListEvents.length}):</div>
              <div className="space-y-2 max-h-96 overflow-y-auto">
                {relayListEvents.map((evt, idx) => {
                  const relayTags = evt.tags?.filter((t: string[]) => t[0] === 'r') || []
                  
                  return (
                    <div key={idx} className="font-mono text-xs p-2 bg-gray-100 dark:bg-gray-800 rounded">
                      <div className="font-semibold mb-1">Relay List Event #{idx + 1}</div>
                      <div className="opacity-70 mb-1">
                        <div>Kind: {evt.kind}</div>
                        <div>Author: {evt.pubkey.slice(0, 16)}...</div>
                        <div>Created: {new Date(evt.created_at * 1000).toLocaleString()}</div>
                        <div>Relays: {relayTags.length}</div>
                      </div>
                      <div className="mt-1">
                        <div className="text-[11px] opacity-70 mb-1">Relay URLs:</div>
                        {relayTags.map((tag, tagIdx) => (
                          <div key={tagIdx} className="text-[10px] opacity-60 break-all">
                            {tag[1]} {tag[2] ? `(${tag[2]})` : ''}
                          </div>
                        ))}
                      </div>
                      <div className="opacity-50 mt-1 text-[10px] break-all">ID: {evt.id}</div>
                    </div>
                  )
                })}
              </div>
            </div>
          )}
        </div>

        {/* Web of Trust Section */}
        <div className="settings-section">
          <h3 className="section-title">Web of Trust</h3>
          <div className="text-sm opacity-70 mb-3">Load your followed contacts (friends) for highlight fetching:</div>
          
          <div className="mb-3">
            <button 
              className="btn btn-primary" 
              onClick={handleLoadFriendsList}
              disabled={friendsButtonLoading || !relayPool || !activeAccount}
            >
              {friendsButtonLoading ? (
                <>
                  <FontAwesomeIcon icon={faSpinner} className="animate-spin mr-2" />
                  Loading...
                </>
              ) : (
                'Load Friends'
              )}
            </button>
          </div>

          {friendsPubkeys.size > 0 && (
            <div className="mb-3">
              <div className="text-sm opacity-70 mb-2">Friends Count: {friendsNpubs.length}</div>
              <div className="font-mono text-xs max-h-48 overflow-y-auto bg-gray-100 dark:bg-gray-800 p-3 rounded space-y-1">
                {friendsNpubs.map(npub => (
                  <div key={npub} title={npub} className="truncate hover:text-clip hover:whitespace-normal cursor-pointer">
                    {npub}
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>

        {/* Debug Logs Section */}
        <div className="settings-section">
          <h3 className="section-title">Debug Logs</h3>
          <div className="text-sm opacity-70 mb-3">Recent bunker logs:</div>
            <div className="max-h-192 overflow-y-auto font-mono text-xs leading-relaxed">
            {logs.length === 0 ? (
              <div className="text-sm opacity-50 italic">No logs yet</div>
            ) : (
              logs.slice(-200).map((l, i) => (
                <div key={i} className="mb-1 p-2 bg-gray-100 dark:bg-gray-800 rounded">
                  <span className="opacity-70">[{new Date(l.ts).toLocaleTimeString()}]</span> <span className="font-semibold">{l.level.toUpperCase()}</span> {l.source}: {l.message}
                  {l.data !== undefined && (
                    <span className="opacity-70"> — {typeof l.data === 'string' ? l.data : JSON.stringify(l.data)}</span>
                  )}
                </div>
              ))
            )}
          </div>
            <div className="mt-3">
              <div className="flex justify-end mb-2">
                <label className="flex items-center gap-2 cursor-pointer">
                  <input
                    type="checkbox"
                    checked={debugEnabled}
                    onChange={toggleDebug}
                    className="checkbox"
                  />
                  <span className="text-sm">Show all applesauce debug logs</span>
                </label>
              </div>
              <div className="flex justify-end">
                <button className="btn btn-secondary" onClick={() => setLogs([])}>Clear logs</button>
              </div>
            </div>
        </div>
      </div>
    </div>
  )
  
  return (
    <ThreePaneLayout
      isCollapsed={isCollapsed}
      isHighlightsCollapsed={true}
      isSidebarOpen={false}
      showSettings={false}
      showSupport={true}
      bookmarks={bookmarks}
      bookmarksLoading={bookmarksLoading}
      viewMode={viewMode}
      isRefreshing={false}
      lastFetchTime={null}
      onToggleSidebar={isMobile ? () => {} : () => setIsCollapsed(!isCollapsed)}
      onLogout={onLogout}
      onViewModeChange={setViewMode}
      onOpenSettings={() => navigate('/settings')}
      onRefresh={onRefreshBookmarks}
      relayPool={relayPool}
      eventStore={eventStore}
      readerLoading={false}
      readerContent={undefined}
      selectedUrl={undefined}
      settings={settings}
      onSaveSettings={saveSettings}
      onCloseSettings={() => navigate('/')}
      classifiedHighlights={[]}
      showHighlights={false}
      selectedHighlightId={undefined}
      highlightVisibility={{ nostrverse: true, friends: true, mine: true }}
      onHighlightClick={() => {}}
      onTextSelection={() => {}}
      onClearSelection={() => {}}
      currentUserPubkey={activeAccount?.pubkey}
      followedPubkeys={new Set()}
      activeAccount={activeAccount}
      currentArticle={null}
      highlights={[]}
      highlightsLoading={false}
      onToggleHighlightsPanel={() => {}}
      onSelectUrl={() => {}}
      onToggleHighlights={() => {}}
      onRefreshHighlights={() => {}}
      onHighlightVisibilityChange={() => {}}
      highlightButtonRef={{ current: null }}
      onCreateHighlight={() => {}}
      hasActiveAccount={!!activeAccount}
      toastMessage={undefined}
      toastType={undefined}
      onClearToast={() => {}}
      support={debugContent}
    />
  )
}

export default Debug
