<template>
  <view class="page">
    <!-- 顶部导航栏 -->
    <view class="nav-bar">
      <view class="nav-content">
        <view class="nav-left" @click="goBack">
          <text class="back-icon">←</text>
        </view>
        <text class="page-title">{{ isTitleGenerating ? 'AI 生成标题中...' : (currentBook?.title || '书籍详情') }}</text>
        <view class="nav-right">
          <text v-if="canEdit" class="nav-edit-btn" @click="goToEdit">编辑</text>
        </view>
      </view>
    </view>

    <!-- 主内容区 -->
    <view class="main-content">
      <!-- 书籍信息 -->
      <view class="card book-info">
        <view class="book-title-row">
          <text class="book-title-large">{{ isTitleGenerating ? '✨ AI 正在生成标题...' : currentBook?.title }}</text>
          <GenerationStatusBadge :status="currentBook?.genStage || 'draft'"></GenerationStatusBadge>
        </view>
        <text v-if="currentBook?.subtitle" class="book-subtitle">{{ currentBook.subtitle }}</text>
        <text class="book-desc">{{ currentBook?.description }}</text>
        <view class="book-meta-row">
          <text>章节：{{ currentBook?.totalChapters }} 章</text>
          <text>预估：{{ currentBook?.estimatedWords || 0 }} 字</text>
        </view>
      </view>

      <!-- 进度显示 -->
      <view v-if="currentBook" class="card progress-card">
        <text class="card-title">📊 生成进度</text>
        <view class="progress-display">
          <text class="progress-text" :class="{ 'progress-failed': isGenerationFailed }">{{ isGenerationFailed ? '失败' : currentBook.progress }}%</text>
          <text class="progress-detail">
            {{ completedChapters }}/{{ currentBook.totalChapters }} 章
          </text>
        </view>
        <view class="progress-bar-large">
          <view class="progress-fill" :class="{ 'progress-fill-failed': isGenerationFailed }" :style="{ width: (isGenerationFailed ? 0 : currentBook.progress) + '%' }"></view>
        </view>

        <!-- 失败状态 -->
        <view v-if="isGenerationFailed" class="failed-tip">
          <text class="failed-icon">❌</text>
          <text class="failed-text">生成失败</text>
          <text v-if="currentBook.error" class="failed-error">{{ currentBook.error }}</text>
          <text class="failed-hint">请检查网络或稍后重试</text>
          <button class="retry-btn" @click="handleRetryGenerate">🔄 重新生成</button>
        </view>

        <!-- 实时生成状态面板 -->
        <view v-if="isCurrentlyGenerating && !isGenerationFailed" class="generation-status">
          <view class="status-header">
            <text class="status-icon">⚡</text>
            <text class="status-title">正在生成中...【时间较长，请耐心等待】</text>
          </view>
          <view class="status-stage">
            <text class="stage-label">当前阶段：</text>
            <text class="stage-value">{{ getCurrentStage() }}</text>
          </view>
          <view v-if="currentGeneratingChapter" class="current-chapter-info">
            <text class="chapter-label">正在处理：</text>
            <text class="chapter-name">第{{ currentGeneratingChapter.number }}{{ currentGeneratingChapter.levelLabel }} {{ currentGeneratingChapter.title }}</text>
          </view>
          <view v-if="currentBook.error" class="status-error">
            <text class="error-icon">⚠️</text>
            <text class="error-text">{{ currentBook.error }}</text>
          </view>
          <view v-if="currentBook.error && currentBook.error.includes('自动恢复')" class="status-recovery">
            <text class="recovery-icon">🔄</text>
            <text class="recovery-text">系统正在尝试自动恢复...</text>
          </view>
          <view class="status-tip">
            <text class="tip-text">💡 生成过程可能需要几分钟，请耐心等待</text>
          </view>
        </view>

        <!-- 实时配额显示 -->
        <view v-if="isCurrentlyGenerating" class="quota-monitor">
          <text class="quota-monitor-title">🎧 额度监控</text>
          <view class="quota-monitor-row">
            <text>已用：{{ usedAudioMinutes }}分钟</text>
            <text>剩余：{{ remainingAudioMinutes }}分钟</text>
          </view>
          <view v-if="overageMinutes > 0" class="quota-overage-warning">
            <text>⚠️ 已超出配额 {{ overageMinutes }} 分钟</text>
          </view>
        </view>

        <!-- 中断提示 -->
        <view v-if="currentBook.genStage === 'failed' && currentBook?.error?.includes('额度')" class="interrupted-tip">
          <text class="interrupted-icon">⚠️</text>
          <text class="interrupted-text">生成已中断：额度不足，已保存当前进度</text>
          <text class="interrupted-hint">可升级套餐后继续生成</text>
        </view>

        <!-- 失败提示 -->
        <view v-if="currentBook.genStage === 'failed' && !currentBook?.error?.includes('额度')" class="failed-tip">
          <text class="failed-icon">❌</text>
          <text class="failed-text">生成失败</text>
          <text v-if="currentBook.error" class="failed-error">{{ currentBook.error }}</text>
          <text class="failed-hint">请检查网络或稍后重试</text>
          <button class="retry-btn" @click="handleRetryGenerate">🔄 重新生成</button>
        </view>
      </view>

      <!-- 完整内容区 -->
      <view v-if="currentBook?.chapters && currentBook.chapters.length > 0" class="card content-card">
        <view class="card-header-row">
          <text class="card-title">📖 完整内容</text>
          <view class="header-actions">
            <text v-if="currentBook.outline?.mainTheme" class="outline-theme">{{ currentBook.outline.mainTheme }}</text>
            <view v-if="!isBatchMode" class="edit-btn" @click="enterBatchMode">
              <text>批量</text>
            </view>
          </view>
        </view>

        <!-- 批量操作栏 -->
        <view v-if="isBatchMode" class="batch-actions-bar">
          <view class="batch-btn" @click="toggleSelectAll">
            <text>{{ isAllSelected ? '取消全选' : '全选' }}</text>
          </view>
          <view class="batch-btn regenerate" @click="handleBatchRegenerate" :class="{ disabled: selectedChapterIds.length === 0 }">
            <text>重新生成 ({{ selectedChapterIds.length }})</text>
          </view>
          <view class="batch-btn delete" @click="handleBatchDelete" :class="{ disabled: selectedChapterIds.length === 0 }">
            <text>删除 ({{ selectedChapterIds.length }})</text>
          </view>
          <view class="batch-btn cancel" @click="exitBatchMode">
            <text>取消</text>
          </view>
        </view>

        <!-- 章节搜索与折叠工具栏 -->
        <view class="chapter-toolbar">
          <view class="search-box">
            <text class="search-icon-small">🔍</text>
            <input
              v-model="searchKeyword"
              class="search-input"
              placeholder="搜索章节标题、摘要..."
              placeholder-class="search-placeholder-text"
            />
            <text v-if="searchKeyword" class="search-clear" @click="searchKeyword = ''">✕</text>
          </view>
          <view class="toggle-btn" @click="toggleExpandAll">
            <text>{{ allExpanded ? '📁 折叠全部' : '📂 展开全部' }}</text>
          </view>
        </view>
        <view v-if="searchKeyword && filteredChaptersLevel1.length === 0" class="search-empty">
          <text>未找到匹配的章节</text>
        </view>

        <!-- 前言 -->
        <view v-if="currentBook?.metadata?.foreword" class="content-item foreword" @click="viewForeword">
          <view class="content-item-left">
            <text class="content-label">前言</text>
            <text class="content-word-count">{{ currentBook?.metadata?.foreword?.length || 0 }}字</text>
          </view>
          <view class="content-item-right">
            <text class="expand-icon">→</text>
          </view>
        </view>

        <!-- 章节列表 -->
        <view class="content-tree-list">
          <view
            v-for="chapter in filteredChaptersLevel1"
            :key="chapter.id"
            class="content-tree-item content-chapter"
          >
            <view class="content-tree-row chapter-row" @click="goToChapterDetail(chapter)">
              <view class="chapter-num-badge">{{ chapter.number }}</view>
              <view class="chapter-info">
                <text class="chapter-title">{{ chapter.title }}</text>
                <view v-if="chapter.content && chapter.content.length > 100" class="chapter-content-hint">
                  <text class="content-hint-tag">📄 正文 {{ chapter.wordCount || 0 }}字</text>
                </view>
                <text v-else-if="getChapterOutline(chapter.number)?.summary" class="chapter-outline-summary">
                  {{ getChapterOutline(chapter.number)?.summary }}
                </text>
                <view v-if="!chapter.content || chapter.content.length <= 100" class="chapter-key-points">
                  <text
                    v-for="(point, idx) in getChapterOutline(chapter.number)?.keyPoints?.slice(0, 3) || []"
                    :key="idx"
                    class="point-tag"
                  >
                    {{ point }}
                  </text>
                </view>
                <view class="chapter-media-badges">
                  <text v-if="chapterHasAudio(chapter)" class="media-badge">🎵 音频</text>
                  <text v-if="chapter.videoUrl" class="media-badge">🎬 视频</text>
                </view>
              </view>
              <view :class="['chapter-status', getNodeStatusClass(chapter)]">
                <text class="status-icon">{{ getNodeStatusIcon(chapter) }}</text>
                <text class="status-text">{{ getNodeStatusText(chapter) }}</text>
              </view>
            </view>

            <!-- 节和小节包裹折叠控制 -->
            <view v-show="allExpanded">
            <!-- 节 -->
            <view v-if="getChapterSectionsFromDB(chapter.id) && getChapterSectionsFromDB(chapter.id).length > 0" class="content-sections-list">
              <view
                v-for="section in getChapterSectionsFromDB(chapter.id)"
                :key="'sec-' + section.id"
                class="content-tree-item content-section"
                @click="goToChapterDetail(section)"
              >
                <view class="content-tree-row section-row">
                  <view class="section-num-badge">{{ section.number }}</view>
                  <view class="section-info">
                    <text class="section-title">{{ section.title }}</text>
                    <text v-if="section.summary" class="section-summary">{{ section.summary }}</text>
                  </view>
                  <view :class="['section-status', getNodeStatusClass(section)]">
                    <text class="status-icon">{{ getNodeStatusIcon(section) }}</text>
                    <text class="status-text">{{ getNodeStatusText(section) }}</text>
                  </view>
                </view>

                <!-- 小节 -->
                <view v-if="getSectionSubsectionsFromDB(section.id) && getSectionSubsectionsFromDB(section.id).length > 0" class="content-subsections-list">
                  <view
                    v-for="subsection in getSectionSubsectionsFromDB(section.id)"
                    :key="'sub-' + subsection.id"
                    class="content-tree-item content-subsection"
                    @click="goToChapterDetail(subsection)"
                  >
                    <view class="content-tree-row subsection-row">
                      <view class="subsection-num-badge">{{ subsection.number }}</view>
                      <view class="subsection-info">
                        <text class="subsection-title">{{ subsection.title }}</text>
                        <view class="subsection-media-badges">
                          <text v-if="subsection.audioUrl" class="media-badge-small">🎵 音频</text>
                          <text v-if="subsection.videoUrl" class="media-badge-small">🎬 视频</text>
                        </view>
                        <text v-if="subsection.content" class="subsection-content-preview">
                          {{ subsection.content.substring(0, 50) }}...
                        </text>
                      </view>
                      <view :class="['subsection-status', getNodeStatusClass(subsection)]">
                        <text class="status-icon">{{ getNodeStatusIcon(subsection) }}</text>
                        <text class="status-text">{{ getNodeStatusText(subsection) }}</text>
                      </view>
                    </view>
                  </view>
                </view>
              </view>
            </view>

            <!-- 章没有节时章本身就是叶子节点 -->
            <view v-else class="content-tree-item content-subsection" @click="goToChapterDetail(chapter)">
              <view class="content-tree-row subsection-row">
                <view class="subsection-info" style="margin-left: 56rpx;">
                  <text class="subsection-title">{{ chapter.title }}</text>
                </view>
                <view class="content-item-right">
                  <text class="expand-icon">→</text>
                </view>
              </view>
            </view>
            </view>
          </view>
        </view>

        <!-- 后记 -->
        <view v-if="currentBook?.metadata?.afterword" class="content-item afterword" @click="viewAfterword">
          <view class="content-item-left">
            <text class="content-label">后记</text>
            <text class="content-word-count">{{ currentBook?.metadata?.afterword?.length || 0 }}字</text>
          </view>
          <view class="content-item-right">
            <text class="expand-icon">→</text>
          </view>
        </view>
      </view>

      <!-- 操作按钮 -->
      <view class="card action-card">
        <view class="btn-col">
          <view class="btn-row">
            <button
              class="action-btn langgraph-btn"
              :disabled="generating"
              @click="handleLangGraphGenerate"
            >
              {{ generating ? '生成中...' : (isContentCompleted ? '🔄 重新生成' : '🤖 开始生成') }}
            </button>
          </view>
          <!-- 音频生成按钮 - 内容生成完成后显示 -->
          <view v-if="isContentCompleted" class="btn-row">
            <view class="btn-with-quota">
              <button
                v-if="hasEnoughQuota"
                class="action-btn audio-btn"
                :disabled="generatingAudio"
                @click="handleGenerateAudio"
              >
                {{ generatingAudio ? '生成中...' : (allLeafAudioCompleted ? '🔄 重新生成全部音频' : '🎵 生成全部音频') }}
              </button>
              <button
                v-else
                class="action-btn upgrade-btn"
                @click="goToSubscription"
              >
                🚀 升级套餐
              </button>
              <view class="quota-info-inline">
                <text class="quota-text">预估消耗 {{ estimatedAudioMinutes }} 分钟</text>
                <text v-if="quotaInfo" class="quota-text">剩余 {{ quotaInfo.remainingMinutes }} 分钟</text>
                <text v-if="!hasEnoughQuota" class="quota-warn">⚠️ 配额不足</text>
              </view>
            </view>
          </view>
        </view>

        <view v-if="generating" class="generating-tip">
          <text>正在生成中，请稍候...</text>
        </view>

        <view v-if="isBookDone && (!currentBook.chapters || currentBook.chapters.length === 0)" class="warning-tip">
          <text class="warning-icon">⚠️</text>
          <text class="warning-text">该书籍状态标记为已完成，但没有任何章节内容。</text>
          <text class="warning-hint">请尝试点击上方按钮重新生成。</text>
        </view>

        <button class="action-btn full-width" @click="goBack">
          返回书籍列表
        </button>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import * as api from '../../utils/book-generator-api';
import { post, get } from '../../utils/request';
import type { Book, Chapter } from '../../utils/book-generator-api';
import { useNotificationStore } from '../../store/notification';
import GenerationStatusBadge from '../../components/GenerationStatusBadge.vue';
// 批量操作状态
const isBatchMode = ref(false);
const selectedChapterIds = ref<string[]>([]);
const isAllSelected = ref(false);

// 章节搜索与折叠
const searchKeyword = ref('');
const allExpanded = ref(true);

// 当前书籍
const currentBook = ref<Book | null>(null);

// 生成状态
const generating = ref(false);
const generatingAudio = ref(false);
const generatingVideo = ref(false);

// 是否有内容（章节有实际内容文本）
const hasContent = computed(() => {
  if (!currentBook.value?.chapters) return false;
  return currentBook.value.chapters.some((c) => c.content && c.content.length > 100);
});

// 检测生成是否失败（有 failedStage 表明在某个阶段失败了）
const isGenerationFailed = computed(() => {
  if (!currentBook.value) return false;
  return !!currentBook.value.failedStage;
});

// 书籍是否已完成（替代旧 status === 'completed' 判断）
const isBookDone = computed(() => {
  if (!currentBook.value?.genStage) return false;
  return ['content_completed', 'audio_completed', 'video_completed'].includes(currentBook.value.genStage);
});

// AI 标题是否正在生成中（标题为临时占位符时）
const isTitleGenerating = computed(() => {
  return currentBook.value?.title === '正在生成标题...' && isCurrentlyGenerating.value;
});

// 是否正在生成中（基于 genStage 判断，或正在生成音频）
const isCurrentlyGenerating = computed(() => {
  if (!currentBook.value) return false;
  if (generatingAudio.value) return true;
  const gs = currentBook.value.genStage;
  return gs === 'outlining' || gs === 'content_generating' || gs === 'audio_generating' || gs === 'video_generating';
});

// 是否可编辑（内容生成完成前可编辑基本信息）
const canEdit = computed(() => {
  if (!currentBook.value) return false;
  const stage = currentBook.value.genStage;
  return !stage || stage === 'draft' || stage === 'outlining';
});

// 内容是否已生成完成（基于 genStage 判断）
const isContentCompleted = computed(() => {
  if (!currentBook.value?.genStage) return false;
  return currentBook.value.genStage === 'content_completed' ||
         currentBook.value.genStage === 'audio_completed' ||
         currentBook.value.genStage === 'video_completed';
});

// 所有叶子节点音频是否全部生成完成
const allLeafAudioCompleted = computed(() => {
  if (!currentBook.value?.chapters?.length) return false;
  const leafLevel = getBookLeafLevel(currentBook.value);
  const leafNodes = getBookLeafNodes(currentBook.value, leafLevel);
  if (leafNodes.length === 0) return false;
  return leafNodes.every((c: any) =>
    c.genStage === 'audio_completed' || c.genStage === 'video_completed'
  );
});

// 轮询定时器
let pollTimer: ReturnType<typeof setInterval> | null = null;
let audioPollTimer: ReturnType<typeof setInterval> | null = null;
let videoPollTimer: ReturnType<typeof setInterval> | null = null;

// 计算属性
const completedChapters = computed(() => {
  if (!currentBook.value) return 0;
  const doneGenStages = ['content_completed', 'audio_completed', 'audio_generating', 'video_generating', 'video_completed'];
  return currentBook.value.chapters.filter((c) => doneGenStages.includes(c.genStage || '')).length;
});

const chaptersLevel1 = computed(() => {
  if (!currentBook.value?.chapters) return [];
  return currentBook.value.chapters.filter((c) => c.level === 1);
});

// 当前正在生成的章节信息
const currentGeneratingChapter = computed(() => {
  if (!currentBook.value?.chapters) return null;
  const generating = currentBook.value.chapters.find(
    (c: any) => c.genStage === 'content_generating' || c.genStage === 'audio_generating' || c.genStage === 'video_generating' || c.contentStatus === 'generating'
  );
  if (!generating) return null;
  const levelLabels = ['', '章', '节', '小节'];
  const levelLabel = levelLabels[generating.level || 1] || '章';
  return { number: generating.number, title: generating.title, levelLabel };
});

// 根据搜索关键词过滤章节
const filteredChaptersLevel1 = computed(() => {
  const chapters = chaptersLevel1.value;
  if (!searchKeyword.value.trim()) return chapters;
  const keyword = searchKeyword.value.trim().toLowerCase();
  return chapters.filter(ch => {
    if (ch.title?.toLowerCase().includes(keyword)) return true;
    const outline = getChapterOutline(ch.number);
    if (outline?.summary?.toLowerCase().includes(keyword)) return true;
    if (outline?.keyPoints?.some((kp: string) => kp.toLowerCase().includes(keyword))) return true;
    const sections = getChapterSectionsFromDB(ch.id);
    if (sections.some((s: any) => s.title?.toLowerCase().includes(keyword) || s.summary?.toLowerCase().includes(keyword))) return true;
    return false;
  });
});

function toggleExpandAll() {
  allExpanded.value = !allExpanded.value;
}

const usedAudioMinutes = computed(() => {
  if (!currentBook.value) return 0;
  const doneGenStages = ['content_completed', 'audio_completed', 'audio_generating', 'video_generating', 'video_completed'];
  const totalWords = currentBook.value.chapters
    .filter((c) => doneGenStages.includes(c.genStage || ''))
    .reduce((sum, c) => sum + (c.wordCount || 0), 0);
  return Math.ceil(totalWords / 150);
});

// 预估全部音频时长（基于所有叶子节点字数）
const estimatedAudioMinutes = computed(() => {
  if (!currentBook.value) return 0;
  const leafLevel = getBookLeafLevel(currentBook.value);
  const leafNodes = getBookLeafNodes(currentBook.value, leafLevel);
  const totalWords = leafNodes.reduce((sum, c: any) => sum + (c.wordCount || 0), 0);
  return Math.max(1, Math.ceil(totalWords / 150));
});

// 配额是否足够
const hasEnoughQuota = computed(() => {
  if (!quotaInfo.value) return true; // 未加载配额信息时不限制
  const remaining = quotaInfo.value.remainingMinutes;
  // 如果启用了超额，总是允许
  if (quotaInfo.value.overageEnabled) return true;
  return remaining >= estimatedAudioMinutes.value;
});

function goToSubscription() {
  uni.navigateTo({ url: '/pages/subscription/index' });
}

function goToEdit() {
  if (!currentBook.value) return;
  uni.navigateTo({
    url: `/pages/book-generator/create?editId=${currentBook.value.id}`
  });
}

// 获取用户音频配额信息
async function fetchQuotaInfo() {
  try {
    const res = await get<{
      totalMinutes: number;
      usedMinutes: number;
      remainingMinutes: number;
      overageEnabled: boolean;
    }>('/subscription/audio-balance');
    if (res) {
      quotaInfo.value = {
        totalMinutes: res.totalMinutes,
        usedMinutes: res.usedMinutes,
        remainingMinutes: res.remainingMinutes,
        overageEnabled: res.overageEnabled,
      };
    }
  } catch {
    // 未登录或获取失败时不显示配额信息
    quotaInfo.value = null;
  }
}

const remainingAudioMinutes = computed(() => {
  if (!quotaInfo.value) return 0;
  return Math.max(0, quotaInfo.value.totalMinutes - quotaInfo.value.usedMinutes);
});

const overageMinutes = computed(() => {
  if (!currentBook.value) return 0;
  const used = usedAudioMinutes.value;
  if (!quotaInfo.value) return 0;
  return Math.max(0, used - quotaInfo.value.totalMinutes);
});

const quotaInfo = ref<{
  totalMinutes: number;
  usedMinutes: number;
  remainingMinutes: number;
  overageEnabled: boolean;
} | null>(null);

function getCurrentStage(): string {
  if (!currentBook.value) return '未知';
  // 优先使用 genStage 进行阶段描述
  if (currentBook.value.genStage) {
    return api.getBookStageDescription(currentBook.value.genStage);
  }
  const progress = currentBook.value.progress || 0;
  const error = currentBook.value.error || '';
  if (error) {
    if (error.includes('AI调用')) return 'AI调用失败，正在重试...';
    if (error.includes('超时')) return '节点执行超时，正在恢复...';
    if (error.includes('自动恢复')) return '系统正在自动恢复...';
  }
  if (progress === 0) return '准备中...';
  if (progress <= 10) return '正在生成章大纲...';
  if (progress <= 20) return '正在生成节大纲...';
  if (progress <= 30) return '正在生成小节大纲...';
  if (progress <= 90) return '正在生成章节内容...';
  if (progress <= 95) return '正在生成前言...';
  if (progress < 100) return '正在生成后记...';
  if (progress === 100) return '生成完成！';
  return '生成中...';
}

function getNodeStatusClass(node: any): string {
  if (node.contentStatus === 'completed') return 'status-completed';
  if (node.contentStatus === 'generating') return 'status-generating';
  if (node.contentStatus === 'failed') return 'status-failed';
  if (node.genStage === 'failed') return 'status-failed';
  if (node.genStage === 'content_generating' || node.genStage === 'audio_generating' || node.genStage === 'video_generating') return 'status-generating';
  if (node.genStage === 'outline_completed' || node.genStage === 'content_completed' || node.genStage === 'audio_completed' || node.genStage === 'video_completed') return 'status-completed';
  return 'status-pending';
}

function getNodeStatusIcon(node: any): string {
  if (node.contentStatus === 'completed') return '✓';
  if (node.contentStatus === 'generating') return '⚡';
  if (node.contentStatus === 'failed') return '✗';
  if (node.contentError) return '⚠';
  if (node.genStage === 'failed') return '✗';
  if (node.genStage === 'content_generating' || node.genStage === 'audio_generating' || node.genStage === 'video_generating') return '⚡';
  if (node.genStage === 'outline_completed' || node.genStage === 'content_completed' || node.genStage === 'audio_completed' || node.genStage === 'video_completed') return '✓';
  return '○';
}

function getNodeStatusText(node: any): string {
  if (node.contentStatus === 'completed') return '已完成';
  if (node.contentStatus === 'generating') return '生成中';
  if (node.contentStatus === 'failed') return '失败';
  if (node.contentError) return '错误';
  if (node.genStage === 'failed') return '失败';
  if (node.genStage === 'outline_completed') return '大纲完成';
  if (node.genStage === 'content_generating') return '生成中';
  if (node.genStage === 'content_completed') return '内容完成';
  if (node.genStage === 'audio_generating') return '生成音频';
  if (node.genStage === 'audio_completed') return '音频完成';
  if (node.genStage === 'video_generating') return '生成视频';
  if (node.genStage === 'video_completed') return '已完成';
  return '待生成';
}

function getChapterSectionsFromDB(chapterId: string | number) {
  if (!currentBook.value?.chapters) return [];
  const chapterIdNum = typeof chapterId === 'string' ? parseInt(chapterId) : chapterId;
  return currentBook.value.chapters.filter((c: any) => {
    const cParentId = typeof c.parentId === 'string' ? parseInt(c.parentId) : c.parentId;
    return cParentId === chapterIdNum && c.level === 2;
  });
}

function getSectionSubsectionsFromDB(sectionId: string | number) {
  if (!currentBook.value?.chapters) return [];
  const sectionIdNum = typeof sectionId === 'string' ? parseInt(sectionId) : sectionId;
  return currentBook.value.chapters.filter((c: any) => {
    const cParentId = typeof c.parentId === 'string' ? parseInt(c.parentId) : (c.parentId || 0);
    return cParentId === sectionIdNum && c.level === 3;
  });
}

function chapterHasAudio(chapter: any) {
  const leafLevel = getBookLeafLevel({ chapters: [chapter] } as Book);
  if (leafLevel === 1) {
    return !!chapter.audioUrl;
  }
  if (chapter.level === leafLevel) {
    return !!chapter.audioUrl;
  }
  const sections = getChapterSectionsFromDB(chapter.id);
  for (const section of sections) {
    if (section.level === leafLevel) {
      if (section.audioUrl) return true;
    } else {
      const subsections = getSectionSubsectionsFromDB(section.id);
      if (subsections.some((s: any) => s.audioUrl)) return true;
    }
  }
  return false;
}

function getBookLeafLevel(book: Book): number {
  const chapters = book.chapters || [];
  if (chapters.length === 0) return 0;
  const maxLevel = Math.max(...chapters.map(c => c.level || 0));
  return maxLevel > 0 ? maxLevel : 1;
}

function getBookLeafNodes(book: Book, leafLevel: number): any[] {
  const chapters = book.chapters || [];
  return chapters.filter(c => c.level === leafLevel);
}

function getChapterOutline(chapterNumber: number) {
  if (!currentBook.value?.outline?.chapters) return null;
  return currentBook.value.outline.chapters.find(c => c.number === chapterNumber) || null;
}

function goBack() {
  const pages = getCurrentPages();
  if (pages.length > 1) {
    uni.navigateBack();
  } else {
    uni.switchTab({ url: '/pages/index/index' });
  }
}

async function loadBook(id: string) {
  try {
    currentBook.value = await api.getBook(id);
    // 同步 generating 状态（只要不是最终完成状态，就开始轮询）
    const stage = currentBook.value.genStage;
    const failedStage = currentBook.value.failedStage;
    const isFinalState = stage === 'content_completed' || stage === 'audio_completed' || stage === 'video_completed';
    // 有 failedStage 表示生成失败
    const isFailed = !!failedStage;
    if ((stage && !isFinalState && !isFailed)) {
      generating.value = true;
    }
    // 开始生成时（包括 content_completed 后台生成音频）都要轮询，失败状态不轮询
    if (stage && stage !== 'video_completed' && !isFailed) {
      startPollingProgress(id);
    }
  } catch (e) {
    console.error('加载书籍失败:', e);
    uni.showToast({ title: '加载失败', icon: 'none' });
  }
}

function startPollingProgress(bookId: string) {
  stopPollingProgress();
  pollTimer = setInterval(async () => {
    try {
      const book = await api.getBook(bookId);
      if (!book) return;
      if (currentBook.value) {
        currentBook.value.progress = book.progress;
        currentBook.value.genStage = book.genStage as any;
        currentBook.value.error = book.error;
        // AI 异步回填标题/规模时自动更新
        if (book.title && book.title !== currentBook.value.title) {
          currentBook.value.title = book.title;
        }
        if (book.bookScale && book.bookScale !== currentBook.value.bookScale) {
          currentBook.value.bookScale = book.bookScale;
        }
        if (book.estimatedWords && book.estimatedWords !== currentBook.value.estimatedWords) {
          currentBook.value.estimatedWords = book.estimatedWords;
        }
        if (book.totalChapters && book.totalChapters !== currentBook.value.totalChapters) {
          currentBook.value.totalChapters = book.totalChapters;
        }
      }
      // 以下状态认为生成已完成，停止轮询
      const doneStages = ['content_completed', 'audio_completed', 'video_completed', 'failed'];
      if (doneStages.includes(book.genStage)) {
        stopPollingProgress();
        generating.value = false;
        currentBook.value = book;
        if (book.genStage === 'content_completed' || book.genStage === 'audio_completed' || book.genStage === 'video_completed') {
          uni.showToast({ title: '生成完成！', icon: 'success' });
          // 写入通知
          const notifStore = useNotificationStore();
          const stageLabel = book.genStage === 'content_completed' ? '内容' : book.genStage === 'audio_completed' ? '音频' : '视频';
          notifStore.add({ type: 'content_complete', title: `${stageLabel}生成完成`, message: `《${currentBook.value?.title || '书籍'}》${stageLabel}已生成完毕`, bookId });
        }
      }
    } catch (e) {
      console.error('轮询进度失败:', e);
    }
  }, 3000);
}

function stopPollingProgress() {
  if (pollTimer) {
    clearInterval(pollTimer);
    pollTimer = null;
  }
}

function startPollingAudioStatus(bookId: string) {
  stopPollingAudioStatus();
  audioPollTimer = setInterval(async () => {
    try {
      const status = await api.getAudioStatus(bookId);
      if (status.allCompleted) {
        stopPollingAudioStatus();
        generatingAudio.value = false;
        await loadBook(bookId);
        uni.showToast({ title: '音频生成完成', icon: 'success' });
        // 写入通知
        const notifStore = useNotificationStore();
        notifStore.add({ type: 'audio_complete', title: '音频生成完成', message: `《${currentBook.value?.title || '书籍'}》音频已生成完毕`, bookId });
      }
    } catch (e) {
      console.error('轮询音频状态失败:', e);
    }
  }, 5000);
}

function stopPollingAudioStatus() {
  if (audioPollTimer) {
    clearInterval(audioPollTimer);
    audioPollTimer = null;
  }
}

function startPollingVideoStatus(bookId: string) {
  stopPollingVideoStatus();
  videoPollTimer = setInterval(async () => {
    try {
      const status = await api.getVideoStatus(bookId);
      if (status.allCompleted) {
        stopPollingVideoStatus();
        generatingVideo.value = false;
        await loadBook(bookId);
        uni.showToast({ title: '视频生成完成', icon: 'success' });
        // 写入通知
        const notifStore = useNotificationStore();
        notifStore.add({ type: 'video_complete', title: '视频生成完成', message: `《${currentBook.value?.title || '书籍'}》视频已生成完毕`, bookId });
      }
    } catch (e) {
      console.error('轮询视频状态失败:', e);
    }
  }, 5000);
}

function stopPollingVideoStatus() {
  if (videoPollTimer) {
    clearInterval(videoPollTimer);
    videoPollTimer = null;
  }
}

async function handleRetryGenerate() {
  if (!currentBook.value) return;
  uni.showModal({
    title: '确认重新生成',
    content: '将重新开始生成，是否继续？',
    success: async (res) => {
      if (res.confirm) {
        await handleLangGraphGenerate();
      }
    },
  });
}

async function handleGenerateAudio() {
  if (!currentBook.value || generatingAudio.value) return;
  uni.showModal({
    title: '确认生成音频',
    content: `将为 ${currentBook.value.totalChapters || '所有'} 个章节生成音频，是否继续？`,
    success: async (res) => {
      if (!res.confirm) return;
      generatingAudio.value = true;
      try {
        const result = await api.generateAllChaptersAudio(currentBook.value.id, 'cherry');
        uni.showToast({ title: `已启动 ${result.totalChapters} 个音频生成`, icon: 'none' });
        // 开始轮询音频生成状态，不再立即清除 generatingAudio
        startPollingAudioStatus(currentBook.value.id);
        // 失败时才清除状态，成功后由轮询完成时清除
      } catch (e: any) {
        uni.showToast({ title: e.message || '启动失败', icon: 'none' });
        generatingAudio.value = false; // 失败时才清除
      }
    },
  });
}

function enterBatchMode() {
  isBatchMode.value = true;
  selectedChapterIds.value = [];
  isAllSelected.value = false;
}

function exitBatchMode() {
  isBatchMode.value = false;
  selectedChapterIds.value = [];
  isAllSelected.value = false;
}

function toggleSelectAll() {
  if (isAllSelected.value) {
    selectedChapterIds.value = [];
    isAllSelected.value = false;
  } else {
    const allSubsections = currentBook.value?.chapters?.filter(c => c.level === 3) || [];
    selectedChapterIds.value = allSubsections.map((ch: Chapter) => ch.id);
    isAllSelected.value = true;
  }
}

async function handleBatchRegenerate() {
  if (selectedChapterIds.value.length === 0 || !currentBook.value) return;
  const bookId = currentBook.value.id;
  uni.showModal({
    title: '确认重新生成',
    content: `将重新生成选中的 ${selectedChapterIds.value.length} 个章节，是否继续？`,
    success: async (res) => {
      if (res.confirm) {
        try {
          uni.showLoading({ title: '提交中...', mask: true });
          await post(`/book-generator/langgraph/books/${bookId}/chapters/batch-regenerate`, {
            chapterIds: selectedChapterIds.value,
          });
          uni.hideLoading();
          uni.showToast({ title: '已提交批量生成任务', icon: 'success' });
          exitBatchMode();
          currentBook.value = await api.getBook(bookId);
        } catch (error) {
          uni.hideLoading();
          console.error('[BatchRegenerate] 失败:', error);
          uni.showToast({ title: '批量重新生成失败', icon: 'none' });
        }
      }
    },
  });
}

async function handleBatchDelete() {
  if (selectedChapterIds.value.length === 0 || !currentBook.value) return;
  const bookId = currentBook.value.id;
  uni.showModal({
    title: '确认删除',
    content: `将删除选中的 ${selectedChapterIds.value.length} 个章节，是否继续？`,
    success: async (res) => {
      if (res.confirm) {
        try {
          uni.showLoading({ title: '删除中...', mask: true });
          await post(`/book-generator/langgraph/books/${bookId}/chapters/batch-delete`, {
            chapterIds: selectedChapterIds.value,
          });
          uni.hideLoading();
          uni.showToast({ title: '删除成功', icon: 'success' });
          exitBatchMode();
          currentBook.value = await api.getBook(bookId);
        } catch (error) {
          uni.hideLoading();
          console.error('[BatchDelete] 失败:', error);
          uni.showToast({ title: '批量删除失败', icon: 'none' });
        }
      }
    },
  });
}

async function handleLangGraphGenerate() {
  if (!currentBook.value) return;
  uni.showModal({
    title: '确认重新生成',
    content: '确定要重新生成所有内容吗？这将清除当前内容并重新开始生成。',
    success: async (res) => {
      if (!res.confirm) return;
      generating.value = true;
      currentBook.value.genStage = 'outlining';
      currentBook.value.progress = 0;
      try {
        await api.generateWithLangGraph(currentBook.value.id);
        uni.showToast({ title: 'LangGraph 生成已启动', icon: 'none', duration: 2000 });
        startPollingProgress(currentBook.value.id);
      } catch (e: any) {
        generating.value = false;
        currentBook.value.genStage = 'failed';
        uni.showToast({ title: e.message || '启动失败', icon: 'none' });
      }
    },
  });
}

function goToChapterDetail(chapter: Chapter) {
  if (!currentBook.value) return;
  (globalThis as any).__navBookData = currentBook.value;
  uni.navigateTo({
    url: `/pages/book-generator/chapter-detail?bookId=${encodeURIComponent(currentBook.value.id)}&chapter=${chapter.id}`
  });
}

function viewForeword() {
  uni.showModal({
    title: '前言',
    content: currentBook.value?.metadata?.foreword || '',
    showCancel: true,
    cancelText: '关闭',
    confirmText: '复制',
    success: (res) => {
      if (res.confirm) {
        uni.setClipboardData({
          data: currentBook.value?.metadata?.foreword || '',
          success: () => uni.showToast({ title: '已复制', icon: 'success' }),
        });
      }
    }
  });
}

function viewAfterword() {
  uni.showModal({
    title: '后记',
    content: currentBook.value?.metadata?.afterword || '',
    showCancel: true,
    cancelText: '关闭',
    confirmText: '复制',
    success: (res) => {
      if (res.confirm) {
        uni.setClipboardData({
          data: currentBook.value?.metadata?.afterword || '',
          success: () => uni.showToast({ title: '已复制', icon: 'success' }),
        });
      }
    }
  });
}

onLoad((query: any) => {
  if (query?.id) {
    loadBook(query.id);
  }
});

onMounted(() => {
  // H5 环境：从 URL hash 获取参数
  // #ifdef H5
  if (typeof window !== 'undefined') {
    const hash = window.location.hash;
    const match = hash.match(/detail\?id=([^&]+)/);
    if (match && !currentBook.value) {
      loadBook(decodeURIComponent(match[1]));
    }
  }
  // #endif
  // 获取配额信息
  fetchQuotaInfo();
});

onShow(() => {
  // onShow 时刷新书籍数据（H5 环境下 onMounted 时 hash 可能未更新）
  if (currentBook.value?.id) {
    loadBook(currentBook.value.id);
  }
  // 刷新配额信息
  fetchQuotaInfo();
});

onUnmounted(() => {
  stopPollingProgress();
  stopPollingAudioStatus();
  stopPollingVideoStatus();
});
</script>

<style scoped>
.page {
  min-height: 100vh;
  background: #f9fafb;
}

.nav-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: #ffffff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.nav-content {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 88rpx;
  padding: 0 32rpx;
  padding-top: env(safe-area-inset-top);
}

.nav-left, .nav-right { width: 80rpx; }
.nav-edit-btn { font-size: 28rpx; color: #4f46e5; font-weight: 600; }
.back-icon { font-size: 40rpx; color: #1f2937; }
.page-title {
  font-size: 32rpx;
  font-weight: 600;
  color: #1f2937;
  max-width: 400rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.main-content {
  padding-top: calc(88rpx + env(safe-area-inset-top));
  padding: 120rpx 32rpx 32rpx;
}

.card {
  background: #ffffff;
  border-radius: 24rpx;
  padding: 32rpx;
  margin-bottom: 24rpx;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.card-header-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
.header-actions { display: flex; align-items: center; gap: 16rpx; }
.edit-btn { padding: 12rpx 24rpx; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 8rpx; font-size: 28rpx; }

.batch-actions-bar { display: flex; gap: 16rpx; padding: 24rpx; background: #f9fafb; border-radius: 16rpx; margin-bottom: 24rpx; flex-wrap: wrap; }
.batch-btn { padding: 16rpx 32rpx; background: white; border: 2rpx solid #e5e7eb; border-radius: 12rpx; font-size: 28rpx; color: #374151; }
.batch-btn.regenerate { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white; border: none; }
.batch-btn.regenerate.disabled { opacity: 0.5; }
.batch-btn.delete { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; border: none; }
.batch-btn.delete.disabled { opacity: 0.5; }
.batch-btn.cancel { background: #f3f4f6; color: #6b7280; border: none; }

.card-title { font-size: 32rpx; font-weight: 600; color: #1f2937; }

.book-info { margin-bottom: 24rpx; }
.book-title-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx; }
.book-title-large { font-size: 36rpx; font-weight: 700; color: #1f2937; flex: 1; }
.book-subtitle { font-size: 28rpx; color: #6b7280; margin-bottom: 16rpx; display: block; }
.book-meta-row { display: flex; gap: 24rpx; font-size: 26rpx; color: #9ca3af; margin-top: 16rpx; }

/* 状态标签由 GenerationStatusBadge 组件统一管理 */

.progress-card { margin-bottom: 24rpx; }
.progress-display { display: flex; align-items: baseline; gap: 16rpx; margin: 16rpx 0; }
.progress-text { font-size: 48rpx; font-weight: 700; color: #4f46e5; }
.progress-text.progress-failed { color: #ef4444; }
.progress-fill-failed { background: linear-gradient(90deg, #ef4444, #dc2626) !important; }
.progress-detail { font-size: 28rpx; color: #6b7280; }
.progress-bar-large { height: 12rpx; background: #e5e7eb; border-radius: 6rpx; }

.generation-status { margin-top: 24rpx; padding: 24rpx; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16rpx; color: white; }
.status-header { display: flex; align-items: center; margin-bottom: 16rpx; }
.status-icon { font-size: 36rpx; margin-right: 12rpx; animation: pulse 2s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.status-title { font-size: 32rpx; font-weight: 600; }
.status-stage { display: flex; align-items: center; padding: 16rpx; background: rgba(255, 255, 255, 0.2); border-radius: 12rpx; margin-bottom: 16rpx; }
.stage-label { font-size: 26rpx; opacity: 0.9; margin-right: 8rpx; }
.stage-value { font-size: 28rpx; font-weight: 600; }
.current-chapter-info { display: flex; align-items: center; padding: 16rpx; background: rgba(255, 255, 255, 0.15); border-radius: 12rpx; margin-bottom: 16rpx; }
.chapter-label { font-size: 24rpx; opacity: 0.8; margin-right: 8rpx; flex-shrink: 0; }
.chapter-name { font-size: 26rpx; font-weight: 600; flex: 1; }
.status-error { display: flex; align-items: flex-start; padding: 16rpx; background: rgba(239, 68, 68, 0.3); border-radius: 12rpx; margin-bottom: 16rpx; }
.error-icon { font-size: 28rpx; margin-right: 12rpx; flex-shrink: 0; }
.error-text { font-size: 24rpx; line-height: 1.6; flex: 1; }
.status-recovery { display: flex; align-items: center; padding: 16rpx; background: rgba(251, 191, 36, 0.3); border-radius: 12rpx; margin-bottom: 16rpx; }
.recovery-icon { font-size: 32rpx; margin-right: 12rpx; animation: spin 1s linear infinite; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.recovery-text { font-size: 26rpx; font-weight: 500; }
.status-tip { padding: 12rpx; background: rgba(255, 255, 255, 0.1); border-radius: 8rpx; }
.tip-text { font-size: 24rpx; opacity: 0.9; }

.failed-tip { margin-top: 24rpx; padding: 24rpx; background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); border-radius: 16rpx; border: 2rpx solid #fca5a5; }
.failed-icon { font-size: 40rpx; display: block; text-align: center; margin-bottom: 12rpx; }
.failed-text { font-size: 32rpx; font-weight: 600; color: #dc2626; display: block; text-align: center; margin-bottom: 12rpx; }
.failed-error { font-size: 26rpx; color: #991b1b; display: block; padding: 16rpx; background: rgba(255, 255, 255, 0.5); border-radius: 8rpx; margin-bottom: 12rpx; line-height: 1.6; }
.failed-hint { font-size: 24rpx; color: #6b7280; display: block; text-align: center; margin-bottom: 16rpx; }
.retry-btn { width: 100%; height: 72rpx; background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; border: none; border-radius: 12rpx; font-size: 28rpx; font-weight: 600; }

.outline-theme { font-size: 24rpx; color: #6b7280; }

/* 章节搜索工具栏 */
.chapter-toolbar {
  display: flex;
  align-items: center;
  gap: 16rpx;
  margin-top: 20rpx;
  margin-bottom: 8rpx;
}

.search-box {
  flex: 1;
  display: flex;
  align-items: center;
  background: #f3f4f6;
  border-radius: 16rpx;
  padding: 12rpx 20rpx;
  border: 2rpx solid transparent;
  transition: border-color 0.2s;
}

.search-box:focus-within {
  border-color: #4f46e5;
  background: #ffffff;
}

.search-icon-small {
  font-size: 28rpx;
  margin-right: 12rpx;
  flex-shrink: 0;
}

.search-input {
  flex: 1;
  font-size: 26rpx;
  color: #1f2937;
  background: transparent;
}

.search-placeholder-text {
  color: #9ca3af;
  font-size: 26rpx;
}

.search-clear {
  font-size: 28rpx;
  color: #9ca3af;
  padding: 4rpx 8rpx;
  flex-shrink: 0;
}

.toggle-btn {
  flex-shrink: 0;
  padding: 12rpx 20rpx;
  background: #f3f4f6;
  border-radius: 16rpx;
  font-size: 24rpx;
  color: #4f46e5;
  white-space: nowrap;
}

.toggle-btn:active {
  background: #e5e7eb;
}

.search-empty {
  text-align: center;
  padding: 40rpx;
  color: #9ca3af;
  font-size: 26rpx;
}

.content-item { display: flex; align-items: center; justify-content: space-between; padding: 24rpx; background: #f9fafb; border-radius: 16rpx; margin-top: 16rpx; }
.content-item.foreword, .content-item.afterword { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); cursor: pointer; }
.content-item-left { display: flex; align-items: center; gap: 16rpx; }
.content-label { font-size: 28rpx; font-weight: 600; color: #1f2937; }
.content-word-count { font-size: 24rpx; color: #9ca3af; }
.content-item-right { display: flex; align-items: center; }
.expand-icon { font-size: 28rpx; color: #9ca3af; }

.content-card { margin-top: 0; }

.content-tree-list { margin-top: 20rpx; }
.content-tree-item { margin-bottom: 12rpx; }
.content-tree-row { display: flex; align-items: center; gap: 16rpx; padding: 16rpx; background: #f9fafb; border-radius: 12rpx; }

.chapter-num-badge { width: 48rpx; height: 48rpx; background: #4f46e5; color: #ffffff; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24rpx; font-weight: 600; flex-shrink: 0; }
.chapter-info { flex: 1; }
.chapter-title { font-size: 28rpx; font-weight: 600; color: #1f2937; display: block; margin-bottom: 6rpx; }
.chapter-content-hint { margin-bottom: 6rpx; }
.content-hint-tag { font-size: 22rpx; padding: 4rpx 12rpx; background: #e0f2fe; color: #0369a1; border-radius: 6rpx; }
.chapter-outline-summary { font-size: 24rpx; color: #6b7280; display: block; margin-bottom: 8rpx; }
.chapter-key-points { display: flex; flex-wrap: wrap; gap: 8rpx; margin-bottom: 8rpx; }
.point-tag { font-size: 20rpx; padding: 4rpx 12rpx; background: #f3f4f6; color: #6b7280; border-radius: 6rpx; }
.chapter-media-badges { display: flex; gap: 8rpx; }
.media-badge { font-size: 20rpx; padding: 4rpx 12rpx; background: rgba(16, 185, 129, 0.1); color: #059669; border-radius: 6rpx; }

.chapter-status { display: flex; flex-direction: column; align-items: center; justify-content: center; min-width: 80rpx; padding: 8rpx 16rpx; border-radius: 8rpx; flex-shrink: 0; }
.chapter-status.status-completed { background: #d1fae5; color: #059669; }
.chapter-status.status-pending { background: #e5e7eb; color: #6b7280; }
.chapter-status.status-generating { background: #fef3c7; color: #d97706; animation: pulse-bg 2s ease-in-out infinite; }
.chapter-status.status-failed { background: #fee2e2; color: #dc2626; }
@keyframes pulse-bg { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
.status-icon { font-size: 28rpx; font-weight: bold; line-height: 1; }
.status-text { font-size: 20rpx; margin-top: 4rpx; white-space: nowrap; }

.section-num-badge { width: 40rpx; height: 40rpx; background: #8b5cf6; color: #ffffff; border-radius: 8rpx; display: flex; align-items: center; justify-content: center; font-size: 22rpx; font-weight: 600; flex-shrink: 0; }
.section-info { flex: 1; }
.section-title { font-size: 26rpx; font-weight: 500; color: #374151; display: block; margin-bottom: 4rpx; }
.section-summary { font-size: 22rpx; color: #9ca3af; display: block; }

.section-status { display: flex; flex-direction: column; align-items: center; justify-content: center; min-width: 70rpx; padding: 6rpx 12rpx; border-radius: 6rpx; flex-shrink: 0; }
.section-status.status-completed { background: #d1fae5; color: #059669; }
.section-status.status-pending { background: #f3f4f6; color: #9ca3af; }
.section-status.status-generating { background: #fef3c7; color: #d97706; }
.section-status.status-failed { background: #fee2e2; color: #dc2626; }

.subsection-num-badge { width: 36rpx; height: 36rpx; background: #06b6d4; color: #ffffff; border-radius: 6rpx; display: flex; align-items: center; justify-content: center; font-size: 20rpx; font-weight: 500; flex-shrink: 0; }
.subsection-info { flex: 1; }
.subsection-title { font-size: 24rpx; color: #6b7280; display: block; }
.subsection-media-badges { display: flex; gap: 8rpx; margin-top: 4rpx; }
.media-badge-small { font-size: 18rpx; padding: 2rpx 8rpx; background: rgba(16, 185, 129, 0.1); color: #059669; border-radius: 4rpx; }
.subsection-content-preview { font-size: 22rpx; color: #6b7280; margin-top: 4rpx; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

.subsection-status { display: flex; flex-direction: column; align-items: center; justify-content: center; min-width: 70rpx; padding: 6rpx 12rpx; border-radius: 6rpx; flex-shrink: 0; }
.subsection-status.status-completed { background: #d1fae5; color: #059669; }
.subsection-status.status-pending { background: #f3f4f6; color: #9ca3af; }
.subsection-status.status-generating { background: #fef3c7; color: #d97706; }
.subsection-status.status-failed { background: #fee2e2; color: #dc2626; }

.action-card { margin-bottom: 24rpx; }
.btn-col { margin-bottom: 20rpx; }
.btn-row { display: flex; gap: 16rpx; margin-bottom: 20rpx; }
.action-btn { flex: 1; height: 88rpx; background: #f3f4f6; border-radius: 16rpx; font-size: 28rpx; color: #374151; display: flex; align-items: center; justify-content: center; border: none; }
.action-btn.primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #ffffff; }
.action-btn.full-width { width: 100%; flex: none; margin-bottom: 16rpx; }
.action-btn[disabled] { opacity: 0.6; }
.action-btn.langgraph-btn { background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #ffffff; }
.action-btn.audio-btn { background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); color: #ffffff; }
.action-btn.upgrade-btn { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: #ffffff; }
.btn-with-quota { display: flex; flex-direction: column; gap: 8rpx; width: 100%; }
.quota-info-inline { display: flex; flex-wrap: wrap; gap: 12rpx; padding: 8rpx 0; }
.quota-text { font-size: 22rpx; color: #6b7280; }
.quota-warn { font-size: 22rpx; color: #ef4444; font-weight: 600; }
.generating-tip { text-align: center; padding: 20rpx; color: #6b7280; font-size: 26rpx; }

.warning-tip { padding: 24rpx; background: #fef3c7; border-radius: 12rpx; margin-top: 16rpx; }
.warning-icon { font-size: 32rpx; display: block; margin-bottom: 8rpx; }
.warning-text { font-size: 26rpx; color: #92400e; display: block; margin-bottom: 8rpx; }
.warning-hint { font-size: 24rpx; color: #b45309; display: block; }
</style>
