import { Button } from '@/components/ui/button'
import logger from '@/lib/logger'
import { useNostr } from '@/providers/NostrProvider'
import { useEffect, useState, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { Trash2, RefreshCw, Database, X, Terminal, XCircle } from 'lucide-react'
import { Input } from '@/components/ui/input'
import client from '@/services/client.service'
import indexedDb from '@/services/indexed-db.service'
import postEditorCache from '@/services/post-editor-cache.service'
import { StorageKey } from '@/constants'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription } from '@/components/ui/drawer'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { toast } from 'sonner'
import { syncUserDeletionTombstones } from '@/lib/sync-user-deletions'
import { useCacheBrowser } from '../../contexts/cache-browser-context'

export default function InBrowserCacheSetting() {
  const { t } = useTranslation()
  const { isSmallScreen } = useScreenSize()
  const {
    pubkey,
    relayList,
    requestAccountNetworkHydrate
  } = useNostr()
  const { openBrowseCache } = useCacheBrowser()
  const [consoleLogs, setConsoleLogs] = useState<Array<{ type: string; message: string; formattedParts?: Array<{ text: string; style?: string }>; timestamp: number }>>([])
  const [showConsoleLogs, setShowConsoleLogs] = useState(false)
  const [consoleLogSearch, setConsoleLogSearch] = useState('')
  const [consoleLogLevel, setConsoleLogLevel] = useState<'errors-warnings' | 'all'>('all')
  const [cacheRefreshBusy, setCacheRefreshBusy] = useState(false)
  const consoleLogRef = useRef<Array<{ type: string; message: string; formattedParts?: Array<{ text: string; style?: string }>; timestamp: number }>>([])

  const handleClearCache = async () => {
    if (!confirm(t('Are you sure you want to clear all cached data? This will delete all stored events and settings from your browser.'))) {
      return
    }

    try {
      await indexedDb.clearAllCache()
      await indexedDb.clearPiperTtsCache()

      const cacheKeys = Object.values(StorageKey).filter(key =>
        key.includes('CACHE') || key.includes('EVENT') || key.includes('FEED') || key.includes('NOTIFICATION')
      )
      cacheKeys.forEach(key => {
        try {
          window.localStorage.removeItem(key)
        } catch (e) {
          logger.warn(`Failed to remove ${key} from localStorage`, e as Error)
        }
      })

      if ('caches' in window) {
        try {
          const cacheNames = await caches.keys()
          const currentOrigin = window.location.origin

          const appCacheNames = [
            'nostr-images',
            'satellite-images',
            'external-images'
          ]

          const appCaches = cacheNames.filter(name => {
            if (appCacheNames.includes(name)) return true
            if (name.startsWith('workbox-') || name.startsWith('precache-')) return true
            if (name.includes(currentOrigin.replace(/https?:\/\//, '').split('/')[0])) return true
            return false
          })

          await Promise.all(appCaches.map(name => caches.delete(name).catch(error => {
            logger.warn(`Failed to delete cache: ${name}`, { error })
          })))
        } catch (error) {
          logger.warn('Failed to clear some service worker caches', { error })
        }
      }

      postEditorCache.clearAllPostCaches()
      client.clearInMemoryCaches()

      toast.success(t('Cache cleared successfully'))
      setTimeout(() => window.location.reload(), 1500)
    } catch (error) {
      logger.error('Failed to clear cache', { error })
      toast.error(t('Failed to clear cache'))
    }
  }

  const handleRefreshCache = async () => {
    try {
      setCacheRefreshBusy(true)
      await indexedDb.forceDatabaseUpgrade()
      if (pubkey) {
        await requestAccountNetworkHydrate()
        await syncUserDeletionTombstones(pubkey, relayList)
      }
      toast.success(t('Cache refreshed successfully'))
    } catch (error) {
      logger.error('Failed to refresh cache', { error })
      toast.error(t('Failed to refresh cache'))
    } finally {
      setCacheRefreshBusy(false)
    }
  }

  const handleClearServiceWorker = async () => {
    if (!confirm(t('Are you sure you want to unregister the service worker? This will clear this app\'s service worker caches and you will need to reload the page.'))) {
      return
    }

    try {
      const currentOrigin = window.location.origin
      let unregisteredCount = 0
      let cacheClearedCount = 0

      if (window.isSecureContext && 'serviceWorker' in navigator) {
        let registrations: readonly ServiceWorkerRegistration[] = []
        try {
          registrations = await navigator.serviceWorker.getRegistrations()
        } catch (error) {
          logger.warn('Failed to get service worker registrations', { error })
        }

        if (registrations.length > 0) {
          const unregisterPromises = registrations.map(async (registration) => {
            try {
              const scope = registration.scope
              if (scope.startsWith(currentOrigin)) {
                const result = await registration.unregister()
                if (result) unregisteredCount++
                return result
              }
              return false
            } catch (error) {
              logger.warn('Failed to unregister a service worker', { error })
              return false
            }
          })
          await Promise.all(unregisterPromises)
        }
      }

      if ('caches' in window) {
        try {
          const cacheNames = await caches.keys()

          const appCacheNames = [
            'nostr-images',
            'satellite-images',
            'external-images'
          ]

          const appCaches = cacheNames.filter(name => {
            if (appCacheNames.includes(name)) return true
            if (name.startsWith('workbox-') || name.startsWith('precache-')) return true
            if (name.includes(currentOrigin.replace(/https?:\/\//, '').split('/')[0])) return true
            return false
          })

          await Promise.all(appCaches.map(name => {
            cacheClearedCount++
            return caches.delete(name).catch(error => {
              logger.warn(`Failed to delete cache: ${name}`, { error })
              cacheClearedCount--
            })
          }))
        } catch (error) {
          logger.warn('Failed to clear some caches', { error })
        }
      }

      if (unregisteredCount > 0 || cacheClearedCount > 0) {
        const message = unregisteredCount > 0 && cacheClearedCount > 0
          ? t('Service worker unregistered and caches cleared. Please reload the page.')
          : unregisteredCount > 0
          ? t('Service worker unregistered. Please reload the page.')
          : t('Service worker caches cleared. Please reload the page.')
        toast.success(message)
        setTimeout(() => window.location.reload(), 1000)
      } else {
        toast.info(t('No service workers or caches found for this app'))
      }
    } catch (error) {
      logger.error('Failed to unregister service worker', { error })
      toast.error(t('Failed to unregister service worker: ') + (error instanceof Error ? error.message : String(error)))
    }
  }

  useEffect(() => {
    const originalLog = console.log
    const originalError = console.error
    const originalWarn = console.warn
    const originalInfo = console.info

    const captureLog = (type: string, ...args: any[]) => {
      let message = ''
      let formattedParts: Array<{ text: string; style?: string }> = []

      if (args.length > 0 && typeof args[0] === 'string' && args[0].includes('%c')) {
        const formatString = args[0]
        const parts = formatString.split(/%c/g)
        formattedParts = []

        for (let i = 0; i < parts.length; i++) {
          const text = parts[i]
          const style = i < args.length - 1 && typeof args[i + 1] === 'string' ? args[i + 1] : undefined
          formattedParts.push({ text, style })
        }

        const remainingArgs = args.slice(parts.length)
        if (remainingArgs.length > 0) {
          const remainingText = remainingArgs.map(arg => {
            if (typeof arg === 'object') {
              try { return JSON.stringify(arg, null, 2) } catch { return String(arg) }
            }
            return String(arg)
          }).join(' ')
          if (formattedParts.length > 0) {
            formattedParts[formattedParts.length - 1].text += ' ' + remainingText
          } else {
            formattedParts.push({ text: remainingText })
          }
        }

        message = formattedParts.map(p => p.text).join('')
      } else {
        message = args.map(arg => {
          if (typeof arg === 'object') {
            try { return JSON.stringify(arg, null, 2) } catch { return String(arg) }
          }
          return String(arg)
        }).join(' ')
        formattedParts = [{ text: message }]
      }

      const logEntry = { type, message, formattedParts, timestamp: Date.now() }
      consoleLogRef.current.push(logEntry)
      if (consoleLogRef.current.length > 1000) {
        consoleLogRef.current = consoleLogRef.current.slice(-1000)
      }
      if (showConsoleLogs) {
        setConsoleLogs([...consoleLogRef.current])
      }
    }

    console.log = (...args: any[]) => { captureLog('log', ...args); originalLog.apply(console, args) }
    console.error = (...args: any[]) => { captureLog('error', ...args); originalError.apply(console, args) }
    console.warn = (...args: any[]) => { captureLog('warn', ...args); originalWarn.apply(console, args) }
    console.info = (...args: any[]) => { captureLog('info', ...args); originalInfo.apply(console, args) }

    return () => {
      console.log = originalLog
      console.error = originalError
      console.warn = originalWarn
      console.info = originalInfo
    }
  }, [showConsoleLogs])

  const handleShowConsoleLogs = () => {
    setConsoleLogs([...consoleLogRef.current])
    setShowConsoleLogs(true)
    setConsoleLogSearch('')
    setConsoleLogLevel('all')
  }

  const handleClearConsoleLogs = () => {
    consoleLogRef.current = []
    setConsoleLogs([])
    toast.success(t('Console logs cleared'))
  }

  const filteredConsoleLogs = useMemo(() => {
    let filtered = [...consoleLogs]
    if (consoleLogLevel === 'errors-warnings') {
      filtered = filtered.filter(log => log.type === 'error' || log.type === 'warn')
    }
    if (consoleLogSearch.trim()) {
      const query = consoleLogSearch.toLowerCase().trim()
      filtered = filtered.filter(log =>
        log.message.toLowerCase().includes(query) ||
        log.type.toLowerCase().includes(query)
      )
    }
    return filtered
  }, [consoleLogs, consoleLogSearch, consoleLogLevel])

  const renderConsoleLogList = () =>
    filteredConsoleLogs.length === 0 ? (
      <div className="text-muted-foreground p-4 text-center">
        {consoleLogs.length === 0
          ? t('No console logs captured yet')
          : t('No logs match the current filters')
        }
      </div>
    ) : (
      filteredConsoleLogs.map((log, index) => (
        <div
          key={index}
          className={`p-2 rounded border ${
            log.type === 'error' ? 'bg-red-50 dark:bg-red-950 border-red-200 dark:border-red-800' :
            log.type === 'warn' ? 'bg-yellow-50 dark:bg-yellow-950 border-yellow-200 dark:border-yellow-800' :
            'bg-background border-border'
          }`}
        >
          <div className="flex items-start gap-2">
            <span className="text-muted-foreground text-[10px] whitespace-nowrap">
              {new Date(log.timestamp).toLocaleTimeString()}
            </span>
            <span className="text-muted-foreground text-[10px] whitespace-nowrap">
              [{log.type}]
            </span>
            <pre className="flex-1 overflow-x-auto whitespace-pre-wrap break-words">
              {log.formattedParts ? (
                log.formattedParts.map((part, i) => {
                  if (part.style) {
                    const styleObj: Record<string, string> = {}
                    part.style.split(';').forEach(rule => {
                      const trimmed = rule.trim()
                      if (trimmed) {
                        const colonIndex = trimmed.indexOf(':')
                        if (colonIndex > 0) {
                          const key = trimmed.substring(0, colonIndex).trim()
                          const value = trimmed.substring(colonIndex + 1).trim()
                          const camelKey = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
                          styleObj[camelKey] = value
                        }
                      }
                    })
                    return <span key={i} style={styleObj}>{part.text}</span>
                  }
                  return <span key={i}>{part.text}</span>
                })
              ) : (
                log.message
              )}
            </pre>
          </div>
        </div>
      ))
    )

  const consoleLogFilters = (
    <div className="flex min-w-0 flex-wrap gap-2">
      <Input
        placeholder={t('Search logs...')}
        value={consoleLogSearch}
        onChange={(e) => setConsoleLogSearch(e.target.value)}
        className="min-w-0 flex-1 basis-[min(100%,12rem)]"
      />
      <div className="flex shrink-0 gap-1">
        <Button
          type="button"
          variant={consoleLogLevel === 'errors-warnings' ? 'secondary' : 'outline'}
          size="sm"
          onClick={() => setConsoleLogLevel('errors-warnings')}
        >
          {t('Errors & warnings')}
        </Button>
        <Button
          type="button"
          variant={consoleLogLevel === 'all' ? 'secondary' : 'outline'}
          size="sm"
          onClick={() => setConsoleLogLevel('all')}
        >
          {t('All')}
        </Button>
      </div>
    </div>
  )

  return (
    <div className="space-y-4">
      <div className="text-xs text-muted-foreground space-y-1">
        <div>{t('Clear cached data stored in your browser, including IndexedDB events, localStorage settings, and service worker caches.')}</div>
        <div>
          {t('refreshCacheButtonExplainer', {
            defaultValue:
              'Refresh Cache runs an IndexedDB upgrade check, re-fetches your relay lists and profile-related events from the network (same work as the automatic startup sync), syncs kind-5 deletions into tombstones and removes deleted items from the local cache, then refreshes the store counts below.'
          })}
        </div>
      </div>
      <div className="flex min-w-0 flex-wrap gap-2">
        <Button variant="outline" className="shrink-0" onClick={handleClearCache}>
          <Trash2 className="mr-2 h-4 w-4" />
          {t('Clear Cache')}
        </Button>
        <Button
          variant="outline"
          className="shrink-0"
          onClick={handleRefreshCache}
          disabled={cacheRefreshBusy}
        >
          <RefreshCw className={`mr-2 h-4 w-4 ${cacheRefreshBusy ? 'animate-spin' : ''}`} />
          {t('Refresh Cache')}
        </Button>
        <Button variant="outline" className="shrink-0" onClick={openBrowseCache}>
          <Database className="mr-2 h-4 w-4" />
          {t('Browse Cache')}
        </Button>
        <Button variant="outline" className="shrink-0" onClick={handleClearServiceWorker}>
          <XCircle className="mr-2 h-4 w-4" />
          {t('Clear Service Worker')}
        </Button>
        <Button variant="outline" className="shrink-0" onClick={handleShowConsoleLogs}>
          <Terminal className="mr-2 h-4 w-4" />
          {t('View Console Logs')} ({consoleLogRef.current.length})
        </Button>
      </div>

      {isSmallScreen ? (
        <Drawer open={showConsoleLogs} onOpenChange={setShowConsoleLogs}>
          <DrawerContent className="max-h-[90vh]">
            <DrawerHeader>
              <div className="flex items-center justify-between">
                <div className="flex-1">
                  <DrawerTitle>{t('Console Logs')}</DrawerTitle>
                  <DrawerDescription>
                    {t('View recent console logs for debugging')} ({filteredConsoleLogs.length} / {consoleLogs.length} {t('entries')})
                  </DrawerDescription>
                </div>
                <div className="flex gap-2">
                  <Button variant="outline" size="sm" onClick={handleClearConsoleLogs}>
                    <Trash2 className="h-4 w-4 mr-2" />
                    {t('Clear')}
                  </Button>
                  <Button variant="ghost" size="sm" onClick={() => setShowConsoleLogs(false)}>
                    <X className="h-4 w-4" />
                  </Button>
                </div>
              </div>
            </DrawerHeader>
            <div className="space-y-2 px-4 pb-2">{consoleLogFilters}</div>
            <div className="flex-1 overflow-auto px-4 pb-4">
              <div className="space-y-1 font-mono text-xs">{renderConsoleLogList()}</div>
            </div>
          </DrawerContent>
        </Drawer>
      ) : (
        <Dialog open={showConsoleLogs} onOpenChange={setShowConsoleLogs}>
          <DialogContent className="max-w-4xl max-h-[80vh] flex flex-col" withoutClose>
            <DialogHeader>
              <div className="flex items-center justify-between">
                <div className="flex-1">
                  <DialogTitle>{t('Console Logs')}</DialogTitle>
                  <DialogDescription>
                    {t('View recent console logs for debugging')} ({filteredConsoleLogs.length} / {consoleLogs.length} {t('entries')})
                  </DialogDescription>
                </div>
                <div className="flex gap-2">
                  <Button variant="outline" size="sm" onClick={handleClearConsoleLogs}>
                    <Trash2 className="h-4 w-4 mr-2" />
                    {t('Clear')}
                  </Button>
                  <Button variant="ghost" size="sm" onClick={() => setShowConsoleLogs(false)}>
                    <X className="h-4 w-4" />
                  </Button>
                </div>
              </div>
            </DialogHeader>
            <div className="space-y-2 px-6 pb-4">{consoleLogFilters}</div>
            <div className="flex-1 overflow-auto px-6 pb-4">
              <div className="space-y-1 font-mono text-xs">{renderConsoleLogList()}</div>
            </div>
          </DialogContent>
        </Dialog>
      )}
    </div>
  )
}
