import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@/components/ui/drawer'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Skeleton } from '@/components/ui/skeleton'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { userReadRelaysWithHttp } from '@/lib/favorites-feed-relays'
import { useNostr } from '@/providers/NostrProvider'
import { ExtendedKind, GIF_RELAY_URLS } from '@/constants'
import { normalizeUrl } from '@/lib/url'
import {
  fetchMemes,
  getCachedMemes,
  mergeMemesIntoIdbCache,
  memeMetadataFrom1063Event,
  searchMemes,
  type MemeMetadata
} from '@/services/meme.service'
import mediaUpload from '@/services/media-upload.service'
import { ExternalLink, X } from 'lucide-react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'

/** In-session cache: survives Drawer/Dropdown open↔close without a relay re-fetch. */
let _sessionMemes: MemeMetadata[] = []
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'

const MEMEAMIGO_URL = 'https://www.memeamigo.lol/'
const MEMEAMIGO_SEARCH_URL = (q: string) =>
  q.trim() ? `${MEMEAMIGO_URL}?q=${encodeURIComponent(q.trim())}` : MEMEAMIGO_URL

function mimeFromImageUrl(url: string): string {
  const lower = url.toLowerCase().split('?')[0] ?? ''
  if (lower.endsWith('.png')) return 'image/png'
  if (lower.endsWith('.webp')) return 'image/webp'
  if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg'
  return 'image/jpeg'
}

function isStaticImageFile(file: File): boolean {
  const n = file.name.toLowerCase()
  const t = file.type.toLowerCase()
  return (
    t === 'image/jpeg' ||
    t === 'image/png' ||
    t === 'image/webp' ||
    n.endsWith('.jpg') ||
    n.endsWith('.jpeg') ||
    n.endsWith('.png') ||
    n.endsWith('.webp')
  )
}

export default function MemePicker({
  children,
  onSelect,
  portalContainer
}: {
  children: React.ReactNode
  onSelect?: (imageUrl: string) => void
  portalContainer?: HTMLElement | null
}) {
  const { t } = useTranslation()
  const { isSmallScreen } = useScreenSize()
  const { publish, pubkey, relayList } = useNostr()
  const [open, setOpen] = useState(false)
  const [query, setQuery] = useState('')
  const [searchInput, setSearchInput] = useState('')
  // Initialise from the module-level session cache so re-opens are instant
  const [memes, setMemesState] = useState<MemeMetadata[]>(() => _sessionMemes)
  const memesRef = useRef<MemeMetadata[]>(_sessionMemes)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [uploading, setUploading] = useState(false)
  const [uploadError, setUploadError] = useState<string | null>(null)
  const [pasteUrl, setPasteUrl] = useState('')
  const [publishingPaste, setPublishingPaste] = useState(false)
  const [publishDescription, setPublishDescription] = useState('')
  const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
  const fileInputRef = useRef<HTMLInputElement | null>(null)
  const memeamigoPopupRef = useRef<Window | null>(null)

  const userReadRelays = useMemo(() => userReadRelaysWithHttp(relayList), [relayList])
  const userWriteRelays = relayList?.write ?? []

  /** Keep memesRef, session cache, and React state in sync. */
  const setMemes = useCallback((newMemes: MemeMetadata[], isSearch = false) => {
    memesRef.current = newMemes
    if (!isSearch) _sessionMemes = newMemes
    setMemesState(newMemes)
  }, [])

  const loadMemes = useCallback(
    async (q: string, forceRefresh = false) => {
      setError(null)
      const isSearch = q.trim() !== ''

      if (isSearch) {
        memesRef.current = []
        setMemesState([])
        setLoading(true)
      } else if (memesRef.current.length === 0) {
        try {
          const cached = await getCachedMemes(pubkey ?? null)
          if (cached.length > 0) {
            setMemes(cached)
          }
        } catch { /* ignore */ }
        if (memesRef.current.length === 0) setLoading(true)
      }

      try {
        const results = isSearch
          ? await searchMemes(q.trim(), 50, forceRefresh, userReadRelays, pubkey ?? null)
          : await fetchMemes(undefined, 50, forceRefresh, userReadRelays, pubkey ?? null)
        setMemes(results, isSearch)
        if (results.length === 0 && !isSearch) {
          setError(
            t(
              'No meme templates found. Try searching or open Meme Amigo. The grid only lists kind 1063 (NIP-94) files tagged memeamigo (not random photos from notes).'
            )
          )
        }
      } catch (e) {
        setError(e instanceof Error ? e.message : 'Failed to load memes')
        if (memesRef.current.length === 0) setMemesState([])
      } finally {
        setLoading(false)
      }
    },
    [t, userReadRelays, pubkey, setMemes]
  )

  useEffect(() => {
    if (!open) return
    loadMemes(query)
  }, [open, query, loadMemes])

  useEffect(() => {
    if (!open) return
    if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current)
    searchTimeoutRef.current = setTimeout(() => {
      setQuery(searchInput)
    }, 300)
    return () => {
      if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current)
    }
  }, [searchInput, open])

  const handleSelect = (meme: MemeMetadata) => {
    const url = meme.fallbackUrl || meme.url
    onSelect?.(url)
    setOpen(false)
  }

  const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0]
    if (!file || !pubkey) return
    setUploadError(null)
    setUploading(true)
    try {
      if (!isStaticImageFile(file)) {
        setUploadError(t('{{name}} is not a JPEG, PNG, or WebP file', { name: file.name }))
        return
      }
      const { url } = await mediaUpload.upload(file)
      const mime = file.type || mimeFromImageUrl(url)
      const draft = {
        kind: ExtendedKind.FILE_METADATA,
        content: publishDescription.trim(),
        tags: [
          ['file', url, mime, `size ${file.size}`],
          ['url', url],
          ['m', mime],
          ['t', 'memeamigo']
        ],
        created_at: Math.floor(Date.now() / 1000)
      }
      const writeUrls = [...GIF_RELAY_URLS, ...userWriteRelays]
      const seen = new Set<string>()
      const specifiedRelayUrls = writeUrls.filter((u) => {
        const n = (normalizeUrl(u) ?? u).toLowerCase()
        if (seen.has(n)) return false
        seen.add(n)
        return true
      })
      const published = await publish(draft, { specifiedRelayUrls })
      const meta = memeMetadataFrom1063Event(published)
      if (meta) {
        await mergeMemesIntoIdbCache([meta])
        const next = [meta, ...memesRef.current.filter((m) => m.eventId !== meta.eventId)].slice(0, 50)
        setMemes(next)
      }
      setPublishDescription('')
      setQuery('')
      await loadMemes('', false)
    } catch (err) {
      setUploadError(err instanceof Error ? err.message : 'Upload failed')
    } finally {
      setUploading(false)
      if (fileInputRef.current) fileInputRef.current.value = ''
    }
  }

  const triggerFileUpload = () => fileInputRef.current?.click()

  const isLoggedIn = !!pubkey

  const openMemeAmigoSearch = useCallback(() => {
    const url = MEMEAMIGO_SEARCH_URL(searchInput)
    const w = window.open(url, '_blank', 'noopener,noreferrer')
    memeamigoPopupRef.current = w ?? null
    const handler = (event: MessageEvent) => {
      if (
        event.origin !== 'https://www.memeamigo.lol' &&
        event.origin !== 'https://memeamigo.lol'
      ) {
        return
      }
      const data = event.data
      const urlToInsert =
        typeof data === 'string' && (data.startsWith('http://') || data.startsWith('https://'))
          ? data
          : data?.url ?? data?.imageUrl
      if (urlToInsert && typeof urlToInsert === 'string') {
        window.removeEventListener('message', handler)
        memeamigoPopupRef.current = null
        onSelect?.(urlToInsert)
        setOpen(false)
      }
    }
    window.addEventListener('message', handler)
    const timer = setTimeout(() => {
      window.removeEventListener('message', handler)
      memeamigoPopupRef.current = null
    }, 10 * 60 * 1000)
    if (w)
      w.addEventListener('beforeunload', () => {
        clearTimeout(timer)
        window.removeEventListener('message', handler)
      })
  }, [searchInput, onSelect])

  const descriptionForPublish = publishDescription.trim()

  const handlePasteUrlInsert = useCallback(async () => {
    const url = pasteUrl.trim()
    if (!url || !/^https?:\/\//i.test(url)) return
    onSelect?.(url)
    setPasteUrl('')
    setOpen(false)
    if (pubkey) {
      setPublishingPaste(true)
      try {
        const mime = mimeFromImageUrl(url)
        const draft = {
          kind: ExtendedKind.FILE_METADATA,
          content: descriptionForPublish,
          tags: [
            ['file', url, mime, 'size 0'],
            ['url', url],
            ['m', mime],
            ['t', 'memeamigo']
          ],
          created_at: Math.floor(Date.now() / 1000)
        }
        const writeUrls = [...GIF_RELAY_URLS, ...userWriteRelays]
        const seen = new Set<string>()
        const specifiedRelayUrls = writeUrls.filter((u) => {
          const n = (normalizeUrl(u) ?? u).toLowerCase()
          if (seen.has(n)) return false
          seen.add(n)
          return true
        })
        const published = await publish(draft, { specifiedRelayUrls })
        const meta = memeMetadataFrom1063Event(published)
        if (meta) {
          await mergeMemesIntoIdbCache([meta])
        }
        setPublishDescription('')
      } catch (err) {
        toast.error(
          err instanceof Error ? err.message : t('Failed to publish meme template for the picker')
        )
      } finally {
        setPublishingPaste(false)
      }
    }
  }, [pasteUrl, pubkey, onSelect, publish, userWriteRelays, descriptionForPublish])

  const isDrawer = isSmallScreen
  const content = (
    <div
      className={`flex flex-col gap-2 p-2 ${isDrawer ? 'w-full h-[70vh] max-h-[70vh] overflow-hidden' : 'min-w-[280px] max-w-[360px]'}`}
    >
      <div className="flex items-center gap-1 shrink-0">
        <Input
          placeholder={t('Search memes')}
          value={searchInput}
          onChange={(e) => setSearchInput(e.target.value)}
          className="flex-1"
        />
        <Button
          type="button"
          variant="ghost"
          size="icon"
          className="shrink-0 size-8"
          onClick={() => setOpen(false)}
          aria-label={t('Close')}
        >
          <X className="size-4" />
        </Button>
      </div>
      {error && <p className="text-sm text-muted-foreground px-1 shrink-0">{error}</p>}
      <div
        className={isDrawer ? 'flex-1 min-h-0 flex flex-col' : undefined}
        {...(isDrawer && { 'data-vaul-no-drag': true })}
      >
        <ScrollArea
          className={
            isDrawer
              ? 'flex-1 min-h-[200px] w-full rounded-md border'
              : 'h-[280px] w-full rounded-md border'
          }
        >
          {loading ? (
            <div
              className="grid grid-cols-2 gap-1 p-2 min-h-[200px]"
              role="status"
              aria-busy="true"
              aria-live="polite"
            >
              {Array.from({ length: 8 }).map((_, i) => (
                <Skeleton key={i} className="aspect-square w-full rounded" />
              ))}
            </div>
          ) : (
            <div className="grid grid-cols-2 gap-1 p-2">
              {memes.map((meme) => (
                <button
                  key={meme.eventId}
                  type="button"
                  className="rounded overflow-hidden border border-transparent hover:border-primary focus:border-primary focus:outline-none aspect-square"
                  onClick={() => handleSelect(meme)}
                >
                  <img
                    src={meme.url}
                    alt=""
                    className="w-full h-full object-cover"
                    loading="lazy"
                    onError={(e) => {
                      const el = e.target as HTMLImageElement
                      const fallback = meme.fallbackUrl?.trim()
                      if (fallback && el.dataset.memeFallbackTried !== '1') {
                        el.dataset.memeFallbackTried = '1'
                        el.src = fallback
                        return
                      }
                      el.style.display = 'none'
                    }}
                  />
                </button>
              ))}
            </div>
          )}
        </ScrollArea>
      </div>
      <div className="flex flex-col gap-2 border-t pt-2 shrink-0">
        <div className="flex flex-col gap-1.5">
          <Button
            type="button"
            variant="outline"
            size="sm"
            className="w-full"
            onClick={openMemeAmigoSearch}
          >
            <ExternalLink className="size-3.5 mr-1.5" />
            {t('Search on Meme Amigo')}
          </Button>
          <p className="text-xs text-muted-foreground">
            {t(
              'Opens in a new tab. Copy an image URL there, then paste below. If this picker closed, click “Insert meme” again to paste.'
            )}
          </p>
          <div className="grid gap-1">
            <Label className="text-xs text-muted-foreground">
              {t('Paste URL of a meme image')}
            </Label>
            <div className="flex gap-1">
              <Input
                placeholder="https://..."
                value={pasteUrl}
                onChange={(e) => setPasteUrl(e.target.value)}
                className="flex-1 min-w-0"
              />
              <Button
                type="button"
                size="sm"
                disabled={!pasteUrl.trim() || publishingPaste}
                onClick={handlePasteUrlInsert}
                title={t(
                  'Insert URL into your post and publish kind 1063 (NIP-94) with hashtag memeamigo for discoverability.'
                )}
              >
                {publishingPaste ? t('Adding…') : t('Insert')}
              </Button>
            </div>
          </div>
        </div>
        {isLoggedIn && (
          <div className="grid gap-1">
            <Label className="text-xs text-muted-foreground">
              {t('Description (optional, for search)')}
            </Label>
            <Input
              placeholder={t('e.g. drake, distracted boyfriend')}
              value={publishDescription}
              onChange={(e) => setPublishDescription(e.target.value)}
              className="min-w-0"
            />
          </div>
        )}
        {isLoggedIn && (
          <>
            <input
              ref={fileInputRef}
              type="file"
              accept=".jpg,.jpeg,.png,.webp,image/jpeg,image/png,image/webp"
              className="hidden"
              onChange={handleUpload}
            />
            <Button
              type="button"
              variant="secondary"
              size="sm"
              className="w-full"
              disabled={uploading}
              onClick={triggerFileUpload}
            >
              {uploading ? t('Uploading...') : t('Add your own meme templates')}
            </Button>
            {uploadError && (
              <p className="text-xs text-destructive text-center">{uploadError}</p>
            )}
          </>
        )}
      </div>
    </div>
  )

  if (isSmallScreen) {
    return (
      <Drawer open={open} onOpenChange={setOpen}>
        <DrawerTrigger asChild>{children}</DrawerTrigger>
        <DrawerContent portalContainer={portalContainer}>
          <DrawerHeader className="sr-only">
            <DrawerTitle>{t('Choose a meme')}</DrawerTitle>
          </DrawerHeader>
          {content}
        </DrawerContent>
      </Drawer>
    )
  }

  return (
    <DropdownMenu open={open} onOpenChange={setOpen}>
      <DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
      <DropdownMenuContent side="top" className="p-0" portalContainer={portalContainer}>
        {content}
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
