<template>
  <div class="search-result-page">
    <!-- 顶部固定搜索栏 -->
    <div class="top-search-bar">
      <div class="search-bar-inner">
        <router-link to="/" class="logo-link" title="返回首页">
          <img v-if="siteLogo" :src="siteLogo" :alt="siteName || '首页'" class="logo-img" @error="(e: any) => e.target.style.display='none'" />
          <div v-else-if="siteName" class="logo-text-only">{{ siteName }}</div>
          <div v-else class="logo-icon">
            <svg viewBox="0 0 28 28" width="28" height="28" fill="none">
              <circle cx="14" cy="14" r="13" stroke="var(--primary-color)" stroke-width="2"/>
              <path d="M8 14l4 4 8-8" stroke="var(--primary-color)" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
          </div>
        </router-link>
        <div class="search-box-inner">
          <el-input
            v-model="query"
            placeholder="搜索网盘资源，或粘贴视频/网盘链接..."
            size="large"
            clearable
            @keyup.enter="handleSearch"
          >
            <template #prefix>
              <el-icon><Search /></el-icon>
            </template>
          </el-input>
          <el-button type="primary" size="large" @click="handleSearch" class="result-search-btn">搜 索</el-button>
        </div>
      </div>
      <!-- 右上角登录/用户信息，独立于搜索栏 -->
      <div class="top-right-user">
        <template v-if="userInfo">
          <span class="user-badge">{{ userInfo.username }}</span>
          <el-button size="small" text @click="handleLogout">退出</el-button>
        </template>
        <el-button v-else size="small" @click="showLogin = true">登录</el-button>
      </div>
    </div>

    <!-- 滚动通知条（跑马灯） -->
    <div v-if="siteMarquee" class="marquee-bar">
      <span class="marquee-icon marquee-icon-left">📢</span>
      <div class="marquee-track">
        <span class="marquee-text">{{ siteMarquee }}</span>
      </div>
      <span class="marquee-icon marquee-icon-right">📢</span>
    </div>

    <div class="result-content">
      <!-- 搜索结果信息栏 -->
      <div v-if="intent === 'SEARCH' && !loading" class="result-info-bar">
        <div class="info-left">
          <span v-if="totalCount > 0" class="info-item info-count">已为您挑选到最符合 {{ totalCount }} 条结果</span>
          <span v-if="cloudTypesCount > 0" class="info-item info-type">📂 {{ cloudTypesCount }} 个网盘</span>
          <span v-if="filteredCount > 0" class="filter-badge">❌ 失效 {{ filteredCount }}</span>
          <span v-if="skippedCount > 0" class="skip-badge">⏭ 跳过 {{ skippedCount }}</span>
        </div>
        <div class="info-right">
          <span v-if="searchTime > 0" class="info-item info-time">⏱ {{ searchTime }}ms</span>
          <span v-if="hasMore" class="info-hasmore">📄 第 {{ currentPage }} 页</span>
          <button class="refresh-btn" @click="handleRefresh" title="强制刷新">🔄 刷新</button>
        </div>
      </div>

      <!-- 意图标签 -->

      <!-- 搜索进度条 -->
      <div v-if="loading" class="loading-section">
        <div class="progress-track">
          <div class="progress-bar" :style="{ width: loadingProgress + '%' }"></div>
        </div>
        <div class="progress-label">
          <span v-if="loadingPhase === 'search'">🔍 正在搜索中...</span>
          <span v-else-if="loadingPhase === 'validate'">
            ✅ 正在验证链接有效性
            <span v-if="validationTotal > 0" class="validate-count">
              ({{ validationDone }} / {{ validationTotal }})
            </span>
          </span>
          <span v-else>⏳ 加载中...</span>
          <span class="progress-time">⏱ {{ searchTime }}ms</span>
        </div>
        <el-skeleton :rows="3" animated class="loading-skeleton" />
      </div>

      <!-- 网盘分类标签栏 — 仅显示有结果的类型 -->
      <div v-if="intent === 'SEARCH' && visibleTabs.length > 0 && !loading" class="cloud-tabs">
        <div
          v-for="tab in visibleTabs"
          :key="tab.type || 'all'"
          class="cloud-tab"
          :class="{ active: activeCloudTab === (tab.type || '') }"
          @click="activeCloudTab = tab.type || ''"
        >
          <img v-if="tab.icon && (tab.icon.startsWith('data:') || tab.icon.startsWith('http') || tab.icon.startsWith('/'))" :src="tab.icon" class="tab-icon-img" />
          <span v-else-if="tab.icon" class="tab-icon">{{ tab.icon }}</span>
          {{ tab.label }}
          <span v-if="tab.count > 0" class="tab-count">{{ tab.count }}</span>
        </div>
      </div>

      <!-- 内容信息（紧凑信息条） -->
      <div v-if="!loading && (contentInfo || contentTags.length > 0) && intent === 'SEARCH'" class="media-strip">
        <a
          v-if="contentInfo?.tmdb_url"
          :href="contentInfo.tmdb_url"
          target="_blank"
          class="media-strip-inner"
          rel="noopener"
        >
          <span v-if="contentInfo?.cover && !coverError" class="strip-thumb">
            <img :src="contentInfo.cover" @error="coverError = true" />
          </span>
          <span v-else class="strip-thumb strip-thumb-fallback">🎬</span>
          <span class="strip-title">{{ contentInfo?.title || query }}</span>
          <span v-if="contentInfo?.year" class="strip-year">{{ contentInfo.year }}</span>
          <span v-if="contentInfo?.rating" class="strip-rating">⭐ {{ contentInfo.rating }}</span>
          <span v-if="contentInfo?.genres?.length" class="strip-genres">
            <span v-for="(g, gi) in contentInfo.genres.slice(0, 3)" :key="gi" class="strip-genre"> {{ g }}</span>
          </span>
          <span v-if="contentTags.length > 0" class="strip-tags">
            <span v-for="tag in contentTags.slice(0, 3)" :key="tag" class="strip-tag">{{ tag }}</span>
          </span>
          <span class="strip-right">信息来源 TMDB · 更多详情 →</span>
        </a>
        <div v-else class="media-strip-inner">
          <span v-if="contentInfo?.cover && !coverError" class="strip-thumb">
            <img :src="contentInfo.cover" @error="coverError = true" />
          </span>
          <span v-else class="strip-thumb strip-thumb-fallback">🎬</span>
          <span class="strip-title">{{ contentInfo?.title || query }}</span>
          <span v-if="contentInfo?.year" class="strip-year">{{ contentInfo.year }}</span>
          <span v-if="contentInfo?.rating" class="strip-rating">⭐ {{ contentInfo.rating }}</span>
          <span v-if="contentInfo?.genres?.length" class="strip-genres">
            <span v-for="(g, gi) in contentInfo.genres.slice(0, 3)" :key="gi" class="strip-genre"> {{ g }}</span>
          </span>
          <span v-if="contentTags.length > 0" class="strip-tags">
            <span v-for="tag in contentTags.slice(0, 3)" :key="tag" class="strip-tag">{{ tag }}</span>
          </span>
          <span class="strip-right">信息来源 TMDB · 更多详情 →</span>
        </div>
      </div>

      <!-- 结果展示 -->
      <template v-if="!loading && intent === 'SEARCH'">
        <!-- 全部标签：按时间分组 + 卡片网格 -->
        <div v-if="!activeCloudTab && displayedResults.length > 0" class="result-list flat-list">
          <template v-for="(item, ri) in displayedResults" :key="'flat-' + ri">
            <ResultCard
              :data="item"
              :fallbackTags="contentTags"
              :fallbackImage="fallbackImage"
              :loggedIn="userInfo !== null"
              :cloudTypeMap="cloudTypeMap"
              @save="handleSave"
            />
          </template>
          <!-- 页面内加载更多 -->
          <div v-if="hasMoreLocal" class="load-more-inline">
            <el-button @click="loadMoreLocal" :loading="loadingMore" class="load-more-btn">
              加载更多 (已显示 {{ visibleCount }} / {{ allResultsFlat.length }})
            </el-button>
          </div>
        </div>
        <!-- 具体网盘标签：频道分组展示 -->
        <div v-else-if="activeCloudTab && filteredChannels.length > 0" class="result-list channel-list">
          <div
            v-for="(channel, ci) in filteredChannels"
            :key="'ch-' + channel.cloud_type"
            class="channel-section"
          >
            <span class="channel-header">
              <img v-if="channelIcon(channel.cloud_type).startsWith('data:') || channelIcon(channel.cloud_type).startsWith('http') || channelIcon(channel.cloud_type).startsWith('/')" :src="channelIcon(channel.cloud_type)" class="channel-icon-img" />
              <span v-else class="channel-icon">{{ channelIcon(channel.cloud_type) }}</span>
              <span class="channel-label">{{ channel.label }}</span>
              <span class="channel-total-badge">{{ channel.count }} 条资源</span>
              <span v-if="channel.newestTime" class="channel-time">🕐 {{ channel.newestTime }}</span>
            </span>
            <ResultCard
              v-for="(item, ri) in channelVisibleItems(channel)"
              :key="'ch-' + ci + '-' + ri"
              :data="item"
              :fallbackTags="contentTags"
              :fallbackImage="fallbackImage"
              :loggedIn="userInfo !== null"
              :cloudTypeMap="cloudTypeMap"
              @save="handleSave"
            />
            <div v-if="channelHasMore(channel)" class="channel-load-more" @click="channelLoadMore(channel.cloud_type)">
              <span class="channel-load-more-text">
                展开更多 (已显示 {{ channelVisibleItems(channel).length }} / {{ channel.count }})
              </span>
            </div>
          </div>
        </div>
        <!-- 点击分类标签后无匹配结果 -->
        <div v-else-if="totalCount > 0 && activeCloudTab" class="no-match-tip">
          <span>当前页暂无「{{ getActiveTabLabel() }}」资源</span>
          <el-button size="small" @click="loadMore" :loading="loadingMore" v-if="hasMore">
            加载更多试试
          </el-button>
        </div>
      </template>

      <!-- 视频解析结果 -->
      <div v-else-if="!loading && intent === 'VIDEO_PARSE'" class="result-list">
        <VideoResultCard
          v-for="(item, index) in videoResults"
          :key="index"
          :data="item"
          @save="handleVideoSave"
        />
      </div>

      <!-- 空状态（智能分析） -->
      <div v-if="!loading && !loadingMore && totalCount === 0 && results.length === 0" class="empty-wrapper">
        <div class="empty-icon">🔍</div>
        <div class="empty-title">没有找到相关资源</div>
        <div class="empty-hint">{{ emptyHint }}</div>
        <div v-if="emptyTips.length > 0" class="empty-tips">
          <div v-for="(tip, ti) in emptyTips" :key="ti" class="empty-tip-item">💡 {{ tip }}</div>
        </div>
      </div>

      <!-- 加载更多 -->
      <div v-if="hasMore && intent === 'SEARCH' && !loading" class="load-more">
        <el-button :loading="loadingMore" @click="loadMore">加载更多 ({{ currentPage }}/{{ totalPages }})</el-button>
      </div>
    </div>

    <!-- 保存结果弹窗 -->
    <el-dialog v-model="resultDialogVisible" width="650px" :close-on-click-modal="false" class="save-dialog">
      <template #header>
        <strong class="dialog-title-bold">{{ dialogTitle }}</strong>
      </template>
      <div class="result-dialog-content">
        <!-- 进度流程 -->
        <div v-if="saving" class="progress-flow">
          <div class="progress-step" :class="{ active: progressStep >= 1, done: progressStep > 1 }">
            <div class="step-dot">
              <span v-if="progressStep > 1" class="step-check">✓</span>
              <span v-else class="step-num">1</span>
            </div>
            <div class="step-body">
              <span class="step-title">正在转存到{{ diskLabel }}...</span>
              <span v-if="progressStep === 1" class="step-status loading">进行中</span>
              <span v-else class="step-status done">已完成</span>
            </div>
          </div>
          <div class="progress-step" :class="{ active: progressStep >= 2, done: progressStep > 2 }">
            <div class="step-dot">
              <span v-if="progressStep > 2" class="step-check">✓</span>
              <span v-else class="step-num">2</span>
            </div>
            <div class="step-body">
              <span class="step-title">正在重命名文件（防和谐）...</span>
              <span v-if="progressStep === 2" class="step-status loading">进行中</span>
              <span v-else-if="progressStep > 2" class="step-status done">已完成</span>
              <span v-else class="step-status pending">等待中</span>
            </div>
          </div>
          <div class="progress-step" :class="{ active: progressStep >= 3, done: progressStep > 3 }">
            <div class="step-dot">
              <span v-if="progressStep > 3" class="step-check">✓</span>
              <span v-else class="step-num">3</span>
            </div>
            <div class="step-body">
              <span class="step-title">正在生成分享链接...</span>
              <span v-if="progressStep === 3" class="step-status loading">进行中</span>
              <span v-else-if="progressStep > 3" class="step-status done">已完成</span>
              <span v-else class="step-status pending">等待中</span>
            </div>
          </div>
        </div>

        <!-- 保存失败 -->
        <div v-else-if="!saveSuccess" class="save-error">
          <el-alert
            type="error"
            :title="saveResult?.message || (saveResult as any)?.error || '保存失败'"
            show-icon
            :closable="false"
          />
        </div>

        <!-- 重命名信息（完成时显示） -->
        <div v-if="saveSuccess && renamedFiles.length > 0 && shareLink" class="rename-info-bar">
          <el-alert type="warning" :closable="false" show-icon>
            <template #title>
              <span style="font-size:13px">已对 {{ renamedFiles.length }} 个文件执行防和谐重命名</span>
            </template>
            <div v-for="r in renamedFiles" :key="r" class="rename-item">{{ r }}</div>
          </el-alert>
        </div>

        <!-- 成功结果展示 -->
        <div v-if="saveSuccess && shareLink" class="share-result">
          <div class="share-layout">
            <div class="qr-left">
              <canvas ref="qrCanvasRef" class="qr-canvas"></canvas>
              <p class="qr-hint">{{ diskLabel }}APP扫码转存</p>
              <p class="qr-subhint">保存到你自己网盘</p>
              <div class="qr-disclaimer-short">
                <span>⚠️ 本站资源仅供学习交流，请于24h内删除</span>
              </div>
            </div>
            <div class="link-right">
              <div class="success-header">
                <el-icon class="success-icon" :size="20" color="#67c23a"><CircleCheckFilled /></el-icon>
                <span class="success-text">【{{ diskLabel }}】<strong>分享链接已生成！</strong></span>
              </div>
              <div class="link-row">
                <el-input v-model="shareLink" readonly class="share-input" />
              </div>
              <div v-if="sharePwd" class="share-pwd-row">
                <span class="pwd-label">🔑 提取密码：</span>
                <el-tag type="warning">{{ sharePwd }}</el-tag>
                <span class="pwd-hint">打开链接后需输入密码</span>
              </div>
              <div class="share-tip">
                <span class="share-tip-warn">⚠️</span>
                <div class="share-tip-text">
                  <strong>请尽快复制链接到浏览器打开</strong> 或 <strong>用{{ diskLabel }}APP扫码</strong><br>
                  <strong>转存至您的网盘，以免资源被官方和谐</strong>
                </div>
              </div>

              <!-- 郑重警告 -->
              <div class="warnings-box">
                <p class="warning-item">郑重警告一：网盘内除您所需资源外，不要打开任何不相关内容。</p>
                <p class="warning-item">郑重警告二：网盘内除您所需资源外，不要打开任何不相关内容。</p>
                <p class="warning-item">郑重警告三：网盘内除您所需资源外，不要打开任何不相关内容。</p>
                <p class="warning-item">郑重警告四：以上警告说三遍，你还要明知故犯吗？</p>
              </div>
              <!-- 底部操作 -->
              <div class="dialog-actions">
                <el-button class="disclaimer-btn" @click="openDisclaimer">📜 免责声明</el-button>
                <el-button @click="resultDialogVisible = false">关闭</el-button>
                <el-button type="primary" @click="copyShareLink">一键复制链接</el-button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
  </div>

  <!-- 登录弹窗 -->
  <el-dialog v-model="showLogin" title="登录" width="380px" :close-on-click-modal="false" top="25vh">
    <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" label-width="0" @keyup.enter="handleLogin">
      <el-form-item prop="username">
        <el-input v-model="loginForm.username" placeholder="用户名" prefix-icon="User" />
      </el-form-item>
      <el-form-item prop="password">
        <el-input v-model="loginForm.password" type="password" placeholder="密码" prefix-icon="Lock" show-password />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" :loading="loginLoading" style="width: 100%" @click="handleLogin">登录</el-button>
      </el-form-item>
    </el-form>
    <p v-if="loginError" class="login-error">{{ loginError }}</p>
  </el-dialog>
  <div v-if="siteDisclaimer" class="site-footer">
    <div class="footer-inner">{{ siteDisclaimer }}</div>
    <div class="footer-actions">
      <el-button class="footer-disclaimer-btn" size="small" @click="openDisclaimer">📜 免责声明</el-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Search, Loading, CircleCheckFilled } from '@element-plus/icons-vue'
import QRCode from 'qrcode'
import ResultCard from '../components/ResultCard.vue'
import VideoResultCard from '../components/VideoResultCard.vue'
import { query as searchQuery, saveToCloud, saveVideoToCloud, getSystemConfigs, getCloudTypes, streamSearch, adminLogin, getMe } from '../api'
import { ElMessage } from 'element-plus'
import type { FormInstance } from 'element-plus'
import type { SearchResult, VideoParseResult, SaveResult, IntentType, CloudType, ChannelGroup } from '../types'
import { CLOUD_LABELS, CLOUD_ICONS, CLOUD_COLORS } from '../types'

const route = useRoute()
const router = useRouter()

const query = ref('')
const loading = ref(false)
const loadingMore = ref(false)
const intent = ref<IntentType | null>(null)
const searchResults = ref<SearchResult[]>([])
const videoResults = ref<VideoParseResult[]>([])
const results = ref<any[]>([])
const channels = ref<ChannelGroup[]>([])
const filteredCount = ref(0)
const skippedCount = ref(0)
const hasMore = ref(false)
const currentPage = ref(1)
const totalCount = ref(0)
const searchTime = ref(0)
const activeCloudTab = ref('')
const loadingProgress = ref(0)
const loadingPhase = ref<'search' | 'validate' | 'done'>('search')
const validationTotal = ref(0)
const validationDone = ref(0)
const contentInfo = ref<any>(null)
const contentTags = ref<string[]>([])
const fallbackImage = computed(() => {
  // 兜底图优先用 search_fallback_image，没有则用 site_logo
  return _fallbackImage.value || siteLogo.value || ''
})
const _fallbackImage = ref('')
const siteLogo = ref('')
const siteName = ref('')
const siteDisclaimer = ref('')
const siteMarquee = ref('')
const coverError = ref(false)

// 登录状态
const userInfo = ref<{ username: string } | null>(null)
const showLogin = ref(false)
const loginFormRef = ref<FormInstance>()
const loginForm = reactive({ username: '', password: '' })
const loginLoading = ref(false)
const loginError = ref('')
const loginRules = {
  username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
  password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
}

const allResultsMap = ref(new Map<string, any>())
const validResults = ref<any[]>([])

// 页面内分批加载
const FLAT_PAGE_SIZE = 30
const CHANNEL_PAGE_SIZE = 20
const visibleCount = ref(FLAT_PAGE_SIZE)
// 每个频道展示的条目数上限
const channelLimits = ref<Record<string, number>>({})

// 网盘图标映射 — 从后端 API 加载
const cloudTypeMap = ref<Record<string, { label: string; icon: string }>>({})
function cloudIconByType(t: string): string {
  return cloudTypeMap.value[t]?.icon || CLOUD_ICONS[t] || '📁'
}
async function loadCloudTypes() {
  try {
    const res = await getCloudTypes()
    const map: Record<string, { label: string; icon: string }> = {}
    for (const ct of res.types) {
      map[ct.type] = { label: ct.label, icon: ct.icon }
    }
    cloudTypeMap.value = map
  } catch { /* ignore */ }
}

onMounted(async () => {
  const q = (route.query.q as string) || ''
  if (q) {
    query.value = q
    doSearch(q)
  }
  // 检查登录状态
  const me: any = await getMe().catch(() => ({ loggedIn: false }))
  if (me.loggedIn && me.username) {
    userInfo.value = { username: me.username }
  }
  loadCloudTypes()
})

async function handleLogin() {
  const valid = await loginFormRef.value?.validate().catch(() => false)
  if (!valid) return
  loginLoading.value = true
  loginError.value = ''
  try {
    const res = await adminLogin(loginForm.username, loginForm.password)
    localStorage.setItem('admin_token', res.token)
    userInfo.value = { username: loginForm.username }
    showLogin.value = false
    loginForm.password = ''
    ElMessage.success('登录成功')
  } catch (e: any) {
    loginError.value = e?.response?.data?.error || e?.message || '登录失败'
  } finally {
    loginLoading.value = false
  }
}

function handleLogout() {
  localStorage.removeItem('admin_token')
  userInfo.value = null
  ElMessage.success('已退出')
}

// 网盘分类标签 — 始终展示所有已知云盘类型（0 也显示）
const cloudTabs = computed(() => {
  const counts: Record<string, number> = {}
  for (const r of searchResults.value) {
    const ct = r.cloud_type || 'others'
    counts[ct] = (counts[ct] || 0) + 1
  }
  const list: { type: string; label: string; count: number; icon: string }[] = []
  list.push({ type: '', label: '全部', count: searchResults.value.length, icon: '📋' })

  const cloudOrder: Record<string, number> = {
    quark: 1, baidu: 2, aliyun: 3, '115': 4,
    tianyi: 5, '123pan': 6, uc: 7, xunlei: 8,
    pikpak: 9, magnet: 10, ed2k: 11, others: 12,
  }
  const allTypes = Object.keys(CLOUD_LABELS) as CloudType[]
  const sorted = allTypes.sort((a, b) => (cloudOrder[a] ?? 99) - (cloudOrder[b] ?? 99))
  for (const type of sorted) {
    list.push({
      type,
      label: CLOUD_LABELS[type],
      count: counts[type] || 0,
      icon: cloudIconByType(type),
    })
  }
  return list
})

// 只显示有结果的标签（隐藏 0 计数的标签）
const visibleTabs = computed(() => {
  return cloudTabs.value.filter(t => t.count > 0)
})

function getActiveTabLabel(): string {
  const tab = cloudTabs.value.find(t => t.type === activeCloudTab.value)
  return tab?.label || activeCloudTab.value || ''
}

// 扁平全部结果（按时间排序 + 页面内分批）
const allResultsFlat = computed(() => {
  const all: any[] = []
  for (const ch of channels.value) {
    all.push(...ch.items)
  }
  // 按 update_time 降序（最新优先）
  return all.sort((a, b) => {
    const ta = a.update_time || a.datetime || ''
    const tb = b.update_time || b.datetime || ''
    if (!ta && !tb) return 0
    if (!ta) return 1
    if (!tb) return -1
    return tb.localeCompare(ta)
  })
})

// 分页显示的切片
const displayedResults = computed(() => {
  return allResultsFlat.value.slice(0, visibleCount.value)
})

// 是否还有更多本地数据可加载
const hasMoreLocal = computed(() => {
  return visibleCount.value < allResultsFlat.value.length
})

// 未显示的数量
const remainingCount = computed(() => {
  return allResultsFlat.value.length - visibleCount.value
})

// ===== 空结果智能提示分析 =====
const emptyHint = computed(() => {
  const q = query.value.trim()
  if (!q) return '请输入关键词进行搜索'
  if (q.length < 2) return `「${q}」太短了，试试输入更完整的关键词`
  if (q.length > 30) return '关键词太长啦，试试用几个核心词代替整句话'
  const chineseChars = (q.match(/[\u4e00-\u9fff]/g) || []).length
  if (chineseChars === 0) return '网盘资源通常以中文命名，试试用中文搜索'
  return `「${q}」暂时没找到匹配的资源`
})

const emptyTips = computed(() => {
  const q = query.value.trim()
  if (!q) return ['输入电视剧/电影/文件名称试试']
  const tips: string[] = []

  // 1. 判断关键词长度
  if (q.length < 2) {
    tips.push('输入至少 2 个字符，试试完整的资源名称')
  }

  // 2. 判断是否过长
  if (q.length > 30) {
    tips.push('缩短到 2-10 个字，用核心关键词搜索更精准')
  }

  // 3. 没有中文
  const chineseChars = (q.match(/[\u4e00-\u9fff]/g) || []).length
  if (chineseChars === 0) {
    tips.push('国内网盘资源标题大多是中文，试试转换为中文名称')
  }

  // 4. 中文太少
  if (chineseChars > 0 && chineseChars < q.length * 0.5 && q.length >= 2) {
    tips.push('混合了太多非中文字符，提取核心中文关键词试试')
  }

  // 5. 包含空格
  if (q.includes(' ') || q.includes('　')) {
    tips.push('搜索词中包含了空格，试试去掉空格连续输入')
  }

  // 6. 是否是完整句子
  const stopWords = ['的', '了', '是', '在', '有', '我', '他', '她', '它', '这', '那', '和', '与', '及', '或', '但', '而', '且']
  const stopCount = stopWords.filter(w => q.includes(w)).length
  if (stopCount >= 2 && q.length > 6) {
    tips.push('看起来像是一句话，试着只保留资源核心名称（去掉「的」「了」「我」等词）')
  }

  // 7. 太具体/特殊字符过多
  const specialChars = (q.match(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~`《》【】！@#￥%……&*（）——+｜]/g) || []).length
  if (specialChars > 2) {
    tips.push('特殊符号过多，试试只用中英文和数字')
  }

  // 8. 如果是单字中文
  if (q.length === 1 && chineseChars === 1) {
    tips.push('单个汉字过于宽泛，试试完整的剧名或文件名')
  }

  // 9. 默认通用建议
  if (tips.length === 0) {
    tips.push('试试更换关键词或减短搜索词')
    tips.push('检查一下是否输入了正确的资源名称')
  }

  return tips.slice(0, 4)
})


// 加载更多本地数据
function loadMoreLocal() {
  visibleCount.value += FLAT_PAGE_SIZE
}

// 获取频道可见条目数
function channelVisibleItems(channel: any): any[] {
  const max = channelLimits.value[channel.cloud_type] || CHANNEL_PAGE_SIZE
  return (channel.items || []).slice(0, max)
}

// 频道是否还有更多
function channelHasMore(channel: any): boolean {
  const max = channelLimits.value[channel.cloud_type] || CHANNEL_PAGE_SIZE
  return (channel.items || []).length > max
}

// 频道展开加载更多
function channelLoadMore(cloudType: string) {
  channelLimits.value = {
    ...channelLimits.value,
    [cloudType]: (channelLimits.value[cloudType] || CHANNEL_PAGE_SIZE) + CHANNEL_PAGE_SIZE
  }
}

// 重置频道限制
function resetChannelLimits() {
  channelLimits.value = {}
}

// 根据当前激活的 cloud tab 过滤 channels
const filteredChannels = computed<ChannelGroup[]>(() => {
  let list = channels.value
  if (activeCloudTab.value) {
    list = list.filter(ch => ch.cloud_type === activeCloudTab.value)
  }
  return list
})

// 总页数
const totalPages = computed(() => {
  if (totalCount.value <= 0) return 1
  return Math.ceil(totalCount.value / 20)
})

// 包含结果的网盘类型数
const cloudTypesCount = computed(() => {
  return cloudTabs.value.filter(t => t.type !== '' && t.count > 0).length
})

// 强制刷新搜索
function handleRefresh() {
  if (query.value.trim()) {
    doSearch(query.value.trim())
  }
}

// 模拟进度条（实时更新时间）
function startProgressSimulation() {
  loadingProgress.value = 0
  loadingPhase.value = 'search'
  const startTime = Date.now()
  searchTime.value = 0

  const interval = setInterval(() => {
    if (!loading.value) {
      loadingProgress.value = 100
      clearInterval(interval)
      return
    }
    // 实时更新时间
    searchTime.value = Date.now() - startTime

    if (loadingProgress.value < 60) {
      loadingProgress.value += 1 + Math.random() * 3
    } else if (loadingProgress.value < 85) {
      loadingPhase.value = 'validate'
      loadingProgress.value += 0.5 + Math.random() * 1
    } else if (loadingProgress.value < 98) {
      loadingProgress.value += 0.2 + Math.random() * 0.5
    }
  }, 200)
  return interval
}

// 计算频道最新时间
function getChannelNewestTime(items: any[]): string {
  const times = items
    .map(i => i.update_time || i.datetime || '')
    .filter(Boolean)
    .sort()
    .reverse()
  if (times.length === 0) return ''
  return formatRelativeTime(times[0])
}


function formatRelativeTime(dateStr: string): string {
  if (!dateStr) return ''
  const now = Date.now()
  const date = new Date(dateStr)
  if (isNaN(date.getTime())) return dateStr.slice(0, 10)
  const diff = now - date.getTime()
  if (diff < 0) return dateStr.slice(0, 10)
  const mins = Math.floor(diff / 60000)
  if (mins < 60) return mins <= 1 ? '刚刚' : `${mins} 分钟前`
  const hours = Math.floor(mins / 60)
  if (hours < 24) return `${hours} 小时前`
  const days = Math.floor(hours / 24)
  if (days < 30) return `${days} 天前`
  const months = Math.floor(days / 30)
  return `${months} 个月前`
}

// 频道图标 — 使用 API 加载的网盘图标
function channelIcon(ct: string): string {
  return cloudIconByType(ct)
}

watch(
  () => route.query.q,
  (newQ) => {
    if (newQ && newQ !== query.value) {
      query.value = newQ as string
      doSearch(newQ as string)
    }
  }
)

const saving = ref(false)
const currentSaveItem = ref<any>(null)
const resultDialogVisible = ref(false)
const saveSuccess = ref(false)
const saveResult = ref<SaveResult | null>(null)
const shareLink = ref('')
const sharePwd = ref('')
const renamedFiles = ref<string[]>([])
const qrCanvasRef = ref<HTMLCanvasElement | null>(null)
const progressStep = ref(0)

// 当前保存的网盘名称（如"夸克网盘"、"百度网盘"）
const diskLabel = computed(() => {
  const ct = currentSaveItem.value?.cloud_type || 'quark'
  return (CLOUD_LABELS as any)[ct] || '夸克网盘'
})
// 弹窗标题
const dialogTitle = computed(() => {
  const title = currentSaveItem.value?.title || ''
  // 取干净标题（去掉【】内容）
  const clean = title.replace(/【[^】]+】/g, '').trim()
  return clean || title || '资源'
})

async function doSearch(q: string) {
  loading.value = true
  const startTime = Date.now()
  visibleCount.value = FLAT_PAGE_SIZE
  currentPage.value = 1
  searchResults.value = []
  videoResults.value = []
  results.value = []
  channels.value = []
  validResults.value = []
  allResultsMap.value = new Map()
  filteredCount.value = 0
  skippedCount.value = 0
  hasMore.value = false
  activeCloudTab.value = ''
  searchTime.value = 0
  coverError.value = false
  siteMarquee.value = ''
  resetChannelLimits()

  const progressTimer = startProgressSimulation()

  try {
    intent.value = 'SEARCH'
    let totalValidated = 0
    let totalFiltered = 0
    const validatedMap = new Map<string, boolean>()
    let validationComplete = false

    await streamSearch(q, {
      onStats: (stats) => {
        searchTime.value = Date.now() - startTime
        totalCount.value = stats.total
        contentInfo.value = stats.content_info || null
        contentTags.value = stats.content_tags || []
        if (stats.fallback_image) {
          _fallbackImage.value = stats.fallback_image
          // 预加载兜底图：成功了缓存，失败了清空引用，避免每个卡片单独试
          const preload = new Image()
          preload.onload = () => {} // 浏览器自动缓存
          preload.onerror = () => { _fallbackImage.value = '' }
          preload.src = stats.fallback_image
        }
        if (stats.site_logo) {
          siteLogo.value = stats.site_logo
        }
        if (stats.site_name) {
          siteName.value = stats.site_name
        }
        if (stats.site_disclaimer) {
          siteDisclaimer.value = stats.site_disclaimer
        }
        if (stats.site_marquee) {
          siteMarquee.value = stats.site_marquee
        }
        loadingPhase.value = 'validate'

        // 存储所有结果，后续按验证结果逐步展示
        if (stats.channels) {
          const map = new Map<string, any>()
          const all: any[] = []
          for (const ch of stats.channels) {
            for (const item of (ch.items || [])) {
              map.set(item.id, item)
              all.push(item)
            }
          }
          allResultsMap.value = map
          results.value = all
        }

        if (stats.link_validation) {
          // Now we validate ALL items, so validation total = total count
          validationTotal.value = stats.total
        }
      },

      onResult: (id, valid) => {
        totalValidated++
        validationDone.value = totalValidated
        searchTime.value = Date.now() - startTime

        // 验证通过就加入结果列表
        if (valid) {
          const item = allResultsMap.value.get(id)
          if (item) {
            validResults.value.push(item)
            // Incrementally update channels for display
            searchResults.value = [...validResults.value]
            channels.value = buildChannelsFromResults(searchResults.value).map((ch: any) => ({
              ...ch,
              newestTime: getChannelNewestTime(ch.items),
            }))
          }
        }
      },

      onComplete: (data) => {
        searchTime.value = Date.now() - startTime
        // 后端已过滤失效链接，直接使用
        const backendResults = (data.results as SearchResult[]) || []

        totalCount.value = backendResults.length
        filteredCount.value = (data.filtered || 0)
        skippedCount.value = (data.skipped || 0)
        hasMore.value = false
        validationDone.value = validationTotal.value

        // Use the complete data's final results
        searchResults.value = backendResults
        channels.value = (data.channels || []).map((ch: any) => ({
          ...ch,
          newestTime: getChannelNewestTime(ch.items),
        }))
        // Recalculate channel item lists
        const itemsByType: Record<string, any[]> = {}
        for (const r of backendResults) {
          const ct = r.cloud_type || 'others'
          if (!itemsByType[ct]) itemsByType[ct] = []
          itemsByType[ct].push(r)
        }
        channels.value = channels.value.map(ch => ({
          ...ch,
          count: (itemsByType[ch.cloud_type] || []).length,
          items: itemsByType[ch.cloud_type] || [],
        })).filter(ch => ch.count > 0)
        results.value = backendResults

        loading.value = false
        loadingPhase.value = 'done'
        loadingProgress.value = 100
        clearInterval(progressTimer)
      },

      onError: (err) => {
        console.error('搜索失败', err)
        loading.value = false
        loadingPhase.value = 'done'
        loadingProgress.value = 100
        clearInterval(progressTimer)
      },
    })
  } catch (e) {
    console.error('搜索异常', e)
    loading.value = false
    loadingPhase.value = 'done'
    loadingProgress.value = 100
    clearInterval(progressTimer)
  }
}

function buildChannelsFromResults(results: any[]): ChannelGroup[] {
  const groups: Record<string, any[]> = {}
  const order: Record<string, number> = {
    quark: 1, baidu: 2, aliyun: 3, '115': 4,
    tianyi: 5, '123pan': 6, uc: 7, xunlei: 8,
    pikpak: 9, magnet: 10, ed2k: 11, others: 12,
  }
  for (const r of results) {
    const ct = r.cloud_type || 'others'
    if (!groups[ct]) groups[ct] = []
    groups[ct].push(r)
  }
  return Object.entries(groups)
    .sort((a, b) => (order[a[0]] ?? 99) - (order[b[0]] ?? 99))
    .map(([cloud_type, items]) => ({
      cloud_type,
      label: CLOUD_LABELS[cloud_type as CloudType] || cloud_type,
      color: CLOUD_COLORS[cloud_type as CloudType] || '#95a5a6',
      count: items.length,
      items,
      newestTime: getChannelNewestTime(items),
    }))
}

async function loadMore() {
  loadingMore.value = true
  currentPage.value++
  try {
    const res = await searchQuery(query.value, currentPage.value)
    const newResults = res.results as SearchResult[]
    searchResults.value.push(...newResults)
    totalCount.value = res.total  // 更新总数
    hasMore.value = res.total > searchResults.value.length
    filteredCount.value += res.filtered || 0
    channels.value = buildChannelsFromResults(searchResults.value)
  } catch (e) {
    console.error('加载更多失败', e)
  } finally {
    loadingMore.value = false
  }
}

function handleSearch() {
  const q = query.value.trim()
  if (q) {
    router.replace('/search?q=' + encodeURIComponent(q))
    doSearch(q)
  }
}

async function handleSave(data: SearchResult) {
  currentSaveItem.value = data
  // 清除上一次保存的结果状态，避免新旧结果同时显示
  saveSuccess.value = false
  saveResult.value = null
  shareLink.value = ''
  sharePwd.value = ''
  renamedFiles.value = []
  saving.value = true
  resultDialogVisible.value = true
  progressStep.value = 1
  const cloudType = data.cloud_type || 'quark'
  try {
    const result: SaveResult = await saveToCloud({
      type: 'search',
      source: data,
      target_cloud: cloudType,
    })
    saveResult.value = result
    saveSuccess.value = result.success

    if (result.success) {
      // Populate rename info from backend response
      if ((result as any).renamed?.length > 0) {
        renamedFiles.value = (result as any).renamed
      }

      // Step 2: 重命名文件（防和谐）
      progressStep.value = 2
      await new Promise(r => setTimeout(r, 600))

      // Step 3: 生成分享链接
      progressStep.value = 3
      await new Promise(r => setTimeout(r, 400))

      if (result.share_url) {
        shareLink.value = result.share_url
        sharePwd.value = (result as any).share_pwd || (result as any).sharePwd || ''
        await new Promise(r => setTimeout(r, 300))
        progressStep.value = 4
      } else {
        progressStep.value = 4
      }
    }
  } catch (e: any) {
    saveResult.value = {
      success: false,
      share_url: '',
      file_name: '',
      file_size: '',
      message: e.message || '保存请求失败',
    }
    saveSuccess.value = false
  } finally {
    saving.value = false
  }
}

async function handleVideoSave(data: VideoParseResult) {
  currentSaveItem.value = data
  // 清除上一次保存的结果状态
  saveSuccess.value = false
  saveResult.value = null
  shareLink.value = ''
  sharePwd.value = ''
  renamedFiles.value = []
  saving.value = true
  resultDialogVisible.value = true
  try {
    const result = await saveVideoToCloud({
      video_url: data.video_url,
      title: data.title,
      target_cloud: 'quark',
    })
    saveResult.value = result
    saveSuccess.value = result.success
    if (result.success && result.share_url) {
      shareLink.value = result.share_url
    }
  } catch (e: any) {
    saveResult.value = { success: false, share_url: '', file_name: '', file_size: '', message: e.message || '保存请求失败' }
    saveSuccess.value = false
  } finally {
    saving.value = false
  }
}

// 当保存完成且 shareLink 更新时，生成二维码
watch([shareLink, saving], async ([newLink, isSaving]) => {
  if (newLink && !isSaving && resultDialogVisible.value) {
    await nextTick()
    if (qrCanvasRef.value) {
      QRCode.toCanvas(qrCanvasRef.value, newLink, { width: 180, margin: 1 })
    }
  }
})

function copyShareLink() {
  if (!shareLink.value) return
  const text = shareLink.value
  // Try modern clipboard API first
  if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard.writeText(text).then(() => {
      ElMessage.success('链接已复制')
    }).catch(() => {
      fallbackCopy(text)
    })
  } else {
    fallbackCopy(text)
  }
}

function openDisclaimer() {
  window.open('/disclaimer/', '_blank')
}

function fallbackCopy(text: string) {
  const textarea = document.createElement('textarea')
  textarea.value = text
  textarea.style.position = 'fixed'
  textarea.style.left = '-9999px'
  textarea.style.top = '-9999px'
  textarea.style.opacity = '0'
  document.body.appendChild(textarea)
  textarea.select()
  try {
    document.execCommand('copy')
    ElMessage.success('链接已复制')
  } catch {
    ElMessage.warning('复制失败，请手动复制链接')
  }
  document.body.removeChild(textarea)
}
</script>

<style scoped>
.search-result-page {
  min-height: 100vh;
  background: var(--bg-color, #f5f5f5);
}

.top-search-bar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: #fff;
  border-bottom: 1px solid #e8e8e8;
  padding: 12px 24px;
}

.search-bar-inner {
  max-width: 800px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
}

.search-box-inner {
  display: flex;
  align-items: center;
  flex: 1;
  border: 1px solid #dfe1e5;
  border-radius: 24px;
  background: #fff;
  box-shadow: none;
  transition: box-shadow .2s, border-color .2s;
  overflow: hidden;
}
.search-box-inner:focus-within {
  box-shadow: 0 1px 6px rgba(32,33,36,.28);
  border-color: rgba(223,225,229,0);
}

/* ---- 跑马灯通知条 ---- */
.marquee-bar {
  max-width: 680px;
  margin: 0 auto;
  padding: 6px 12px;
  display: flex;
  align-items: center;
  gap: 8px;
  overflow: hidden;
}
.marquee-icon {
  flex-shrink: 0;
  font-size: 15px;
  line-height: 1;
}
.marquee-track {
  flex: 1;
  overflow: hidden;
  white-space: nowrap;
}
.marquee-text {
  display: inline-block;
  font-size: 13px;
  color: #e6a23c;
  animation: marquee-scroll 20s linear infinite;
  padding-left: 100%;
}
@keyframes marquee-scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-100%);
  }
}
.search-box-inner :deep(.el-input__wrapper) {
  border: none; box-shadow: none; background: transparent;
  padding: 4px 20px; border-radius: 0;
}
.search-box-inner :deep(.el-input__inner) {
  font-size: 15px;
}

/* 右上角用户信息 - 独立于搜索栏 */
.top-right-user {
  position: absolute;
  top: 12px;
  right: 20px;
  display: flex;
  align-items: center;
  gap: 8px;
  z-index: 100;
  flex-shrink: 0;
}

.user-badge {
  font-size: 13px;
  color: var(--primary-color, #409eff);
  font-weight: 600;
  white-space: nowrap;
}

.login-error {
  color: #f56c6c;
  font-size: 13px;
  text-align: center;
  margin: 0;
}

.logo-link {
  text-decoration: none;
  flex-shrink: 0;
}

.logo-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: rgba(64, 158, 255, 0.08);
  transition: background 0.2s;
}

.logo-icon:hover {
  background: rgba(64, 158, 255, 0.15);
}

.logo-text-only {
  font-size: 18px;
  font-weight: 700;
  color: var(--primary-color);
  white-space: nowrap;
  flex-shrink: 0;
}

.logo-img {
  width: auto;
  height: 40px;
  max-width: 160px;
  object-fit: contain;
  flex-shrink: 0;
}

.result-search-btn {
  height: 38px !important;
  padding: 0 22px !important;
  border: none !important;
  border-radius: 999px !important;
  margin: 4px;
  font-size: 14px !important;
  font-weight: 600 !important;
  letter-spacing: 0.5px;
  flex-shrink: 0;
  background: var(--primary-color);
  color: #fff;
  transition: all .2s;
}
.result-search-btn:hover {
  background: #3a7be0;
}
.result-search-btn:active {
  background: #2d6ccf;
}

.share-input {
  flex: 1;
}
.share-pwd-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.pwd-label {
  font-size: 13px;
  color: var(--text-secondary, #909399);
}
.pwd-hint {
  font-size: 12px;
  color: var(--text-secondary, #909399);
}
/* 结果信息栏 */
.result-info-bar {
  max-width: 800px;
  margin: 16px auto 0;
  padding: 0 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 13px;
  color: #909399;
}

.info-left, .info-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.info-item {
  white-space: nowrap;
}

.info-count {
  font-weight: 600;
  color: #303133;
}

.info-type {
  color: #909399;
  font-size: 12px;
  background: #f4f4f5;
  padding: 1px 7px;
  border-radius: 4px;
}

.info-time {
  font-family: monospace;
  background: #f4f4f5;
  padding: 1px 7px;
  border-radius: 4px;
  color: #909399;
}

.info-hasmore {
  color: #c0c4cc;
}

.filter-badge {
  background: #fef0f0;
  color: #f56c6c;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
}

.skip-badge {
  background: #fdf6ec;
  color: #e6a23c;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
}

.refresh-btn {
  background: none;
  border: 1px solid #e4e7ed;
  border-radius: 6px;
  padding: 2px 10px;
  font-size: 12px;
  color: #909399;
  cursor: pointer;
  transition: all 0.2s;
}
.refresh-btn:hover {
  color: #409eff;
  border-color: #409eff;
  background: rgba(64,158,255,0.05);
}

/* 意图标签 */
.intent-badge {
  max-width: 800px;
  margin: 12px auto 0;
  padding: 0 24px;
}

.intent-tag {
  font-size: 13px;
  color: #909399;
  background: #f0f2f5;
  padding: 2px 10px;
  border-radius: 4px;
}

/* 加载进度 */
.loading-section {
  max-width: 800px;
  margin: 24px auto;
  padding: 0 24px;
}

.progress-track {
  width: 100%;
  height: 4px;
  background: #e8e8e8;
  border-radius: 2px;
  overflow: hidden;
}

.progress-bar {
  height: 100%;
  background: linear-gradient(90deg, #409eff, #67c23a);
  border-radius: 2px;
  transition: width 0.3s ease;
}

.progress-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 13px;
  color: #909399;
}

.progress-time {
  margin-left: auto;
  font-family: monospace;
  color: #c0c4cc;
}

.validate-count {
  font-family: monospace;
  color: #409eff;
  font-weight: 600;
}

.loading-skeleton {
  margin-top: 16px;
}

/* 网盘分类标签栏 — 跟资源展示等宽（继承父容器） */
.cloud-tabs {
  margin: 16px auto 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.cloud-tab {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 14px;
  border-radius: 20px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: #606266;
  background: #f0f2f5;
  cursor: pointer;
  transition: all 0.2s;
  user-select: none;
}

.cloud-tab:hover {
  background: #e4e7ed;
  color: #303133;
}

.cloud-tab.active {
  background: rgba(64, 158, 255, 0.1);
  color: #409eff;
  font-weight: 600;
}

.tab-icon {
  font-size: 15px;
  line-height: 1;
}
.tab-icon-img {
  width: 16px;
  height: 16px;
  vertical-align: middle;
  margin-right: 2px;
}

.tab-count {
  font-size: 11px;
  color: #c0c4cc;
  margin-left: 2px;
}

/* 结果列表 */
.result-content {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 24px 48px;
}

/* 宽屏下搜索栏保持800px，结果容器保持1080px，网格保持3列 */
@media (min-width: 1280px) {
  .result-content {
    max-width: 1080px;
  }
  .search-bar-inner {
    max-width: 800px;
  }
}

.result-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 16px;
}

/* 无匹配提示 */
.no-match-tip {
  margin-top: 32px;
  text-align: center;
  color: #909399;
  font-size: 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

/* 频道分组 */
.channel-section {
  background: #fff;
  border-radius: 12px;
  padding: 14px 16px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
  border: 1px solid #ebeef5;
}

.channel-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid #f0f0f0;
}

.channel-icon {
  font-size: 16px;
  line-height: 1;
}
.channel-icon-img {
  width: 18px;
  height: 18px;
  vertical-align: middle;
  margin-right: 4px;
}

.channel-label {
  font-size: 15px;
  font-weight: 600;
  color: #303133;
}

.channel-total-badge {
  font-size: 12px;
  color: #909399;
  background: #f4f4f5;
  padding: 1px 8px;
  border-radius: 10px;
  margin-left: 2px;
}

.channel-time {
  margin-left: auto;
  font-size: 12px;
  color: #b8860b;
  white-space: nowrap;
}

/* 全部标签 — 卡片网格，3列 */
.flat-list {
  display: grid !important;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
}

/* 窄屏降为1列，平板降为2列 */
@media (max-width: 720px) {
  .flat-list {
    grid-template-columns: 1fr;
  }
  .channel-section {
    grid-template-columns: 1fr;
  }
}
@media (min-width: 721px) and (max-width: 1079px) {
  .flat-list {
    grid-template-columns: repeat(2, 1fr);
  }
  .channel-section {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 频道列表 — 每个频道区块也3列 */
.channel-list {
  gap: 14px;
}

.channel-section {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-bottom: 24px;
}

.channel-section .channel-header {
  grid-column: 1 / -1;
}

.channel-section .channel-load-more {
  grid-column: 1 / -1;
}

.channel-load-more {
  margin-top: 8px;
  text-align: center;
  padding: 8px;
  border: 1px dashed #dcdfe6;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.2s;
  background: #fafafa;
}
.channel-load-more:hover {
  background: #f0f2f5;
  border-color: #c0c4cc;
}
.channel-load-more-text {
  font-size: 13px;
  color: #909399;
}

/* 加载更多 */
.load-more {
  text-align: center;
  margin-top: 24px;
}

/* 内联加载更多（"全部"标签的分批加载） */
.load-more-inline {
  text-align: center;
  margin-top: 8px;
}
.load-more-btn {
  width: 100%;
  border-radius: 8px;
  padding: 10px;
  border: 1px dashed #dcdfe6;
  background: #fafafa;
  color: #909399;
  font-size: 13px;
  transition: all 0.2s;
}
.load-more-btn:hover {
  background: #f0f2f5;
  border-color: #c0c4cc;
  color: #606266;
}

/* 保存弹窗 */
.save-dialog :deep(.el-dialog__title) {
  font-weight: 700;
  font-size: 16px;
}
.save-dialog {
  width: 650px !important;
}
/* 修复dialog被el-dialog默认样式覆盖 */
.save-dialog :deep(.el-dialog) {
  --el-dialog-width: 650px !important;
}

.dialog-title-bold {
  font-size: 16px;
  font-weight: 700;
  color: #303133;
  line-height: 1.4;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
}

/* ---- 保存弹窗 — 进度流程 ---- */
.result-dialog-content {
  min-height: 80px;
}

.progress-flow {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 8px 0;
}

.progress-step {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  opacity: 0.4;
  transition: all 0.3s ease;
}
.progress-step.active {
  opacity: 1;
}
.progress-step.done {
  opacity: 0.7;
}

.step-dot {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  background: #e4e7ed;
  color: #909399;
  transition: all 0.3s;
}
.progress-step.active .step-dot {
  background: #409eff;
  color: #fff;
  box-shadow: 0 0 0 4px rgba(64,158,255,0.2);
}
.progress-step.done .step-dot {
  background: #67c23a;
  color: #fff;
}

.step-check {
  font-size: 14px;
}

.step-body {
  flex: 1;
  padding-top: 3px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.step-title {
  font-size: 14px;
  color: #303133;
  font-weight: 500;
}

.step-status {
  font-size: 12px;
  padding: 1px 8px;
  border-radius: 10px;
  white-space: nowrap;
}
.step-status.loading {
  background: #ecf5ff;
  color: #409eff;
}
.step-status.done {
  background: #f0f9eb;
  color: #67c23a;
}
.step-status.pending {
  background: #f4f4f5;
  color: #c0c4cc;
}

/* 保存弹窗 — 分享结果展示（左二维码 + 右链接） */
.share-result {
  padding: 8px 0;
}

.share-layout {
  display: flex;
  gap: 24px;
  align-items: stretch;
}

.qr-left {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px;
  background: #fafafa;
  border-radius: 12px;
  border: 1px solid #ebeef5;
  align-self: stretch;
}

.qr-canvas {
  border-radius: 8px;
}

.qr-hint {
  margin: 4px 0 0;
  font-size: 12px;
  color: #409eff;
  font-weight: 600;
}

.qr-subhint {
  margin: 0;
  font-size: 11px;
  color: #c0c4cc;
}

.qr-disclaimer-short {
  margin-top: 8px;
  padding: 4px 8px;
  background: #fff7e6;
  border: 1px solid #ffe7ba;
  border-radius: 4px;
  font-size: 10px;
  color: #d46b08;
  text-align: center;
  line-height: 1.4;
}

.link-right {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.success-header {
  display: flex;
  align-items: center;
  gap: 6px;
}

.success-text {
  font-size: 15px;
  font-weight: 700;
  color: #303133;
}

.link-row {
  display: flex;
  gap: 8px;
}

.share-input :deep(.el-input__wrapper) {
  background: #f5f7fa;
}

.share-tip {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  background: #fdf6ec;
  padding: 8px 10px;
  border-radius: 6px;
  text-align: left;
  display: flex;
  gap: 6px;
  align-items: flex-start;
}
.share-tip strong {
  color: #d46b08;
  font-weight: 700;
}
.share-tip-warn {
  font-size: 18px;
  line-height: 1.5;
  flex-shrink: 0;
  display: inline-flex;
  align-items: flex-start;
  padding-top: 1px;
}
.share-tip-text {
  flex: 1;
  min-width: 0;
  line-height: 1.6;
}

/* 底部操作按钮 */
.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: auto;
  padding-top: 8px;
}

.disclaimer-btn {
  margin-right: auto !important;
  font-size: 12px !important;
  color: #909399 !important;
}
.disclaimer-btn:hover {
  color: #409eff !important;
}

/* 郑重警告 */
.warnings-box {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: #fff2f0;
  border: 1px solid #ffccc7;
  border-radius: 8px;
  padding: 8px 10px;
}
.warning-item {
  margin: 0;
  font-size: 12px;
  line-height: 1.8;
  font-weight: 700;
  white-space: nowrap;
}
.warning-item:nth-child(odd) {
  color: #cf1322;
}
.warning-item:nth-child(even) {
  color: #d46b08;
}
.warning-item:last-child {
  color: #b71c1c;
  font-size: 13px;
}

.save-error {
  padding: 8px 0;
}

/* ===== 内容信息（紧凑信息条） ===== */
.media-strip {
  margin: 10px auto 0;
  width: 100%;
}
.media-strip-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: linear-gradient(135deg, #f8faff, #f0f5ff);
  border: 1px solid #e8f0fe;
  border-radius: 10px;
  text-decoration: none;
  color: inherit;
  font-size: 13px;
  line-height: 1.4;
  transition: all 0.2s;
}
.media-strip-inner:hover {
  border-color: #c0d9ff;
  background: linear-gradient(135deg, #f0f7ff, #e6f0ff);
}
.strip-thumb {
  flex-shrink: 0;
  width: 32px;
  height: 44px;
  border-radius: 4px;
  overflow: hidden;
  background: #e8ecf1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.strip-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.strip-thumb-fallback {
  font-size: 18px;
  opacity: 0.5;
}
.strip-title {
  font-weight: 700;
  color: #1a1a2e;
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}
.strip-year {
  color: #909399;
  font-size: 12px;
  flex-shrink: 0;
}
.strip-rating {
  color: #e6a23c;
  font-size: 12px;
  font-weight: 600;
  flex-shrink: 0;
}
.strip-genres {
  display: flex;
  gap: 2px;
  flex-wrap: wrap;
}
.strip-genre {
  font-size: 11px;
  color: #67c23a;
  background: #f0f9eb;
  padding: 1px 7px;
  border-radius: 4px;
}
.strip-tags {
  display: flex;
  gap: 3px;
  flex-wrap: wrap;
}
.strip-tag {
  font-size: 11px;
  color: #409eff;
  background: #ecf5ff;
  padding: 1px 7px;
  border-radius: 4px;
}
.strip-right {
  font-size: 12px;
  color: #909399;
  margin-left: auto;
  flex-shrink: 0;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}

/* 重命名信息 */
.rename-info-bar {
  margin-bottom: 12px;
}
.rename-item {
  font-size: 11px;
  color: #909399;
  margin-top: 4px;
  word-break: break-all;
  line-height: 1.4;
}

/* ===== 响应式 ===== */
@media (max-width: 768px) {
  .save-dialog {
    width: 96vw !important;
  }
  .save-dialog :deep(.el-dialog) {
    --el-dialog-width: 96vw !important;
    margin: 5vh auto !important;
  }
  .save-dialog :deep(.el-dialog__body) {
    padding: 12px;
  }
  .share-layout {
    flex-direction: column;
    align-items: center;
  }
  .qr-left {
    width: 100%;
    align-self: auto;
  }
  .link-right {
    width: 100%;
  }
  /* 手机版警告区域水平滚动 */
  .warnings-box {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

@media (max-width: 640px) {
  .search-result-page {
  }
  .top-search-bar {
    flex-direction: column;
    gap: 8px;
  }
  .top-search-bar .search-bar-inner {
    width: 100%;
  }
  .top-search-bar .search-bar-inner .el-input {
    min-width: 0;
  }
  .top-right-user {
    position: static;
    align-self: flex-end;
  }
  .result-info-bar {
    flex-direction: column;
    gap: 6px;
  }
  .info-left,
  .info-right {
    flex-wrap: wrap;
  }
  .cloud-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    gap: 4px;
  }
  .cloud-tab {
    flex-shrink: 0;
    font-size: 12px;
    padding: 4px 10px;
  }
}

/* ===== 空结果智能提示 ===== */
.empty-wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 60px 20px 40px;
  text-align: center;
}
.empty-icon {
  font-size: 48px;
  margin-bottom: 16px;
}
.empty-title {
  font-size: 18px;
  font-weight: 700;
  color: #1a1a2e;
  margin-bottom: 8px;
}
.empty-hint {
  font-size: 14px;
  color: #909399;
  margin-bottom: 20px;
  max-width: 360px;
  line-height: 1.5;
}
.empty-tips {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.empty-tip-item {
  font-size: 13px;
  color: #606266;
  background: #f4f8ff;
  padding: 8px 16px;
  border-radius: 8px;
  border: 1px solid #e8f0fe;
  line-height: 1.4;
  max-width: 400px;
}

.site-footer {
  margin-top: 40px;
  padding: 20px 16px 32px;
  background: #f9fafb;
  border-top: 1px solid #ebeef5;
}
.footer-inner {
  max-width: 800px;
  margin: 0 auto;
  font-size: 12px;
  line-height: 1.8;
  color: #909399;
  text-align: center;
  white-space: pre-line;
}
.footer-actions {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
}
.footer-disclaimer-btn {
  font-size: 12px !important;
  color: #909399 !important;
}
.footer-disclaimer-btn:hover {
  color: #409eff !important;
}
</style>
