import { useState, useEffect, useCallback } from 'react'
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
import { EventStoreProvider, AccountsProvider, Hooks } from 'applesauce-react'
import { EventStore } from 'applesauce-core'
import { AccountManager, Accounts } from 'applesauce-accounts'
import { registerCommonAccountTypes } from 'applesauce-accounts/accounts'
import { RelayPool } from 'applesauce-relay'
import { NostrConnectSigner } from 'applesauce-signers'
import { getDefaultBunkerPermissions } from './services/nostrConnect'
import { createAddressLoader } from 'applesauce-loaders/loaders'
import Debug from './components/Debug'
import Bookmarks from './components/Bookmarks'
import RouteDebug from './components/RouteDebug'
import Toast from './components/Toast'
import { useToast } from './hooks/useToast'
import { useOnlineStatus } from './hooks/useOnlineStatus'
import { RELAYS } from './config/relays'
import { SkeletonThemeProvider } from './components/Skeletons'
import { loadUserRelayList, loadBlockedRelays, computeRelaySet } from './services/relayListService'
import { applyRelaySetToPool, getActiveRelayUrls, ALWAYS_LOCAL_RELAYS } from './services/relayManager'
import { Bookmark } from './types/bookmarks'
import { bookmarkController } from './services/bookmarkController'
import { contactsController } from './services/contactsController'
import { highlightsController } from './services/highlightsController'
import { writingsController } from './services/writingsController'
import { readingProgressController } from './services/readingProgressController'
// import { fetchNostrverseHighlights } from './services/nostrverseService'
import { nostrverseHighlightsController } from './services/nostrverseHighlightsController'
import { nostrverseWritingsController } from './services/nostrverseWritingsController'
import { archiveController } from './services/archiveController'

const DEFAULT_ARTICLE = import.meta.env.VITE_DEFAULT_ARTICLE_NADDR || 
  'naddr1qvzqqqr4gupzqmjxss3dld622uu8q25gywum9qtg4w4cv4064jmg20xsac2aam5nqqxnzd3cxqmrzv3exgmr2wfesgsmew'

// AppRoutes component that has access to hooks
function AppRoutes({ 
  relayPool, 
  eventStore,
  showToast 
}: { 
  relayPool: RelayPool
  eventStore: EventStore | null
  showToast: (message: string) => void
}) {
  const accountManager = Hooks.useAccountManager()
  const activeAccount = Hooks.useActiveAccount()
  
  // Centralized bookmark state (fed by controller)
  const [bookmarks, setBookmarks] = useState<Bookmark[]>([])
  const [bookmarksLoading, setBookmarksLoading] = useState(false)

  // Centralized contacts state (fed by controller)
  const [contacts, setContacts] = useState<Set<string>>(new Set())
  const [contactsLoading, setContactsLoading] = useState(false)

  // Subscribe to bookmark controller
  useEffect(() => {
    const unsubBookmarks = bookmarkController.onBookmarks((bookmarks) => {
      setBookmarks(bookmarks)
    })
    const unsubLoading = bookmarkController.onLoading((loading) => {
      setBookmarksLoading(loading)
    })
    
    return () => {
      unsubBookmarks()
      unsubLoading()
    }
  }, [])

  // Subscribe to contacts controller
  useEffect(() => {
    const unsubContacts = contactsController.onContacts((contacts) => {
      setContacts(contacts)
    })
    const unsubLoading = contactsController.onLoading((loading) => {
      setContactsLoading(loading)
    })
    
    return () => {
      unsubContacts()
      unsubLoading()
    }
  }, [])


  // Auto-load bookmarks, contacts, and highlights when account is ready (on login or page mount)
  useEffect(() => {
    if (activeAccount && relayPool) {
      const pubkey = (activeAccount as { pubkey?: string }).pubkey
      
      // Load bookmarks
      if (bookmarks.length === 0 && !bookmarksLoading) {
        bookmarkController.start({ relayPool, activeAccount, accountManager })
      }
      
      // Load contacts
      if (pubkey && contacts.size === 0 && !contactsLoading) {
        contactsController.start({ relayPool, pubkey })
      }
      
      // Load highlights (controller manages its own state)
      if (pubkey && eventStore && !highlightsController.isLoadedFor(pubkey)) {
        highlightsController.start({ relayPool, eventStore, pubkey })
      }

      // Load writings (controller manages its own state)
      if (pubkey && eventStore && !writingsController.isLoadedFor(pubkey)) {
        writingsController.start({ relayPool, eventStore, pubkey })
      }

      // Load reading progress (controller manages its own state)
      if (pubkey && eventStore && !readingProgressController.isLoadedFor(pubkey)) {
        readingProgressController.start({ relayPool, eventStore, pubkey })
      }

      // Load archive (marked-as-read) controller
      if (pubkey && eventStore && !archiveController.isLoadedFor(pubkey)) {
        archiveController.start({ relayPool, eventStore, pubkey })
      }

      // Start centralized nostrverse highlights controller (non-blocking)
      if (eventStore) {
        nostrverseHighlightsController.start({ relayPool, eventStore })
        nostrverseWritingsController.start({ relayPool, eventStore })
      }
    }
  }, [activeAccount, relayPool, eventStore, bookmarks.length, bookmarksLoading, contacts.size, contactsLoading, accountManager])

  // Ensure nostrverse controllers run even when logged out
  useEffect(() => {
    if (relayPool && eventStore) {
      nostrverseHighlightsController.start({ relayPool, eventStore })
      nostrverseWritingsController.start({ relayPool, eventStore })
    }
  }, [relayPool, eventStore])

  // Manual refresh (for sidebar button)
  const handleRefreshBookmarks = useCallback(async () => {
    if (!relayPool || !activeAccount) {
      return
    }
    bookmarkController.reset()
    await bookmarkController.start({ relayPool, activeAccount, accountManager })
  }, [relayPool, activeAccount, accountManager])

  const handleLogout = () => {
    accountManager.clearActive()
    bookmarkController.reset() // Clear bookmarks via controller
    contactsController.reset() // Clear contacts via controller
    highlightsController.reset() // Clear highlights via controller
    readingProgressController.reset() // Clear reading progress via controller
    archiveController.reset() // Clear archive state
    showToast('Logged out successfully')
  }

  return (
    <Routes>
      <Route 
        path="/a/:naddr" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/r/*" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/settings" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/support" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/explore" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/explore/writings" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/me" 
        element={<Navigate to="/me/highlights" replace />} 
      />
      <Route 
        path="/me/highlights" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/me/reading-list" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/me/reads" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/me/reads/:filter" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/me/links" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/me/links/:filter" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/me/writings" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/p/:npub" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/p/:npub/writings" 
        element={
          <Bookmarks 
            relayPool={relayPool}
            onLogout={handleLogout}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
          />
        } 
      />
      <Route 
        path="/debug" 
        element={
          <Debug 
            relayPool={relayPool}
            eventStore={eventStore}
            bookmarks={bookmarks}
            bookmarksLoading={bookmarksLoading}
            onRefreshBookmarks={handleRefreshBookmarks}
            onLogout={handleLogout}
          />
        } 
      />
      <Route path="/" element={<Navigate to={`/a/${DEFAULT_ARTICLE}`} replace />} />
    </Routes>
  )
}

function App() {
  const [eventStore, setEventStore] = useState<EventStore | null>(null)
  const [accountManager, setAccountManager] = useState<AccountManager | null>(null)
  const [relayPool, setRelayPool] = useState<RelayPool | null>(null)
  const { toastMessage, toastType, showToast, clearToast } = useToast()
  const isOnline = useOnlineStatus()

  useEffect(() => {
    const initializeApp = async () => {
      // Initialize event store, account manager, and relay pool
      const store = new EventStore()
      const accounts = new AccountManager()
      
      // Disable request queueing globally - makes all operations instant
      // Queue causes requests to wait for user interaction which blocks batch operations
      accounts.disableQueue = true
      
      // Register common account types (needed for deserialization)
      registerCommonAccountTypes(accounts)
      
      // Create relay pool and set it up BEFORE loading accounts
      // NostrConnectAccount.fromJSON needs this to restore the signer
      const pool = new RelayPool()
      // Wire the signer to use this pool; make publish non-blocking so callers don't
      // wait for every relay send to finish. Responses still resolve the pending request.
      NostrConnectSigner.subscriptionMethod = pool.subscription.bind(pool)
      NostrConnectSigner.publishMethod = (relays: string[], event: unknown) => {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const result: any = pool.publish(relays, event as any)
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        if (result && typeof (result as any).subscribe === 'function') {
          // Subscribe to the observable but ignore completion/errors (fire-and-forget)
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          try { (result as any).subscribe({ complete: () => { /* noop */ }, error: () => { /* noop */ } }) } catch { /* ignore */ }
        }
        // Return an already-resolved promise so upstream await finishes immediately
        return Promise.resolve()
      }
      
      // Create a relay group for better event deduplication and management
      pool.group(RELAYS)
      console.log('[relay-init] Initial pool setup - added RELAYS:', RELAYS.length, 'relays')
      console.log('[relay-init] Pool now has:', Array.from(pool.relays.keys()).length, 'relays')
      
      // Load persisted accounts from localStorage
      try {
        const accountsJson = localStorage.getItem('accounts')
        
        const json = JSON.parse(accountsJson || '[]')
        
        await accounts.fromJSON(json)
        
        // Load active account from storage
        const activeId = localStorage.getItem('active')
        
        if (activeId) {
          const account = accounts.getAccount(activeId)
          
          if (account) {
            accounts.setActive(activeId)
          }
        }
      } catch (err) {
        console.error('Failed to load accounts from storage:', err)
      }
      
      // Subscribe to accounts changes and persist to localStorage
      const accountsSub = accounts.accounts$.subscribe(() => {
        localStorage.setItem('accounts', JSON.stringify(accounts.toJSON()))
      })
      
      // Subscribe to active account changes and persist to localStorage
      const activeSub = accounts.active$.subscribe((account) => {
        if (account) {
          localStorage.setItem('active', account.id)
        } else {
          localStorage.removeItem('active')
        }
      })
      
      // Reconnect bunker signers when active account changes
      // Keep track of which accounts we've already reconnected to avoid double-connecting
      const reconnectedAccounts = new Set<string>()
      
            const bunkerReconnectSub = accounts.active$.subscribe(async (account) => {
              
              if (account && account.type === 'nostr-connect') {
                const nostrConnectAccount = account as Accounts.NostrConnectAccount<unknown>
                // Disable applesauce account queueing so decrypt requests aren't serialized behind earlier ops
                try {
                  if (!(nostrConnectAccount as unknown as { disableQueue?: boolean }).disableQueue) {
                    (nostrConnectAccount as unknown as { disableQueue?: boolean }).disableQueue = true
                  }
                } catch (err) {
                  // Ignore queue disable errors
                }
                // Note: for Amber bunker, the remote signer pubkey is the user's pubkey. This is expected.
                
                // Skip if we've already reconnected this account
                if (reconnectedAccounts.has(account.id)) {
                  return
                }
                
                
                try {
                  // For restored signers, ensure they have the pool's subscription methods
                  // The signer was created in fromJSON without pool context, so we need to recreate it
                  const signerData = nostrConnectAccount.toJSON().signer
                  
                  // Add bunker's relays to the pool BEFORE recreating the signer
                  // This ensures the pool has all relays when the signer sets up its methods
                  const bunkerRelays = signerData.relays || []
                  const existingRelayUrls = new Set(Array.from(pool.relays.keys()))
                  const newBunkerRelays = bunkerRelays.filter(url => !existingRelayUrls.has(url))
                  
                  if (newBunkerRelays.length > 0) {
                    pool.group(newBunkerRelays)
                  } else {
                    // Bunker relays already in pool
                  }
                  
                  const recreatedSigner = new NostrConnectSigner({
                    relays: signerData.relays,
                    pubkey: nostrConnectAccount.pubkey,
                    remote: signerData.remote,
                    signer: nostrConnectAccount.signer.signer, // Use the existing SimpleSigner
                    pool: pool
                  })
                  // Ensure local relays are included for NIP-46 request/response traffic (e.g., Amber bunker)
                  try {
                    const mergedRelays = Array.from(new Set([...(signerData.relays || []), ...RELAYS]))
                    recreatedSigner.relays = mergedRelays
                  } catch (err) { /* ignore */ }
                  
                  // Replace the signer on the account
                  nostrConnectAccount.signer = recreatedSigner
                  
                  // Fire-and-forget publish for bunker: trigger but don't wait for completion
                  // IMPORTANT: bind originals to preserve `this` context used internally by the signer
                  const originalPublish = (recreatedSigner as unknown as { publishMethod: (relays: string[], event: unknown) => unknown }).publishMethod.bind(recreatedSigner)
                  ;(recreatedSigner as unknown as { publishMethod: (relays: string[], event: unknown) => unknown }).publishMethod = (relays: string[], event: unknown) => {
                    const result = originalPublish(relays, event)
                    if (result && typeof (result as { subscribe?: unknown }).subscribe === 'function') {
                      try { (result as { subscribe: (h: { complete?: () => void; error?: (e: unknown) => void }) => unknown }).subscribe({ complete: () => { /* noop */ }, error: () => { /* noop */ } }) } catch { /* ignore */ }
                    }
                    return {} as unknown as never
                  }

                  
                  // Just ensure the signer is listening for responses - don't call connect() again
                  // The fromBunkerURI already connected with permissions during login
                  if (!nostrConnectAccount.signer.listening) {
                    await nostrConnectAccount.signer.open()
                  }
                  
                  // Attempt a guarded reconnect to ensure Amber authorizes decrypt operations
                  try {
                    if (nostrConnectAccount.signer.remote && !reconnectedAccounts.has(account.id)) {
                      const permissions = getDefaultBunkerPermissions()
                      await nostrConnectAccount.signer.connect(undefined, permissions)
                    }
                  } catch (e) {
                    // Ignore reconnect errors
                  }
                  
                  // Give the subscription a moment to fully establish before allowing decrypt operations
                  // This ensures the signer is ready to handle and receive responses
                  await new Promise(resolve => setTimeout(resolve, 100))
                  // Fire-and-forget: probe decrypt path to verify Amber responds to NIP-46 decrypt
                  try {
                    const withTimeout = async <T,>(p: Promise<T>, ms = 10000): Promise<T> => {
                      return await Promise.race([
                        p,
                        new Promise<T>((_, rej) => setTimeout(() => rej(new Error(`probe timeout after ${ms}ms`)), ms)),
                      ])
                    }
                    setTimeout(async () => {
                      const self = nostrConnectAccount.pubkey
                      // Try a roundtrip so the bunker can respond successfully
                      try {
                        await withTimeout(nostrConnectAccount.signer.nip44!.encrypt(self, 'probe-nip44'))
                        await withTimeout(nostrConnectAccount.signer.nip44!.decrypt(self, ''))
                      } catch (_err) {
                        // Ignore probe errors
                      }
                      try {
                        await withTimeout(nostrConnectAccount.signer.nip04!.encrypt(self, 'probe-nip04'))
                        await withTimeout(nostrConnectAccount.signer.nip04!.decrypt(self, ''))
                      } catch (_err) {
                        // Ignore probe errors
                      }
                    }, 0)
                  } catch (_err) {
                    // Ignore signer setup errors
                  }
                  // The bunker remembers the permissions from the initial connection
                  nostrConnectAccount.signer.isConnected = true
                  
                  
                  // Mark this account as reconnected
                  reconnectedAccounts.add(account.id)
                } catch (error) {
                  console.error('Failed to open signer:', error)
                }
              }
            })
      
      // Handle user relay list and blocked relays when account changes
      const userRelaysSub = accounts.active$.subscribe((account) => {
        console.log('[relay-init] userRelaysSub fired, account:', account ? 'logged in' : 'logged out')
        console.log('[relay-init] Pool has', Array.from(pool.relays.keys()).length, 'relays before applying changes')
        if (account) {
          // User logged in - start with hardcoded relays immediately, then stream user relay list updates
          const pubkey = account.pubkey

          // Bunker relays (if any)
          let bunkerRelays: string[] = []
          if (account.type === 'nostr-connect') {
            const nostrConnectAccount = account as Accounts.NostrConnectAccount<unknown>
            const signerData = nostrConnectAccount.toJSON().signer
            bunkerRelays = signerData.relays || []
          }
          console.log('[relay-init] Bunker relays:', bunkerRelays.length, 'relays', bunkerRelays)

          // Start with hardcoded + bunker relays immediately (non-blocking)
          const initialRelays = computeRelaySet({
            hardcoded: RELAYS,
            bunker: bunkerRelays,
            userList: [],
            blocked: [],
            alwaysIncludeLocal: ALWAYS_LOCAL_RELAYS
          })
          console.log('[relay-init] Initial relay set (hardcoded):', initialRelays.length, 'relays', initialRelays)

          // Apply initial set immediately
          applyRelaySetToPool(pool, initialRelays)
          console.log('[relay-init] After initial applyRelaySetToPool, pool has:', Array.from(pool.relays.keys()).length, 'relays')

          // Prepare keep-alive helper
          const updateKeepAlive = () => {
            const poolWithSub = pool as unknown as { _keepAliveSubscription?: { unsubscribe: () => void } }
            if (poolWithSub._keepAliveSubscription) {
              poolWithSub._keepAliveSubscription.unsubscribe()
            }
            const activeRelays = getActiveRelayUrls(pool)
            const newKeepAliveSub = pool.subscription(activeRelays, { kinds: [0], limit: 0 }).subscribe({
              next: () => {},
              error: () => {}
            })
            poolWithSub._keepAliveSubscription = newKeepAliveSub
          }

          // Begin loading blocked relays in background
          const blockedPromise = loadBlockedRelays(pool, pubkey)

          // Stream user relay list; apply immediately on first/updated event
          loadUserRelayList(pool, pubkey, {
            onUpdate: (userRelays) => {
              const interimRelays = computeRelaySet({
                hardcoded: [],
                bunker: bunkerRelays,
                userList: userRelays,
                blocked: [],
                alwaysIncludeLocal: ALWAYS_LOCAL_RELAYS
              })
              console.log('[relay-init] Interim relay set from first user list:', interimRelays.length, 'relays', interimRelays)
              applyRelaySetToPool(pool, interimRelays)
              updateKeepAlive()
            }
          }).then(async (userRelayList) => {
            const blockedRelays = await blockedPromise.catch(() => [])
            console.log('[relay-init] User relay list (10002):', userRelayList.length, 'relays', userRelayList.map(r => r.url))
            console.log('[relay-init] Blocked relays (10006):', blockedRelays.length, 'relays', blockedRelays)

            const finalRelays = computeRelaySet({
              hardcoded: userRelayList.length > 0 ? [] : RELAYS,
              bunker: bunkerRelays,
              userList: userRelayList,
              blocked: blockedRelays,
              alwaysIncludeLocal: ALWAYS_LOCAL_RELAYS
            })
            console.log('[relay-init] Final relay set (with user preferences):', finalRelays.length, 'relays', finalRelays)
            applyRelaySetToPool(pool, finalRelays)
            console.log('[relay-init] After user relay list apply, pool has:', Array.from(pool.relays.keys()).length, 'relays')
            console.log('[relay-init] Final relay URLs:', Array.from(pool.relays.keys()))
            updateKeepAlive()
            
            // Update address loader with new relays
            const activeRelays = getActiveRelayUrls(pool)
            const addressLoader = createAddressLoader(pool, {
              eventStore: store,
              lookupRelays: activeRelays
            })
            store.addressableLoader = addressLoader
            store.replaceableLoader = addressLoader
          }).catch((error) => {
            console.error('[relay-init] Failed to load user relay list (continuing with initial set):', error)
            // Continue with initial relay set on error - no need to change anything
          })
        } else {
          // User logged out - reset to hardcoded relays
          console.log('[relay-init] Applying RELAYS for logged out user, RELAYS.length:', RELAYS.length)
          applyRelaySetToPool(pool, RELAYS)
          console.log('[relay-init] After applyRelaySetToPool (logged out), pool has:', Array.from(pool.relays.keys()).length, 'relays')
          console.log('[relay-init] Relay URLs:', Array.from(pool.relays.keys()))
          
          // Update keep-alive subscription
          const poolWithSub = pool as unknown as { _keepAliveSubscription?: { unsubscribe: () => void } }
          if (poolWithSub._keepAliveSubscription) {
            poolWithSub._keepAliveSubscription.unsubscribe()
          }
          const newKeepAliveSub = pool.subscription(RELAYS, { kinds: [0], limit: 0 }).subscribe({
            next: () => {},
            error: () => {}
          })
          poolWithSub._keepAliveSubscription = newKeepAliveSub
          
          // Reset address loader
          const addressLoader = createAddressLoader(pool, {
            eventStore: store,
            lookupRelays: RELAYS
          })
          store.addressableLoader = addressLoader
          store.replaceableLoader = addressLoader
        }
      })
      
      // Keep all relay connections alive indefinitely by creating a persistent subscription
      // This prevents disconnection when no other subscriptions are active
      // Create a minimal subscription that never completes to keep connections alive
      const keepAliveSub = pool.subscription(RELAYS, { kinds: [0], limit: 0 }).subscribe({
        next: () => {},
        error: () => {}
      })
      
      // Store subscription for cleanup
      ;(pool as unknown as { _keepAliveSubscription: typeof keepAliveSub })._keepAliveSubscription = keepAliveSub
      
      // Attach address/replaceable loaders so ProfileModel can fetch profiles
      const addressLoader = createAddressLoader(pool, {
        eventStore: store,
        lookupRelays: RELAYS
      })
      store.addressableLoader = addressLoader
      store.replaceableLoader = addressLoader

      setEventStore(store)
      setAccountManager(accounts)
      setRelayPool(pool)
      
      // Cleanup function
      return () => {
        accountsSub.unsubscribe()
        activeSub.unsubscribe()
        bunkerReconnectSub.unsubscribe()
        userRelaysSub.unsubscribe()
        // Clean up keep-alive subscription if it exists
        const poolWithSub = pool as unknown as { _keepAliveSubscription?: { unsubscribe: () => void } }
        if (poolWithSub._keepAliveSubscription) {
          poolWithSub._keepAliveSubscription.unsubscribe()
        }
      }
    }
    
    let cleanup: (() => void) | undefined
    initializeApp().then((fn) => {
      cleanup = fn
    })
    
    return () => {
      if (cleanup) cleanup()
    }
  }, [isOnline, showToast])

  // Monitor online/offline status
  useEffect(() => {
    if (!isOnline) {
      showToast('You are offline. Some features may be limited.')
    }
  }, [isOnline, showToast])

  // Listen for service worker updates
  useEffect(() => {
    const handleSWUpdate = () => {
      showToast('New version available! Refresh to update.')
    }

    window.addEventListener('sw-update-available', handleSWUpdate)
    return () => {
      window.removeEventListener('sw-update-available', handleSWUpdate)
    }
  }, [showToast])

  if (!eventStore || !accountManager || !relayPool) {
    return (
      <div className="loading">
        <FontAwesomeIcon icon={faSpinner} spin />
      </div>
    )
  }

  return (
    <SkeletonThemeProvider>
      <EventStoreProvider eventStore={eventStore}>
        <AccountsProvider manager={accountManager}>
          <BrowserRouter>
            <div className="min-h-screen p-0 max-w-none m-0 relative">
              <AppRoutes relayPool={relayPool} eventStore={eventStore} showToast={showToast} />
              <RouteDebug />
            </div>
          </BrowserRouter>
          {toastMessage && (
            <Toast
              message={toastMessage}
              type={toastType}
              onClose={clearToast}
            />
          )}
        </AccountsProvider>
      </EventStoreProvider>
    </SkeletonThemeProvider>
  )
}

export default App
