import { Button } from '@/components/ui/button'
import {
  ResponsiveDialog,
  ResponsiveDialogBody,
  ResponsiveDialogContent,
  ResponsiveDialogHeader,
  ResponsiveDialogTitle,
} from '@/components/ui/responsive-dialog'
import { Input } from '@/components/ui/input'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { VirtualTrackList, type VirtualTrackListRef } from '@/components/VirtualTrackList'
import { PLATFORM_ACTIVE, PLATFORM_TEXT } from '@/lib/platform'
import { cn, trackKey } from '@/lib/utils'
import { useRoomStore } from '@/stores/roomStore'
import { useSearch } from '@/hooks/useSearch'
import { useRecommendations } from '@/hooks/useRecommendations'
import { usePlaylist } from '@/hooks/usePlaylist'
import { useIsMobile } from '@/hooks/useIsMobile'
import { useSocketContext } from '@/providers/socket-context'
import { EVENTS, LIMITS } from '@music-together/shared'
import type { MusicSource, Track, Playlist } from '@music-together/shared'
import type { BilibiliMetadataSource } from '@music-together/shared'
import { Loader2, Music2, Search, ListMusic, Radio, RefreshCw, Sparkles } from 'lucide-react'
import { motion } from 'motion/react'
import { useCallback, useLayoutEffect, useMemo, useRef, useState, useEffect } from 'react'
import { toast } from 'sonner'
import { PlaylistDetail } from './Settings/PlaylistDetail'
import { BilibiliCollectionDialog } from './BilibiliCollectionDialog'
import { BilibiliMetadataDialog } from './BilibiliMetadataDialog'

const EMPTY_QUEUE: Track[] = []
type BilibiliQueueAction = 'add' | 'insert'
type SearchMode = 'song' | 'album' | 'playlist' | 'recommend'
type TencentRecommendationView = 'radar' | 'playlists'

const SOURCES: { id: MusicSource; label: string }[] = [
  { id: 'netease', label: '网易云' },
  { id: 'tencent', label: 'QQ' },
  { id: 'kugou', label: '酷狗' },
  { id: 'kugou_concept', label: '概念版' },
  { id: 'bilibili', label: 'B站' },
]

interface SearchDialogProps {
  open: boolean
  onOpenChange: (open: boolean) => void
  onAddToQueue: (track: Track) => void
  onInsertAfterCurrent: (track: Track) => void
}

interface PlaylistListProps {
  playlists: Playlist[]
  onSelect: (playlist: Playlist) => void
  hasMore?: boolean
  loadingMore?: boolean
  onLoadMore?: () => void
}

function PlaylistList({ playlists, onSelect, hasMore, loadingMore, onLoadMore }: PlaylistListProps) {
  return (
    <div className="min-h-0 flex-1 overflow-y-auto rounded-md border p-2">
      <div className="flex flex-col gap-2">
        {playlists.map((playlist, index) => (
          <button
            key={`${playlist.source}-${playlist.id}-${index}`}
            className="hover:bg-accent flex w-full min-w-0 items-center gap-3 overflow-hidden rounded-lg p-2 text-left transition-colors"
            onClick={() => onSelect(playlist)}
          >
            {playlist.cover ? (
              <img
                src={playlist.cover}
                alt={playlist.name}
                referrerPolicy="no-referrer"
                className="h-12 w-12 shrink-0 rounded-md object-cover"
                loading="lazy"
              />
            ) : (
              <div className="bg-muted flex h-12 w-12 shrink-0 items-center justify-center rounded-md">
                <ListMusic className="text-muted-foreground h-5 w-5" />
              </div>
            )}
            <div className="min-w-0 flex-1">
              <p className="truncate text-sm font-medium">{playlist.name}</p>
              <p className="text-muted-foreground truncate text-xs">
                {[
                  playlist.trackCount > 0 || !['kugou', 'kugou_concept'].includes(playlist.source)
                    ? `${playlist.trackCount} 首`
                    : '',
                  playlist.creator,
                ]
                  .filter(Boolean)
                  .join(' · ')}
              </p>
            </div>
          </button>
        ))}
        {hasMore && onLoadMore && (
          <Button variant="ghost" size="sm" className="mt-2 w-full" onClick={onLoadMore} disabled={loadingMore}>
            {loadingMore ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
            {loadingMore ? '加载中...' : '加载更多'}
          </Button>
        )}
      </div>
    </div>
  )
}

export function SearchDialog({ open, onOpenChange, onAddToQueue, onInsertAfterCurrent }: SearchDialogProps) {
  const [source, setSource] = useState<MusicSource>('netease')
  const [searchType, setSearchType] = useState<SearchMode>('song')
  const [tencentRecommendationView, setTencentRecommendationView] = useState<TencentRecommendationView>('radar')
  const [bilibiliMatch, setBilibiliMatch] = useState<{ track: Track; action: BilibiliQueueAction } | null>(null)
  const [bilibiliCollectionMatch, setBilibiliCollectionMatch] = useState<{
    track: Track
    action: BilibiliQueueAction
  } | null>(null)
  const [keyword, setKeyword] = useState('')
  const [addedIds, setAddedIds] = useState<Set<string>>(new Set())
  const listRef = useRef<VirtualTrackListRef>(null)
  const dialogContentRef = useRef<HTMLDivElement>(null)
  const sourceContainerRef = useRef<HTMLDivElement>(null)
  const [pillStyle, setPillStyle] = useState({ left: 0, width: 0 })
  const queue = useRoomStore((s) => s.room?.queue ?? EMPTY_QUEUE)
  const roomId = useRoomStore((s) => s.room?.id)
  const queueKeys = useMemo(() => new Set(queue.map(trackKey)), [queue])
  const { socket } = useSocketContext()
  const isMobile = useIsMobile()

  // Mobile browsers can pan the visual viewport when the keyboard focuses the
  // search input, which moves even fixed drawers above the visible screen.
  // Keep this search drawer pinned to the actual visible viewport instead.
  useLayoutEffect(() => {
    if (!open || !isMobile) return

    const content = dialogContentRef.current
    const viewport = window.visualViewport
    if (!content || !viewport) return

    let frame = 0
    const syncToVisualViewport = () => {
      cancelAnimationFrame(frame)
      frame = requestAnimationFrame(() => {
        content.style.setProperty('top', `${Math.max(0, viewport.offsetTop)}px`, 'important')
        content.style.setProperty('bottom', 'auto', 'important')
        content.style.setProperty('height', `${viewport.height}px`, 'important')
        content.style.setProperty('max-height', `${viewport.height}px`, 'important')
      })
    }

    syncToVisualViewport()
    viewport.addEventListener('resize', syncToVisualViewport)
    viewport.addEventListener('scroll', syncToVisualViewport)

    return () => {
      cancelAnimationFrame(frame)
      viewport.removeEventListener('resize', syncToVisualViewport)
      viewport.removeEventListener('scroll', syncToVisualViewport)
      content.style.removeProperty('top')
      content.style.removeProperty('bottom')
      content.style.removeProperty('height')
      content.style.removeProperty('max-height')
    }
  }, [open, isMobile])

  // Album Detail view state
  const [selectedAlbum, setSelectedAlbum] = useState<Playlist | null>(null)
  const {
    playlistTracks,
    playlistTotal,
    tracksLoading,
    loadingMore: albumLoadingMore,
    hasMoreTracks,
    fetchPlaylistTracks,
    loadMoreTracks,
    fetchAllPlaylistTracks,
  } = usePlaylist()

  const { results, loading, loadingMore, hasMore, hasSearched, search, loadMore, resetState } = useSearch(
    source,
    searchType === 'recommend' ? 'song' : searchType,
    roomId,
  )
  const {
    recommendations,
    loading: recommendationsLoading,
    loadingMore: recommendationsLoadingMore,
    loaded: recommendationsLoaded,
    load: loadRecommendations,
    loadMore: loadMoreRecommendations,
    reset: resetRecommendations,
  } = useRecommendations(roomId)
  const activeRecommendation = useMemo(
    () => recommendations.find((recommendation) => recommendation.platform === source),
    [recommendations, source],
  )
  const visibleSources = useMemo(() => {
    if (searchType !== 'recommend') return SOURCES
    if (!recommendationsLoaded) return SOURCES
    const available = new Set(recommendations.map((recommendation) => recommendation.platform))
    return SOURCES.filter((item) => available.has(item.id))
  }, [recommendations, recommendationsLoaded, searchType])

  // Auto re-search when source or type changes
  const prevSourceRef = useRef(source)
  const prevTypeRef = useRef(searchType)
  useEffect(() => {
    const sourceChanged = prevSourceRef.current !== source
    const typeChanged = prevTypeRef.current !== searchType
    prevSourceRef.current = source
    prevTypeRef.current = searchType
    if ((sourceChanged || typeChanged) && searchType !== 'recommend' && keyword.trim()) {
      search(keyword.trim())
      if (searchType === 'song') listRef.current?.scrollToTop()
    }
  }, [source, searchType, keyword, search])

  useEffect(() => {
    if (open && searchType === 'recommend' && !recommendationsLoaded && !recommendationsLoading) {
      loadRecommendations()
    }
  }, [loadRecommendations, open, recommendationsLoaded, recommendationsLoading, searchType])

  useEffect(() => {
    if (searchType !== 'recommend' || !recommendationsLoaded || recommendations.length === 0) return
    if (!recommendations.some((recommendation) => recommendation.platform === source)) {
      const frame = requestAnimationFrame(() => setSource(recommendations[0]!.platform))
      return () => cancelAnimationFrame(frame)
    }
  }, [recommendations, recommendationsLoaded, searchType, source])

  // Measure active source button position for sliding pill
  const measurePill = useCallback(() => {
    const container = sourceContainerRef.current
    if (!container) return
    const activeBtn = container.querySelector<HTMLButtonElement>(`[data-source="${source}"]`)
    if (!activeBtn) return
    setPillStyle({ left: activeBtn.offsetLeft, width: activeBtn.offsetWidth })
  }, [source])

  useLayoutEffect(() => {
    measurePill()
  }, [measurePill, visibleSources])

  // Re-measure after dialog opens (DOM may not be ready on first render)
  useEffect(() => {
    if (open) requestAnimationFrame(measurePill)
  }, [open, measurePill])

  useEffect(() => {
    if (open) return
    const frame = requestAnimationFrame(() => {
      setSelectedAlbum(null)
      resetRecommendations()
    })
    return () => cancelAnimationFrame(frame)
  }, [open, resetRecommendations])

  const handleSearch = (overrideKeyword?: string) => {
    const searchKeyword = (overrideKeyword ?? keyword).trim()
    if (!searchKeyword) return
    if (overrideKeyword !== undefined) setKeyword(overrideKeyword)
    setAddedIds(new Set())
    search(searchKeyword)
    if (searchType === 'song') {
      listRef.current?.scrollToTop()
    }
  }

  const beginBilibiliMetadataMatch = useCallback((track: Track, action: BilibiliQueueAction) => {
    setBilibiliMatch({ track, action })
  }, [])

  const beginBilibiliCollectionMatch = useCallback((track: Track, action: BilibiliQueueAction) => {
    setBilibiliCollectionMatch({ track, action })
  }, [])

  const handleBilibiliNotCollection = useCallback(() => {
    if (!bilibiliCollectionMatch) return
    const { track, action } = bilibiliCollectionMatch
    setBilibiliCollectionMatch(null)
    beginBilibiliMetadataMatch(track, action)
  }, [bilibiliCollectionMatch, beginBilibiliMetadataMatch])

  const handleBilibiliCollectionTrack = useCallback(
    (track: Track) => {
      if (!bilibiliCollectionMatch) return
      // Close the picker before opening the metadata dialog. Both dialogs use
      // the same portal z-index, so leaving it open would cover the matcher.
      const { action } = bilibiliCollectionMatch
      setBilibiliCollectionMatch(null)
      beginBilibiliMetadataMatch(track, action)
    },
    [bilibiliCollectionMatch, beginBilibiliMetadataMatch],
  )

  const applyBilibiliMetadataMatch = useCallback(
    (metadataTrack: Track, metadataSource: BilibiliMetadataSource) => {
      if (!bilibiliMatch) return
      const track = {
        ...bilibiliMatch.track,
        metadataSource,
        lyricId: metadataTrack.lyricId,
        picId: metadataTrack.picId,
        cover: metadataTrack.cover || bilibiliMatch.track.cover,
      }
      if (bilibiliMatch.action === 'insert') {
        onInsertAfterCurrent(track)
      } else {
        onAddToQueue(track)
      }
      setAddedIds((prev) => new Set(prev).add(trackKey(track)))
      setBilibiliMatch(null)
    },
    [bilibiliMatch, onAddToQueue, onInsertAfterCurrent],
  )

  const skipBilibiliMetadataMatch = useCallback(() => {
    if (!bilibiliMatch) return
    if (bilibiliMatch.action === 'insert') {
      onInsertAfterCurrent(bilibiliMatch.track)
    } else {
      onAddToQueue(bilibiliMatch.track)
    }
    setAddedIds((prev) => new Set(prev).add(trackKey(bilibiliMatch.track)))
    setBilibiliMatch(null)
  }, [bilibiliMatch, onAddToQueue, onInsertAfterCurrent])

  const handleAdd = useCallback(
    (track: Track) => {
      const key = trackKey(track)
      if (queueKeys.has(key) || addedIds.has(key)) {
        toast.info(`「${track.title}」已在队列中`)
        return
      }
      if (track.source === 'bilibili') {
        beginBilibiliCollectionMatch(track, 'add')
        return
      }
      onAddToQueue(track)
      setAddedIds((prev) => new Set(prev).add(key))
      // Removed duplicate toast.success since onAddToQueue (from useQueue) usually already handles it
      // or the UI handles feedback.
    },
    [onAddToQueue, queueKeys, addedIds, beginBilibiliCollectionMatch],
  )

  const handleInsertAfterCurrent = useCallback(
    (track: Track) => {
      const key = trackKey(track)
      if (queueKeys.has(key) || addedIds.has(key)) {
        toast.info(`「${track.title}」已在队列中`)
        return
      }
      if (track.source === 'bilibili') {
        beginBilibiliCollectionMatch(track, 'insert')
        return
      }
      onInsertAfterCurrent(track)
      setAddedIds((prev) => new Set(prev).add(key))
      // Removed duplicate toast.success
    },
    [onInsertAfterCurrent, queueKeys, addedIds, beginBilibiliCollectionMatch],
  )

  const handleAddBatch = useCallback(
    (tracks: Track[], playlistName?: string) => {
      if (tracks.length === 0) return
      for (let offset = 0; offset < tracks.length; offset += LIMITS.QUEUE_BATCH_MAX_SIZE) {
        socket.emit(EVENTS.QUEUE_ADD_BATCH, {
          tracks: tracks.slice(offset, offset + LIMITS.QUEUE_BATCH_MAX_SIZE),
          playlistName,
        })
      }
      setAddedIds((prev) => {
        const next = new Set(prev)
        for (const t of tracks) next.add(trackKey(t))
        return next
      })
      toast.success(`已添加 ${tracks.length} 首歌曲`)
    },
    [socket],
  )

  const isTrackAdded = useCallback(
    (track: Track) => {
      const key = trackKey(track)
      return addedIds.has(key) || queueKeys.has(key)
    },
    [addedIds, queueKeys],
  )

  const handleSelectAlbum = (playlist: Playlist, type?: 'album' | 'playlist') => {
    setSelectedAlbum(playlist)
    fetchPlaylistTracks(
      playlist.source,
      playlist.id,
      playlist.trackCount,
      type ?? (searchType === 'album' ? 'album' : 'playlist'),
    )
  }

  return (
    <ResponsiveDialog open={open} onOpenChange={onOpenChange} mobileRepositionInputs={false}>
      <ResponsiveDialogContent
        ref={dialogContentRef}
        className="flex h-dvh max-h-dvh flex-col overflow-hidden sm:h-auto sm:max-h-[80vh] sm:max-w-2xl"
      >
        <ResponsiveDialogHeader>
          <div className="flex items-center gap-3">
            <ResponsiveDialogTitle className="shrink-0">
              {selectedAlbum ? selectedAlbum.name : searchType === 'recommend' ? '推荐点歌' : '搜索点歌'}
            </ResponsiveDialogTitle>
            {!selectedAlbum && visibleSources.length > 0 && (
              <div ref={sourceContainerRef} className="bg-muted/50 relative flex items-center rounded-lg p-0.5">
                <motion.div
                  className={cn('absolute inset-y-0.5 rounded-md', PLATFORM_ACTIVE[source])}
                  animate={{ left: pillStyle.left, width: pillStyle.width }}
                  transition={{ type: 'spring', bounce: 0.15, duration: 0.3 }}
                />
                {visibleSources.map((s) => (
                  <button
                    key={s.id}
                    data-source={s.id}
                    className={cn(
                      'relative z-10 rounded-md px-2.5 py-0.5 text-xs font-medium transition-colors',
                      source === s.id ? PLATFORM_TEXT[s.id] : 'text-muted-foreground hover:text-foreground',
                    )}
                    onClick={() => {
                      setSource(s.id)
                      if (
                        (s.id === 'bilibili' || s.id === 'kugou_concept') &&
                        (searchType === 'album' || searchType === 'playlist')
                      ) {
                        setSearchType('song')
                      }
                      resetState()
                      setAddedIds(new Set())
                    }}
                  >
                    {s.label}
                  </button>
                ))}
              </div>
            )}
          </div>
        </ResponsiveDialogHeader>

        <ResponsiveDialogBody className="flex min-h-0 flex-1 flex-col gap-4 overflow-hidden">
          {selectedAlbum ? (
            <PlaylistDetail
              playlist={selectedAlbum}
              tracks={playlistTracks}
              loading={tracksLoading}
              loadingMore={albumLoadingMore}
              hasMore={hasMoreTracks}
              total={playlistTotal}
              onBack={() => setSelectedAlbum(null)}
              onAddTrack={handleAdd}
              onInsertAfterCurrent={handleInsertAfterCurrent}
              onAddAll={handleAddBatch}
              onLoadAll={fetchAllPlaylistTracks}
              onLoadMore={loadMoreTracks}
            />
          ) : (
            <>
              <Tabs
                value={searchType}
                onValueChange={(value) => {
                  setSearchType(value as SearchMode)
                  resetState()
                  setAddedIds(new Set())
                }}
              >
                <TabsList className="w-full">
                  <TabsTrigger value="song" className="flex-1 text-xs sm:text-sm">
                    单曲
                  </TabsTrigger>
                  <TabsTrigger value="recommend" className="flex-1 gap-1 text-xs sm:text-sm">
                    <Sparkles className="h-3.5 w-3.5" />
                    推荐
                  </TabsTrigger>
                  {source !== 'bilibili' && source !== 'kugou_concept' && (
                    <>
                      <TabsTrigger value="album" className="flex-1 text-xs sm:text-sm">
                        专辑
                      </TabsTrigger>
                      <TabsTrigger value="playlist" className="flex-1 text-xs sm:text-sm">
                        歌单
                      </TabsTrigger>
                    </>
                  )}
                </TabsList>
              </Tabs>

              {searchType === 'recommend' ? (
                <div className="bg-muted/40 flex items-center justify-between gap-3 rounded-lg border px-3 py-2">
                  <div className="flex min-w-0 items-center gap-2 text-sm">
                    <Sparkles className={cn('h-4 w-4 shrink-0', PLATFORM_TEXT[source])} />
                    <span className="truncate">展示当前账号在平台上的原生推荐内容</span>
                  </div>
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={() => loadRecommendations()}
                    disabled={recommendationsLoading || recommendationsLoadingMore}
                    aria-label="刷新推荐"
                  >
                    <RefreshCw className={cn('h-4 w-4', recommendationsLoading && 'animate-spin')} />
                  </Button>
                </div>
              ) : (
                <div className="flex gap-2">
                  <Input
                    placeholder={
                      source === 'bilibili'
                        ? '搜索视频，或粘贴 B站链接 / BV号...'
                        : searchType === 'song'
                          ? '搜索歌曲、歌手...'
                          : searchType === 'album'
                            ? '搜索专辑...'
                            : '搜索歌单...'
                    }
                    value={keyword}
                    onChange={(e) => setKeyword(e.target.value)}
                    onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
                    className="flex-1"
                    autoFocus
                    aria-label="搜索关键词"
                  />
                  <Button onClick={() => handleSearch()} disabled={loading} aria-label="搜索">
                    {loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <Search className="h-4 w-4" />}
                  </Button>
                </div>
              )}

              {/* Results area — virtual scrolling with auto-load */}
              {searchType === 'recommend' ? (
                !recommendationsLoaded || recommendationsLoading ? (
                  <div className="flex min-h-0 flex-1 items-center justify-center rounded-md border">
                    <Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
                  </div>
                ) : recommendations.length === 0 ? (
                  <div className="flex min-h-0 flex-1 items-center justify-center rounded-md border">
                    <div className="text-muted-foreground flex h-48 flex-col items-center justify-center gap-2 px-6 text-center">
                      <Sparkles className="h-8 w-8" />
                      <span className="text-sm">请先在设置中登录音乐平台，再查看平台推荐</span>
                    </div>
                  </div>
                ) : source === 'bilibili' ? (
                  <VirtualTrackList
                    ref={listRef}
                    tracks={activeRecommendation?.tracks ?? []}
                    loading={false}
                    hasMore={false}
                    loadingMore={false}
                    onLoadMore={() => undefined}
                    isTrackAdded={isTrackAdded}
                    onAddTrack={handleAdd}
                    onInsertAfterCurrent={handleInsertAfterCurrent}
                    onArtistClick={(artist) => {
                      setSearchType('song')
                      handleSearch(artist)
                    }}
                    emptyIcon={<Sparkles className="h-8 w-8" />}
                    emptyMessage={
                      activeRecommendation?.unavailableReason === 'upstream_unavailable'
                        ? '平台推荐暂时不可用，请刷新重试'
                        : '平台暂时没有返回推荐内容'
                    }
                  />
                ) : source === 'tencent' ? (
                  <Tabs
                    value={tencentRecommendationView}
                    onValueChange={(value) => setTencentRecommendationView(value as TencentRecommendationView)}
                    className="min-h-0 flex-1 gap-2"
                  >
                    <TabsList className="grid w-full grid-cols-2">
                      <TabsTrigger value="radar">
                        <Radio />
                        雷达歌曲
                      </TabsTrigger>
                      <TabsTrigger value="playlists">
                        <ListMusic />
                        推荐歌单
                      </TabsTrigger>
                    </TabsList>
                    <TabsContent value="radar" className="flex min-h-0 flex-1 flex-col">
                      <VirtualTrackList
                        ref={listRef}
                        tracks={activeRecommendation?.tracks ?? []}
                        loading={false}
                        hasMore={activeRecommendation?.pagination?.tracks?.hasMore ?? false}
                        loadingMore={recommendationsLoadingMore}
                        onLoadMore={loadMoreRecommendations}
                        isTrackAdded={isTrackAdded}
                        onAddTrack={handleAdd}
                        onInsertAfterCurrent={handleInsertAfterCurrent}
                        onArtistClick={(artist) => {
                          setSearchType('song')
                          handleSearch(artist)
                        }}
                        emptyIcon={<Radio className="h-8 w-8" />}
                        emptyMessage={
                          activeRecommendation?.unavailableReason === 'upstream_unavailable'
                            ? 'QQ 雷达暂时不可用，请刷新重试'
                            : 'QQ 雷达暂时没有返回歌曲'
                        }
                      />
                    </TabsContent>
                    <TabsContent value="playlists" className="flex min-h-0 flex-1 flex-col">
                      {(activeRecommendation?.playlists?.length ?? 0) > 0 ? (
                        <PlaylistList
                          playlists={activeRecommendation?.playlists ?? []}
                          onSelect={(playlist) => handleSelectAlbum(playlist, 'playlist')}
                          hasMore={activeRecommendation?.pagination?.playlists?.hasMore ?? false}
                          loadingMore={recommendationsLoadingMore}
                          onLoadMore={loadMoreRecommendations}
                        />
                      ) : (
                        <div className="flex min-h-0 flex-1 items-center justify-center rounded-md border">
                          <div className="text-muted-foreground flex h-48 flex-col items-center justify-center gap-2 px-6 text-center">
                            <ListMusic className="h-8 w-8" />
                            <span className="text-sm">
                              {activeRecommendation?.unavailableReason === 'upstream_unavailable'
                                ? 'QQ 推荐歌单暂时不可用，请刷新重试'
                                : 'QQ 暂时没有返回推荐歌单'}
                            </span>
                          </div>
                        </div>
                      )}
                    </TabsContent>
                  </Tabs>
                ) : (activeRecommendation?.playlists?.length ?? 0) > 0 ? (
                  <PlaylistList
                    playlists={activeRecommendation?.playlists ?? []}
                    onSelect={(playlist) => handleSelectAlbum(playlist, 'playlist')}
                  />
                ) : (
                  <div className="flex min-h-0 flex-1 items-center justify-center rounded-md border">
                    <div className="text-muted-foreground flex h-48 flex-col items-center justify-center gap-2 px-6 text-center">
                      <Sparkles className="h-8 w-8" />
                      <span className="text-sm">
                        {activeRecommendation?.unavailableReason === 'upstream_unavailable'
                          ? '平台推荐暂时不可用，请刷新重试'
                          : '平台暂时没有返回推荐歌单'}
                      </span>
                    </div>
                  </div>
                )
              ) : hasSearched ? (
                searchType === 'song' ? (
                  <VirtualTrackList
                    ref={listRef}
                    tracks={results as Track[]}
                    loading={loading}
                    hasMore={hasMore}
                    loadingMore={loadingMore}
                    onLoadMore={loadMore}
                    isTrackAdded={isTrackAdded}
                    onAddTrack={handleAdd}
                    onInsertAfterCurrent={handleInsertAfterCurrent}
                    onArtistClick={(artist) => {
                      setSearchType('song')
                      handleSearch(artist)
                    }}
                    emptyIcon={<Music2 className="h-8 w-8" />}
                    emptyMessage={
                      source === 'bilibili' ? '暂无结果，请检查链接、BV号或更换关键词' : '暂无结果，换个关键词试试'
                    }
                  />
                ) : loading && results.length === 0 ? (
                  <div className="flex min-h-0 flex-1 items-center justify-center rounded-md border">
                    <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
                  </div>
                ) : results.length === 0 ? (
                  <div className="flex min-h-0 flex-1 items-center justify-center rounded-md border">
                    <div className="flex h-48 flex-col items-center justify-center gap-2 text-muted-foreground">
                      <Music2 className="h-8 w-8" />
                      <span className="text-sm">暂无结果，换个关键词试试</span>
                    </div>
                  </div>
                ) : (
                  <PlaylistList
                    playlists={results as Playlist[]}
                    onSelect={handleSelectAlbum}
                    hasMore={hasMore}
                    loadingMore={loadingMore}
                    onLoadMore={loadMore}
                  />
                )
              ) : (
                <div className="min-h-0 flex-1 overflow-y-auto rounded-md border">
                  <div className="flex h-48 flex-col items-center justify-center gap-2 text-muted-foreground">
                    <Music2 className="h-8 w-8" />
                    <span className="text-sm">输入关键词开始搜索</span>
                  </div>
                </div>
              )}
            </>
          )}
        </ResponsiveDialogBody>
      </ResponsiveDialogContent>

      <BilibiliMetadataDialog
        track={bilibiliMatch?.track ?? null}
        roomId={roomId}
        onOpenChange={(isOpen) => !isOpen && setBilibiliMatch(null)}
        onSelect={applyBilibiliMetadataMatch}
        onSkip={skipBilibiliMetadataMatch}
      />
      <BilibiliCollectionDialog
        track={bilibiliCollectionMatch?.track ?? null}
        onOpenChange={(isOpen) => !isOpen && setBilibiliCollectionMatch(null)}
        onSelectTrack={handleBilibiliCollectionTrack}
        onNotCollection={handleBilibiliNotCollection}
        isTrackAdded={isTrackAdded}
      />
    </ResponsiveDialog>
  )
}
