<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">交互式创建</text>
      </view>
      <!-- 模式切换独立行 -->
      <view class="mode-bar">
        <text class="mode-switch" @click="switchToSimple">切换到一键模式</text>
      </view>
    </view>

    <!-- 步骤指示器 -->
    <view class="steps-bar">
      <view v-for="(step, i) in steps" :key="i" :class="['step-dot', currentStep >= i ? 'active' : '', currentStep > i ? 'done' : '']">
        <view class="dot-num">{{ currentStep > i ? '✓' : step.num }}</view>
        <text class="dot-label">{{ step.label }}</text>
      </view>
      <view class="step-line" :style="{ width: (currentStep / (steps.length - 1) * 100) + '%' }"></view>
    </view>

    <!-- 主内容区 -->
    <view class="main-content">
      <!-- ============ Step 1: 信息输入 ============ -->
      <view v-if="currentStep === 0" class="card">
        <view class="card-header">
          <text class="card-title">📝 第一步：基本信息</text>
        </view>

        <view class="form-item">
          <text class="form-label">书名 *</text>
          <input v-model="form.title" class="form-input" placeholder="例如：《时间是什么》" />
        </view>

        <view class="form-item">
          <text class="form-label">内容描述 *</text>
          <textarea v-model="form.description" class="form-textarea" placeholder="描述这本书的内容、主题、写作目的..." :maxlength="500" />
          <view class="ai-recommend-row">
            <button class="btn-ai-recommend" :disabled="!form.description.trim() || recommendLoading" @click="doSmartRecommend">
              {{ recommendLoading ? 'AI分析中...' : '🤖 AI智能推荐' }}
            </button>
            <text class="ai-recommend-hint">根据描述自动填充目标人群和书籍类型</text>
          </view>
        </view>

        <view class="form-item">
          <text class="form-label">书籍规模 *</text>
          <view class="scale-picker">
            <view
              v-for="scale in bookScales"
              :key="scale.value"
              :class="['scale-chip', form.bookScale === scale.value ? 'active' : '']"
              @click="form.bookScale = scale.value"
            >
              <text class="scale-label">{{ scale.label }}</text>
              <text class="scale-desc">{{ scale.chapters }} · {{ scale.audio }}</text>
            </view>
          </view>
        </view>

        <!-- 目标人群 -->
        <view class="form-item">
          <view class="label-with-badge">
            <text class="form-label">目标人群</text>
            <view class="required-badge">核心</view>
          </view>
          <text class="form-hint-small">指定读者，AI会据此调整语言风格和深度</text>
          <view class="chip-group" style="margin-top: 8rpx;">
            <view
              v-for="a in audiences"
              :key="a.value"
              :class="['chip', form.targetAudience === a.value ? 'active' : '']"
              @click="form.targetAudience = a.value"
            >
              <text class="chip-icon">{{ a.icon }}</text>
              <text class="chip-text">{{ a.label }}</text>
              <text class="chip-desc" style="font-size: 20rpx; margin-left: 4rpx;">{{ a.desc }}</text>
            </view>
          </view>
        </view>

        <view class="form-item">
          <text class="form-label">书籍类型</text>
          <view class="chip-group">
            <view
              v-for="bt in bookTypes"
              :key="bt.value"
              :class="['chip', form.bookType === bt.value ? 'active' : '']"
              @click="form.bookType = bt.value"
            >
              <text class="chip-icon">{{ bt.icon }}</text>
              <text class="chip-text">{{ bt.label }}</text>
            </view>
          </view>
        </view>

        <view class="form-item">
          <text class="form-label">大纲层级</text>
          <view class="chip-group">
            <view
              v-for="opt in outlineLevelOptions"
              :key="opt.value"
              :class="['chip', form.outlineLevel === opt.value ? 'active' : '']"
              @click="form.outlineLevel = opt.value"
            >
              <text class="chip-text">{{ opt.label }}</text>
            </view>
          </view>
        </view>

        <!-- 预估 -->
        <view v-if="bookEstimate" class="estimate-card">
          <view class="estimate-row">
            <text class="estimate-label">预估字数：</text>
            <text class="estimate-value">{{ bookEstimate.words.min }}~{{ bookEstimate.words.max }}字</text>
          </view>
          <view class="estimate-row">
            <text class="estimate-label">预估章节：</text>
            <text class="estimate-value">约{{ bookEstimate.estimatedChapters }}章</text>
          </view>
        </view>

        <view class="btn-group">
          <button class="btn-cancel" @click="goBack">取消</button>
          <button class="btn-primary" :disabled="!canProceed || stepLoading" @click="doCreateBook">
            {{ stepLoading ? '创建中...' : '下一步：AI分析' }}
          </button>
        </view>
      </view>

      <!-- ============ Step 2: Plan 审核 ============ -->
      <view v-if="currentStep === 1" class="card">
        <view class="card-header">
          <text class="card-title">🧠 第二步：AI 深度规划</text>
          <text v-if="planLoading" class="loading-hint">AI 正在分析...</text>
        </view>

        <!-- AI自动生成提示 -->
        <view v-if="!planLoading && planData" class="ai-generated-notice">
          <text class="notice-icon">✨</text>
          <text class="notice-text">AI 已根据你的描述自动生成了以下方案，可直接确认或提出修改意见后重新分析</text>
        </view>

        <view v-if="planLoading" class="loading-box">
          <view class="loading-spinner"></view>
          <text class="loading-text">AI 正在制定书籍规划，请稍候...</text>
        </view>

        <view v-else-if="planData" class="plan-review">
          <!-- 概要信息 -->
          <view v-if="planSummaryInfo.length" class="plan-summary">
            <text v-for="s in planSummaryInfo" :key="s.label" class="summary-tag">
              {{ s.icon }} {{ s.label }}: {{ s.value }}
            </text>
          </view>

          <!-- 黄金主线 -->
          <view class="plan-section">
            <view class="plan-section-header">
              <text class="plan-section-icon">🎯</text>
              <text class="plan-section-title">黄金主线</text>
              <text class="plan-section-hint">一句话概括本书的核心线索</text>
            </view>
            <textarea v-model="planData.goldenThread" class="plan-textarea" :maxlength="500" placeholder="AI生成的核心主线..." />
          </view>

          <!-- 叙事弧 -->
          <view class="plan-section">
            <view class="plan-section-header">
              <text class="plan-section-icon">📈</text>
              <text class="plan-section-title">叙事弧</text>
              <text class="plan-section-hint">AI将全书拆分为几个大的叙事阶段</text>
            </view>
            <view v-if="narrativeParts.length" class="narrative-parts">
              <view v-for="(part, pi) in narrativeParts" :key="pi" class="narrative-part-card">
                <view class="part-badge">{{ part.key }}</view>
                <view class="part-body">
                  <view class="part-field">
                    <text class="part-label">主题</text>
                    <input v-model="part.theme" class="form-input-sm" />
                  </view>
                  <view class="part-field">
                    <text class="part-label">覆盖章节</text>
                    <text class="part-text">{{ (part.chapters || []).join(', ') || '—' }}</text>
                  </view>
                  <view class="part-field">
                    <text class="part-label">目标</text>
                    <textarea v-model="part.goal" class="form-textarea-xs" :maxlength="300" />
                  </view>
                </view>
              </view>
            </view>
            <view v-else class="plan-empty">
              <text class="empty-text">AI 未生成叙事弧结构</text>
            </view>
          </view>

          <!-- 语调风格 -->
          <view class="plan-section">
            <view class="plan-section-header">
              <text class="plan-section-icon">🎵</text>
              <text class="plan-section-title">语调风格</text>
            </view>
            <view v-if="toneFields" class="tone-fields">
              <view class="tone-row">
                <text class="tone-label">基础语调</text>
                <input v-model="toneFields.base" class="form-input-sm" />
              </view>
              <view class="tone-row">
                <text class="tone-label">示例用语</text>
                <textarea v-model="toneFields.examples" class="form-textarea-xs" :maxlength="300" />
              </view>
              <view v-if="toneFields.avoidPatterns" class="tone-row">
                <text class="tone-label">避免模式</text>
                <view class="tag-list">
                  <view v-for="(ap, api) in toneFields.avoidPatterns" :key="api" class="tag-item">
                    <text class="tag-text">{{ ap }}</text>
                    <text class="tag-remove" @click="removeAvoidPattern(api)">x</text>
                  </view>
                  <view class="tag-add" @click="addAvoidPattern">+ 添加</view>
                </view>
              </view>
            </view>
            <view v-else class="plan-empty">
              <text class="empty-text">AI 未生成语调配置</text>
            </view>
          </view>

          <!-- 受众校准 -->
          <view class="plan-section">
            <view class="plan-section-header">
              <text class="plan-section-icon">👥</text>
              <text class="plan-section-title">受众校准</text>
            </view>
            <view v-if="audienceFields" class="audience-fields">
              <view class="audience-row">
                <text class="audience-label">已有知识</text>
                <view class="tag-list">
                  <text v-for="(ak, aki) in audienceFields.assumedKnowledge" :key="aki" class="tag-item-static">{{ ak }}</text>
                </view>
              </view>
              <view class="audience-row">
                <text class="audience-label">阅读痛点</text>
                <view class="tag-list">
                  <text v-for="(pp, ppi) in audienceFields.painPoints" :key="ppi" class="tag-item-static">{{ pp }}</text>
                </view>
              </view>
              <view class="audience-row">
                <text class="audience-label">期望收获</text>
                <textarea v-model="audienceFields.desiredOutcome" class="form-textarea-xs" :maxlength="300" />
              </view>
            </view>
            <view v-else class="plan-empty">
              <text class="empty-text">AI 未生成受众校准</text>
            </view>
          </view>

          <!-- 用户自然语言反馈 -->
          <view class="plan-section feedback-section">
            <view class="plan-section-header">
              <text class="plan-section-icon">💬</text>
              <text class="plan-section-title">修改意见</text>
              <text class="plan-section-hint">用自然语言告诉AI你想怎么调整</text>
            </view>
            <textarea v-model="userFeedback" class="feedback-textarea" :maxlength="500" placeholder="比如：「语调太严肃了，改得更活泼一点」「再加一章关于水星探索历史的」" />
            <view class="feedback-hint">
              <text v-if="userFeedback.trim()">点击「重新分析」AI会参考你的意见重新规划</text>
              <text v-else>输入修改意见后点击「重新分析」，AI会根据你的反馈调整方案</text>
            </view>
          </view>
        </view>

        <view v-if="planError" class="error-box">
          <text class="error-text">{{ planError }}</text>
        </view>

        <view v-if="!planLoading" class="btn-group">
          <button class="btn-primary" :disabled="stepLoading" @click="doSavePlan">
            {{ stepLoading ? '保存中...' : '使用此方案，生成大纲' }}
          </button>
          <button class="btn-secondary" :disabled="regeneratingPlan" @click="doRefinePlan">
            {{ regeneratingPlan ? '重新分析中...' : '重新分析' }}
          </button>
          <button class="btn-cancel" @click="currentStep = 0">返回修改</button>
        </view>
      </view>

      <!-- ============ Step 3: 大纲审核 ============ -->
      <view v-if="currentStep === 2" class="card">
        <view class="card-header">
          <text class="card-title">📚 第三步：审核大纲</text>
          <text v-if="outlineLoading" class="loading-hint">AI 正在生成大纲...</text>
        </view>

        <view v-if="outlineLoading" class="loading-box">
          <view class="loading-spinner"></view>
          <text class="loading-text">AI 正在生成详细大纲，请稍候...</text>
        </view>

        <view v-else-if="outlineChapters.length > 0" class="outline-review">
          <text class="outline-subtitle">共 {{ outlineChapters.length }} 章，点击展开编辑详情</text>

          <view v-for="(ch, ci) in outlineChapters" :key="ci" class="chapter-card">
            <view class="chapter-header" @click="toggleChapterExpand(ci)">
              <text class="chapter-num">第{{ ch.number }}章</text>
              <text class="expand-icon">{{ expandedChapters[ci] ? '▼' : '▶' }}</text>
            </view>
            <view v-if="expandedChapters[ci]" class="chapter-edit">
              <view class="form-item">
                <text class="form-label-sm">章节标题</text>
                <input v-model="ch.title" class="form-input-sm" />
              </view>
              <view class="form-item">
                <text class="form-label-sm">摘要</text>
                <textarea v-model="ch.summary" class="form-textarea-sm" :maxlength="500" />
              </view>
              <view class="form-item">
                <text class="form-label-sm">预估字数</text>
                <input v-model.number="ch.estimatedWords" class="form-input-sm" type="number" />
              </view>
              <view class="form-item">
                <text class="form-label-sm">写作指令</text>
                <textarea v-model="ch.writingInstructions" class="form-textarea-sm" :maxlength="500" placeholder="AI写作时会参考这些指令..." />
              </view>
              <view class="btn-row-sm">
                <button class="btn-delete-sm" @click="removeChapter(ci)">删除此章</button>
                <button class="btn-move-sm" :disabled="ci === 0" @click="moveChapter(ci, -1)">↑ 上移</button>
                <button class="btn-move-sm" :disabled="ci >= outlineChapters.length - 1" @click="moveChapter(ci, 1)">↓ 下移</button>
              </view>
            </view>
          </view>

          <button class="btn-add-chapter" @click="addChapter">+ 添加章节</button>
        </view>

        <view v-if="outlineError" class="error-box">
          <text class="error-text">{{ outlineError }}</text>
        </view>

        <view v-if="!outlineLoading" class="btn-group">
          <button class="btn-cancel" @click="currentStep = 1">返回修改</button>
          <button class="btn-secondary" :disabled="regeneratingOutline" @click="doGenerateOutline">
            {{ regeneratingOutline ? '重新生成中...' : '重新生成大纲' }}
          </button>
          <button class="btn-primary" :disabled="stepLoading" @click="doSaveOutline">
            {{ stepLoading ? '保存中...' : '确认，开始写内容' }}
          </button>
        </view>
      </view>

      <!-- ============ Step 4: 内容生成 ============ -->
      <view v-if="currentStep === 3" class="card">
        <view class="card-header">
          <text class="card-title">✍️ 第四步：生成内容</text>
        </view>

        <view class="generate-box">
          <view class="generate-stage" :class="{ 'completed': genProgress > 0 }">
            <text class="stage-icon">{{ genProgress > 0 ? '✅' : '⏳' }}</text>
            <view class="stage-info">
              <text class="stage-title">并行内容生成</text>
              <text class="stage-hint">{{ genProgress > 0 ? '已完成' : '等待开始...' }}</text>
            </view>
          </view>
          <view class="generate-stage" :class="{ 'completed': genProgress >= 100 }">
            <text class="stage-icon">{{ genProgress >= 100 ? '✅' : '⏳' }}</text>
            <view class="stage-info">
              <text class="stage-title">连贯性编辑</text>
              <text class="stage-hint">{{ genProgress >= 100 ? '已完成' : '等待中...' }}</text>
            </view>
          </view>

          <view v-if="genStarted && genProgress < 100" class="progress-bar-wrap">
            <view class="progress-bar" :style="{ width: genProgress + '%' }"></view>
            <text class="progress-text">生成进度 {{ genProgress }}%</text>
          </view>

          <view v-if="genError" class="error-box">
            <text class="error-text">{{ genError }}</text>
          </view>
        </view>

        <view v-if="genStarted && genProgress < 100" class="btn-group">
          <button class="btn-cancel" :disabled="true">请等待生成完成...</button>
        </view>

        <view v-if="genProgress >= 100" class="btn-group">
          <button class="btn-primary" @click="goToDetail">查看书籍详情</button>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import * as api from '../../utils/book-generator-api';
import { getApiBaseUrl } from '../../utils/config';

const BASE_URL = getApiBaseUrl();

// ============ 步骤状态 ============
const currentStep = ref(0);
const steps = [
  { num: 1, label: '信息输入' },
  { num: 2, label: 'AI规划' },
  { num: 3, label: '审核大纲' },
  { num: 4, label: '生成内容' },
];

// ============ Step 1: 表单 ============
const stepLoading = ref(false);
const form = ref({
  title: '',
  description: '',
  bookScale: '80000',
  bookType: 'auto',
  outlineLevel: 'auto',
  targetAudience: '',
});
const bookId = ref('');
const bookEstimate = ref<{ words: { min: number; max: number }; audioMinutes: { min: number; max: number }; estimatedChapters: number } | null>(null);

const bookScales = [
  { value: '340000', label: '上2', chapters: '170章', audio: '约28小时' },
  { value: '210000', label: '上1', chapters: '105章', audio: '约17小时' },
  { value: '130000', label: '标准', chapters: '65章', audio: '约11小时' },
  { value: '80000', label: '下1', chapters: '40章', audio: '约7小时' },
  { value: '50000', label: '下2', chapters: '25章', audio: '约4小时' },
  { value: '31000', label: '下3', chapters: '16章', audio: '约2.5小时' },
  { value: '19000', label: '下4', chapters: '10章', audio: '约1.5小时' },
  { value: '12000', label: '下5', chapters: '6章', audio: '约1小时' },
  { value: '7000', label: '下6', chapters: '4章', audio: '约35分钟' },
  { value: '4000', label: '下7', chapters: '2章', audio: '约20分钟' },
  { value: '1000', label: '下10', chapters: '1章', audio: '约5分钟' },
];

const bookTypes = [
  { value: 'auto', label: '自动检测', icon: '🤖' },
  { value: '教材', label: '教材/学术', icon: '📚' },
  { value: '技术教程', label: '技术教程', icon: '💻' },
  { value: '小说', label: '小说/文学', icon: '📖' },
  { value: '商业', label: '商业/经管', icon: '📊' },
  { value: '科普', label: '科普/大众', icon: '🔬' },
];


const audiences = [
  { value: '儿童', label: '儿童', icon: '👶', desc: '6-12岁，形象生动' },
  { value: '青少年', label: '青少年', icon: '🧑', desc: '13-18岁，通俗易懂' },
  { value: '大学生', label: '大学生', icon: '🎓', desc: '系统专业，有理论' },
  { value: '专业人士', label: '专业人士', icon: '💼', desc: '从业者，实用深入' },
  { value: '研究生', label: '研究生', icon: '🔍', desc: '研究级别，前沿深入' },
  { value: '大众读者', label: '大众读者', icon: '👥', desc: '通俗普及，有趣味' },
];
const outlineLevelOptions = [
  { value: 'auto', label: '自动' },
  { value: '1', label: '1层' },
  { value: '2', label: '2层' },
  { value: '3', label: '3层' },
];

const canProceed = computed(() => form.value.title.trim() && form.value.description.trim());

// AI智能推荐
const recommendLoading = ref(false);

async function doSmartRecommend() {
  if (!form.value.description.trim() || recommendLoading.value) return;
  recommendLoading.value = true;
  try {
    const response = await uni.request({
      url: `${BASE_URL}/book-generator/langgraph/smart-recommend`,
      method: 'POST',
      data: { description: form.value.description, title: form.value.title },
    });
    const res = response.data as any;
    if (res.code === 0 && res.data) {
      const data = res.data;
      // 自动填充目标人群
      if (data.targetAudience && data.targetAudience !== '通用') {
        const matched = audiences.find(a => a.value === data.targetAudience || a.label === data.targetAudience);
        if (matched) {
          form.value.targetAudience = matched.value;
        }
      }
      // 自动填充书籍类型
      if (data.industry || data.style) {
        const typeHint = data.industry || data.style;
        const matched = bookTypes.find(t => t.label.includes(typeHint) || typeHint.includes(t.label));
        if (matched && matched.value !== 'auto') {
          form.value.bookType = matched.value;
        }
      }
      uni.showToast({ title: `推荐: ${data.reason || '已自动填充'}`, icon: 'none', duration: 2000 });
    }
  } catch (e: any) {
    uni.showToast({ title: '推荐失败，请手动选择', icon: 'none' });
  } finally {
    recommendLoading.value = false;
  }
}

// ============ Step 2: Plan ============
const planLoading = ref(false);
const planData = ref<any>(null);
const planRaw = ref('');
const planError = ref('');
const regeneratingPlan = ref(false);
const userFeedback = ref('');

// 解析叙事弧为 flat array
const narrativeParts = computed(() => {
  const arc = planData.value?.narrativeArc;
  if (!arc || typeof arc !== 'object') return [];
  return Object.entries(arc).map(([key, val]: [string, any]) => ({
    key,
    theme: val.theme || '',
    chapters: val.chapters || [],
    goal: val.goal || '',
  }));
});

// 解析语调配置
const toneFields = computed(() => {
  const tp = planData.value?.toneProfile;
  if (!tp || typeof tp !== 'object') return null;
  return {
    base: tp.base || '',
    examples: tp.examples || '',
    avoidPatterns: Array.isArray(tp.avoidPatterns) ? [...tp.avoidPatterns] : [],
  };
});

// 解析受众校准
const audienceFields = computed(() => {
  const ac = planData.value?.audienceCalibration;
  if (!ac || typeof ac !== 'object') return null;
  return {
    assumedKnowledge: Array.isArray(ac.assumedKnowledge) ? ac.assumedKnowledge : [],
    painPoints: Array.isArray(ac.painPoints) ? ac.painPoints : [],
    desiredOutcome: ac.desiredOutcome || '',
  };
});

// 概要标签
const summaryMeta = [
  { key: 'bookType', label: '类型', icon: '📖' },
  { key: 'writingStyle', label: '风格', icon: '✏️' },
  { key: 'contentDepth', label: '深度', icon: '📊' },
  { key: 'targetAudienceAnalysis', label: '目标读者', icon: '🎯' },
  { key: 'bookTypeAnalysis', label: '结构分析', icon: '🔍' },
];
const planSummaryInfo = computed(() =>
  summaryMeta
    .filter(m => planData.value?.[m.key] && typeof planData.value[m.key] === 'string')
    .map(m => ({ ...m, value: planData.value[m.key] }))
);

// ============ Step 3: Outline ============
const outlineLoading = ref(false);
const outlineChapters = ref<any[]>([]);
const outlineError = ref('');
const regeneratingOutline = ref(false);
const expandedChapters = ref<Record<number, boolean>>({});

// ============ Step 4: Generation ============
const genStarted = ref(false);
const genProgress = ref(0);
const genError = ref('');
let progressTimer: any = null;

// ============ 方法 ============

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

function switchToSimple() {
  uni.redirectTo({ url: '/pages/book-generator/create' });
}

async function fetchEstimate() {
  try {
    const response = await uni.request({
      url: `${BASE_URL}/book-generator/langgraph/estimate?scale=${form.value.bookScale}&userId=1`,
      method: 'GET',
    });
    const res = response.data as any;
    if (res.code === 0 && res.data?.words) {
      bookEstimate.value = {
        words: res.data.words,
        audioMinutes: res.data.audioMinutes,
        estimatedChapters: res.data.estimatedChapters,
      };
    }
  } catch (e) { /* ignore */ }
}

// Step 1: 创建书籍（不自动生成）
async function doCreateBook() {
  if (!canProceed.value) return;
  stepLoading.value = true;
  try {
    const genLevel = form.value.outlineLevel === 'auto' ? undefined : parseInt(form.value.outlineLevel);
    const bookType = form.value.bookType === 'auto' ? undefined : form.value.bookType;
    const book = await api.createBookInteractive({
      title: form.value.title,
      description: form.value.targetAudience        ? form.value.description + '\n目标人群：' + form.value.targetAudience        : form.value.description,
      bookScale: form.value.bookScale,
      bookType,
      genLevel,
    });
    bookId.value = book.id;
    currentStep.value = 1;
    // 自动触发 Plan 生成
    doGeneratePlan();
  } catch (e: any) {
    uni.showToast({ title: e.message || '创建失败', icon: 'none' });
  } finally {
    stepLoading.value = false;
  }
}

// Step 2: 生成 Plan (异步轮询)
let planPollTimer: ReturnType<typeof setInterval> | null = null;

function stopPlanPolling() {
  if (planPollTimer) { clearInterval(planPollTimer); planPollTimer = null; }
}

async function doGeneratePlan(feedback?: string) {
  planLoading.value = true;
  planError.value = '';
  regeneratingPlan.value = true;
  stopPlanPolling();

  try {
    // 1. 启动异步生成
    await api.startInteractivePlan(bookId.value, feedback);

    // 2. 轮询等待结果（最多等待 180s）
    const startTime = Date.now();
    planPollTimer = setInterval(async () => {
      try {
        const res = await api.pollInteractivePlan(bookId.value);

        if (res.genStage === 'plan_failed') {
          stopPlanPolling();
          planError.value = res.error || 'AI 规划失败';
          planLoading.value = false;
          regeneratingPlan.value = false;
          return;
        }

        if (res.genStage === 'plan_ready' && res.planData) {
          stopPlanPolling();
          planRaw.value = res.rawPlan || '';
          planData.value = res.planData;
          planLoading.value = false;
          regeneratingPlan.value = false;
          return;
        }

        // 超时检查
        if (Date.now() - startTime > 600000) {
          stopPlanPolling();
          planError.value = 'AI 规划超时，请重新分析';
          planLoading.value = false;
          regeneratingPlan.value = false;
        }
      } catch { /* ignore polling errors */ }
    }, 3000);
  } catch (e: any) {
    planError.value = e.message || '启动失败';
    planLoading.value = false;
    regeneratingPlan.value = false;
  }
}

// Step 2: 保存 Plan
async function doSavePlan() {
  stepLoading.value = true;
  try {
    const dataToSave: any = { ...planData.value };
    if (narrativeParts.value.length) {
      const arc: any = {};
      narrativeParts.value.forEach(p => { arc[p.key] = { theme: p.theme, chapters: p.chapters, goal: p.goal }; });
      dataToSave.narrativeArc = arc;
    }
    if (toneFields.value) dataToSave.toneProfile = { ...toneFields.value };
    if (audienceFields.value) dataToSave.audienceCalibration = { ...audienceFields.value };
    await api.updateInteractivePlan(bookId.value, JSON.stringify(dataToSave));
    currentStep.value = 2;
    doGenerateOutline();
  } catch (e: any) {
    uni.showToast({ title: e.message || '保存失败', icon: 'none' });
  } finally {
    stepLoading.value = false;
  }
}

async function doRefinePlan() {
  if (regeneratingPlan.value) return;
  const dataToSave: any = { ...planData.value };
  if (narrativeParts.value.length) {
    const arc: any = {};
    narrativeParts.value.forEach(p => { arc[p.key] = { theme: p.theme, chapters: p.chapters, goal: p.goal }; });
    dataToSave.narrativeArc = arc;
  }
  if (toneFields.value) dataToSave.toneProfile = { ...toneFields.value };
  if (audienceFields.value) dataToSave.audienceCalibration = { ...audienceFields.value };
  try { await api.updateInteractivePlan(bookId.value, JSON.stringify(dataToSave)); } catch { /* ignore */ }
  planData.value = null;
  planRaw.value = '';
  planError.value = '';
  doGeneratePlan(userFeedback.value);
}

function removeAvoidPattern(index: number) {
  if (toneFields.value?.avoidPatterns) toneFields.value.avoidPatterns.splice(index, 1);
}
function addAvoidPattern() {
  if (toneFields.value?.avoidPatterns) toneFields.value.avoidPatterns.push('新避免项');
}

// Step 3: 生成 Outline (异步轮询)
let outlinePollTimer: ReturnType<typeof setInterval> | null = null;

function stopOutlinePolling() {
  if (outlinePollTimer) { clearInterval(outlinePollTimer); outlinePollTimer = null; }
}

async function doGenerateOutline() {
  outlineLoading.value = true;
  outlineError.value = '';
  regeneratingOutline.value = true;
  stopOutlinePolling();

  try {
    // 1. 启动异步生成
    await api.startInteractiveOutline(bookId.value);

    // 2. 轮询等待结果（最多等待 180s）
    const startTime = Date.now();
    outlinePollTimer = setInterval(async () => {
      try {
        const res = await api.pollInteractiveOutline(bookId.value);

        if (res.genStage === 'outline_failed') {
          stopOutlinePolling();
          outlineError.value = res.error || '大纲生成失败';
          outlineLoading.value = false;
          regeneratingOutline.value = false;
          return;
        }

        if (res.genStage === 'outline_ready' && res.outlineData?.chapters) {
          stopOutlinePolling();
          outlineChapters.value = res.outlineData.chapters.map((ch: any, i: number) => ({
            number: ch.number || (i + 1),
            title: ch.title || '',
            summary: ch.summary || '',
            estimatedWords: ch.estimatedWords || 2000,
            writingInstructions: ch.writingInstructions || '',
            sections: ch.sections || [],
            subsections: ch.subsections || [],
          }));

          // 默认展开前3章
          const exp: Record<number, boolean> = {};
          outlineChapters.value.forEach((_: any, i: number) => { if (i < 3) exp[i] = true; });
          expandedChapters.value = exp;

          outlineLoading.value = false;
          regeneratingOutline.value = false;
          return;
        }

        // 超时检查
        if (Date.now() - startTime > 600000) {
          stopOutlinePolling();
          outlineError.value = '大纲生成超时，请重新生成';
          outlineLoading.value = false;
          regeneratingOutline.value = false;
        }
      } catch { /* ignore polling errors */ }
    }, 3000);
  } catch (e: any) {
    outlineError.value = e.message || '启动失败';
    outlineLoading.value = false;
    regeneratingOutline.value = false;
  }
}

// Step 3: 章节操作
function toggleChapterExpand(i: number) {
  expandedChapters.value = { ...expandedChapters.value, [i]: !expandedChapters.value[i] };
}
function removeChapter(i: number) {
  outlineChapters.value.splice(i, 1);
  // 重新编号
  outlineChapters.value.forEach((ch: any, idx: number) => { ch.number = idx + 1; });
}
function moveChapter(i: number, dir: number) {
  const target = i + dir;
  if (target < 0 || target >= outlineChapters.value.length) return;
  const tmp = outlineChapters.value[i];
  outlineChapters.value[i] = outlineChapters.value[target];
  outlineChapters.value[target] = tmp;
  // 重新编号
  outlineChapters.value.forEach((ch: any, idx: number) => { ch.number = idx + 1; });
}
function addChapter() {
  const newNum = outlineChapters.value.length + 1;
  outlineChapters.value.push({
    number: newNum,
    title: `第${newNum}章`,
    summary: '',
    estimatedWords: 2000,
    writingInstructions: '',
    sections: [],
    subsections: [],
  });
}

// Step 3: 保存 Outline
async function doSaveOutline() {
  stepLoading.value = true;
  try {
    const outlineObj = { chapters: outlineChapters.value };
    await api.updateInteractiveOutline(bookId.value, outlineObj);
    currentStep.value = 3;
    doGenerateContent();
  } catch (e: any) {
    uni.showToast({ title: e.message || '保存失败', icon: 'none' });
  } finally {
    stepLoading.value = false;
  }
}

// Step 4: 生成内容
async function doGenerateContent() {
  genStarted.value = true;
  genProgress.value = 0;
  genError.value = '';
  try {
    await api.generateInteractiveContent(bookId.value);
    // 开始轮询进度
    startProgressPolling();
  } catch (e: any) {
    genError.value = e.message || '生成失败';
  }
}

function startProgressPolling() {
  progressTimer = setInterval(async () => {
    try {
      const response = await uni.request({
        url: `${BASE_URL}/book-generator/langgraph/books/${bookId.value}/progress`,
        method: 'GET',
      });
      const res = response.data as any;
      if (res.code === 0) {
        genProgress.value = Math.min(res.data.progress || 0, 100);
        if (genProgress.value >= 100) {
          genProgress.value = 100;
          stopProgressPolling();
        }
      }
    } catch (e) { /* ignore */ }
  }, 3000);
}

function stopProgressPolling() {
  if (progressTimer) {
    clearInterval(progressTimer);
    progressTimer = null;
  }
}

function goToDetail() {
  uni.redirectTo({ url: `/pages/book-generator/detail?id=${bookId.value}` });
}

onMounted(() => {
  fetchEstimate();
});
</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; height: 88rpx; padding: 0 32rpx; padding-top: env(safe-area-inset-top); }
.nav-left { width: 60rpx; flex-shrink: 0; }
.page-title { font-size: 32rpx; font-weight: 600; color: #1f2937; text-align: center; flex: 1; }
.back-icon { font-size: 40rpx; color: #1f2937; }

/* 模式切换独立行 */
.mode-bar { padding: 0 32rpx 16rpx 32rpx; }
.mode-switch { display: flex; align-items: center; justify-content: center; width: 100%; height: 72rpx; background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: #fff; border-radius: 12rpx; font-size: 26rpx; font-weight: 600; }

/* 覆盖 steps-bar 顶部间距 — nav-bar 现在更高了 */
.steps-bar { display: flex; align-items: center; justify-content: center; padding: 24rpx 32rpx; padding-top: calc(88rpx + 16rpx + 72rpx + 16rpx + env(safe-area-inset-top) + 16rpx); background: #fff; position: relative; gap: 60rpx; }
.step-dot { display: flex; flex-direction: column; align-items: center; gap: 8rpx; z-index: 1; }
.dot-num { width: 48rpx; height: 48rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 24rpx; font-weight: 600; background: #e5e7eb; color: #9ca3af; transition: all .3s; }
.step-dot.active .dot-num { background: #4f46e5; color: #fff; }
.step-dot.done .dot-num { background: #10b981; color: #fff; }
.dot-label { font-size: 20rpx; color: #9ca3af; white-space: nowrap; }
.step-dot.active .dot-label { color: #4f46e5; font-weight: 600; }
.step-line { position: absolute; top: calc(48rpx + 12rpx); left: calc(32rpx + 24rpx); height: 4rpx; background: #4f46e5; z-index: 0; transition: width .5s; }

.main-content { padding-top: 16rpx; padding-bottom: 48rpx; }

.card { margin: 24rpx; padding: 32rpx; background: #ffffff; border-radius: 16rpx; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.card-header { margin-bottom: 32rpx; display: flex; align-items: center; justify-content: space-between; }
.card-title { font-size: 36rpx; font-weight: 700; color: #1f2937; }
.loading-hint { font-size: 24rpx; color: #4f46e5; }

.form-item { margin-bottom: 24rpx; }
.form-label { font-size: 28rpx; font-weight: 600; color: #374151; margin-bottom: 12rpx; display: block; }
.form-hint-small { font-size: 24rpx; color: #9ca3af; margin-bottom: 16rpx; display: block; }
.label-with-badge { display: flex; align-items: center; gap: 12rpx; margin-bottom: 12rpx; }
.required-badge { padding: 4rpx 12rpx; background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; border-radius: 8rpx; font-size: 20rpx; font-weight: 500; display: inline-block; }
.chip-desc { font-size: 20rpx; color: inherit; }
.form-label-sm { font-size: 24rpx; font-weight: 600; color: #374151; margin-bottom: 8rpx; display: block; }

.form-input { width: 100%; height: 88rpx; padding: 0 24rpx; background: #f9fafb; border: 2rpx solid #e5e7eb; border-radius: 12rpx; font-size: 28rpx; }
.form-input-sm { width: 100%; height: 72rpx; padding: 0 16rpx; background: #f9fafb; border: 2rpx solid #e5e7eb; border-radius: 8rpx; font-size: 26rpx; }
.form-textarea { width: 100%; min-height: 160rpx; padding: 24rpx; background: #f9fafb; border: 2rpx solid #e5e7eb; border-radius: 12rpx; font-size: 28rpx; line-height: 1.6; }
.form-textarea-sm { width: 100%; min-height: 100rpx; padding: 16rpx; background: #f9fafb; border: 2rpx solid #e5e7eb; border-radius: 8rpx; font-size: 24rpx; line-height: 1.5; }

.chip-group { display: flex; flex-wrap: wrap; gap: 12rpx; }
.chip { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 20rpx; background: #f3f4f6; border-radius: 24rpx; font-size: 24rpx; color: #4b5563; transition: all .2s; }
.chip.active { background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: white; }
.chip-icon { font-size: 24rpx; }
.chip-text { font-size: 24rpx; }

.scale-picker { display: flex; flex-wrap: wrap; gap: 12rpx; }
.scale-chip { display: flex; flex-direction: column; align-items: center; gap: 4rpx; padding: 12rpx 16rpx; background: #f3f4f6; border-radius: 12rpx; min-width: 100rpx; transition: all .2s; }
.scale-chip.active { background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: white; }
.scale-label { font-size: 24rpx; font-weight: 600; }
.scale-desc { font-size: 20rpx; color: #9ca3af; }

.estimate-card { padding: 24rpx; background: rgba(79,70,229,.05); border-radius: 12rpx; margin-bottom: 24rpx; }
.estimate-row { display: flex; justify-content: space-between; padding: 8rpx 0; }
.estimate-label { font-size: 24rpx; color: #6b7280; }
.estimate-value { font-size: 24rpx; color: #4f46e5; font-weight: 600; }

.btn-group { display: flex; flex-direction: column; gap: 12rpx; margin-top: 32rpx; }
.btn-primary { width: 100%; height: 88rpx; background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: white; border-radius: 12rpx; font-size: 28rpx; font-weight: 600; border: none; display: flex; align-items: center; justify-content: center; white-space: nowrap; }
.btn-secondary { width: 100%; height: 88rpx; background: #f3f4f6; color: #4b5563; border-radius: 12rpx; font-size: 28rpx; font-weight: 600; border: none; display: flex; align-items: center; justify-content: center; white-space: nowrap; }
.btn-cancel { width: 100%; height: 88rpx; background: #f3f4f6; color: #6b7280; border-radius: 12rpx; font-size: 28rpx; border: none; display: flex; align-items: center; justify-content: center; white-space: nowrap; }
.btn-row-sm { display: flex; gap: 8rpx; margin-top: 12rpx; }
.btn-delete-sm { padding: 8rpx 16rpx; background: #fef2f2; color: #ef4444; border-radius: 8rpx; font-size: 22rpx; border: none; }
.btn-move-sm { padding: 8rpx 16rpx; background: #f3f4f6; color: #6b7280; border-radius: 8rpx; font-size: 22rpx; border: none; }
.btn-add-chapter { width: 100%; height: 72rpx; margin-top: 16rpx; background: #eef2ff; color: #4f46e5; border: 2rpx dashed #c7d2fe; border-radius: 12rpx; font-size: 26rpx; font-weight: 600; display: flex; align-items: center; justify-content: center; }
button[disabled] { opacity: .5; }

/* Plan Review */
.plan-review { margin-top: 16rpx; }
.plan-section { margin-bottom: 24rpx; padding: 20rpx; background: #f9fafb; border-radius: 12rpx; }
.plan-section-header { display: flex; align-items: center; gap: 8rpx; margin-bottom: 12rpx; }
.plan-section-icon { font-size: 28rpx; }
.plan-section-title { font-size: 26rpx; font-weight: 600; color: #374151; }
.plan-section-hint { font-size: 22rpx; color: #9ca3af; margin-left: auto; }
.plan-textarea { width: 100%; min-height: 80rpx; padding: 16rpx; background: #ffffff; border: 2rpx solid #e5e7eb; border-radius: 8rpx; font-size: 26rpx; line-height: 1.6; }
.plan-empty { padding: 24rpx; text-align: center; }
.empty-text { font-size: 24rpx; color: #9ca3af; }

/* Plan Summary Tags */
.plan-summary { display: flex; flex-wrap: wrap; gap: 12rpx; margin-bottom: 24rpx; }
.summary-tag { font-size: 22rpx; padding: 8rpx 16rpx; background: #eef2ff; color: #4338ca; border-radius: 8rpx; line-height: 1.5; }

/* Narrative Arc */
.narrative-parts { display: flex; flex-direction: column; gap: 16rpx; }
.narrative-part-card { background: #ffffff; border: 2rpx solid #e5e7eb; border-radius: 12rpx; overflow: hidden; }
.part-badge { padding: 10rpx 20rpx; background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: #fff; font-size: 24rpx; font-weight: 600; }
.part-body { padding: 20rpx; display: flex; flex-direction: column; gap: 16rpx; }
.part-field { display: flex; flex-direction: column; gap: 6rpx; }
.part-label { font-size: 22rpx; color: #6b7280; font-weight: 600; }
.part-text { font-size: 24rpx; color: #374151; }

/* Tone Fields */
.tone-fields { display: flex; flex-direction: column; gap: 16rpx; }
.tone-row { display: flex; flex-direction: column; gap: 8rpx; }
.tone-label { font-size: 24rpx; color: #6b7280; font-weight: 600; }

/* Audience Fields */
.audience-fields { display: flex; flex-direction: column; gap: 16rpx; }
.audience-row { display: flex; flex-direction: column; gap: 8rpx; }
.audience-label { font-size: 24rpx; color: #6b7280; font-weight: 600; }

/* Tag List (avoidPatterns etc) */
.tag-list { display: flex; flex-wrap: wrap; gap: 10rpx; }
.tag-item { display: flex; align-items: center; gap: 6rpx; padding: 8rpx 14rpx; background: #fee2e2; border-radius: 8rpx; font-size: 22rpx; }
.tag-item-static { padding: 8rpx 14rpx; background: #eef2ff; color: #4338ca; border-radius: 8rpx; font-size: 22rpx; }
.tag-text { color: #dc2626; }
.tag-remove { color: #ef4444; font-weight: 700; font-size: 24rpx; padding: 0 4rpx; }
.tag-add { padding: 8rpx 14rpx; background: #f3f4f6; color: #6b7280; border-radius: 8rpx; font-size: 22rpx; border: 2rpx dashed #d1d5db; }

/* Feedback Section */
.feedback-section { background: #fef9c3; }
.feedback-textarea { width: 100%; min-height: 120rpx; padding: 16rpx; background: #ffffff; border: 2rpx solid #fde68a; border-radius: 8rpx; font-size: 26rpx; line-height: 1.6; }
.feedback-hint { font-size: 22rpx; color: #a16207; margin-top: 10rpx; }

/* Form textarea xs (compact) */
.form-textarea-xs { width: 100%; min-height: 72rpx; padding: 12rpx 16rpx; background: #ffffff; border: 2rpx solid #e5e7eb; border-radius: 8rpx; font-size: 24rpx; line-height: 1.5; }

/* Outline Review */
.outline-review { margin-top: 16rpx; }
.outline-subtitle { font-size: 24rpx; color: #9ca3af; margin-bottom: 16rpx; display: block; }
.chapter-card { margin-bottom: 16rpx; background: #f9fafb; border-radius: 12rpx; overflow: hidden; }
.chapter-header { display: flex; align-items: center; justify-content: space-between; padding: 20rpx 24rpx; cursor: pointer; }
.chapter-num { font-size: 26rpx; font-weight: 600; color: #374151; }
.expand-icon { font-size: 24rpx; color: #9ca3af; }
.chapter-edit { padding: 0 24rpx 24rpx; }

/* Loading */
.loading-box { display: flex; flex-direction: column; align-items: center; padding: 64rpx 0; gap: 24rpx; }
.loading-spinner { width: 56rpx; height: 56rpx; border: 4rpx solid #e5e7eb; border-top-color: #4f46e5; border-radius: 50%; animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 26rpx; color: #6b7280; }

/* Error */
.error-box { padding: 24rpx; background: #fef2f2; border-radius: 12rpx; margin-top: 16rpx; }
.error-text { font-size: 24rpx; color: #ef4444; }

/* Step 4 Generate */
.generate-box { display: flex; flex-direction: column; gap: 32rpx; padding: 32rpx 0; }
.generate-stage { display: flex; align-items: center; gap: 16rpx; opacity: .4; }
.generate-stage.completed { opacity: 1; }
.stage-icon { font-size: 40rpx; }
.stage-info { display: flex; flex-direction: column; gap: 4rpx; }
.stage-title { font-size: 28rpx; font-weight: 600; color: #374151; }
.stage-hint { font-size: 22rpx; color: #9ca3af; }

.progress-bar-wrap { padding: 16rpx 0; }
.progress-bar { height: 12rpx; background: linear-gradient(90deg, #4f46e5, #6366f1); border-radius: 6rpx; transition: width .5s; }
.progress-text { font-size: 24rpx; color: #4f46e5; text-align: center; display: block; margin-top: 12rpx; }

/* AI智能推荐 */
.ai-recommend-row { display: flex; align-items: center; gap: 12rpx; margin-top: 16rpx; }
.btn-ai-recommend { padding: 14rpx 24rpx; background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); color: #ffffff; border-radius: 12rpx; font-size: 24rpx; font-weight: 600; border: none; white-space: nowrap; flex-shrink: 0; }
.btn-ai-recommend[disabled] { opacity: 0.5; }
.ai-recommend-hint { font-size: 22rpx; color: #9ca3af; flex: 1; }

/* AI自动生成提示 */
.ai-generated-notice { display: flex; align-items: flex-start; gap: 12rpx; padding: 20rpx 24rpx; background: linear-gradient(135deg, #eef2ff 0%, #f0fdf4 100%); border-radius: 12rpx; border: 2rpx solid #c7d2fe; margin-bottom: 20rpx; }
.notice-icon { font-size: 32rpx; flex-shrink: 0; margin-top: 2rpx; }
.notice-text { font-size: 26rpx; color: #4338ca; line-height: 1.5; }
</style>
