/**
 * 书籍规划节点（planBookNode）
 * 在生成任何内容之前，由 AI 像职业作家一样对整本书做详细规划：
 *   - 分析书籍类型、目标读者、内容深度
 *   - 决定大纲层级（1/2/3层）
 *   - 规划写作风格和结构逻辑
 *   - 输出完整的写作方案
 *
 * 该节点的输出（planResult）会被存储到 GraphState，供后续节点参考。
 */

import { GraphState } from '../graph';
import { bookStore } from '../book-generator.store';
import { callLLMWithMessages, ChatMessage } from '../../../services/llm';
import { PROGRESS } from '../utils';
import { getScaleConfig } from '../book-type-config';

/**
 * 书籍规划输出接口
 */
export interface BookPlan {
  /** 最终确定的大纲层级 */
  genLevel: number;
  /** 书籍类型分析 */
  bookTypeAnalysis: string;
  /** 推荐的写作风格 */
  writingStyle: string;
  /** 结构逻辑说明 */
  structureLogic: string;
  /** 内容深度评估 */
  contentDepth: string;
  /** 目标读者分析 */
  targetAudienceAnalysis: string;
  /** 规划理由 */
  reasoning: string;
}

/**
 * 从描述中提取明确的字数要求
 * 如 "生成200字文本" → 200
 */
function extractWordCountFromDescription(description: string): number | null {
  const text = description.toLowerCase();
  // 匹配 "200字"、"500字" 等
  const match = text.match(/(\d+)\s*字/i);
  if (match) {
    const n = parseInt(match[1]);
    return isNaN(n) ? null : n;
  }
  return null;
}

/**
 * 构建规划提示词
 */
function buildPlanPrompt(
  title: string,
  description: string,
  bookScale: string
): string {
  const config = getScaleConfig(bookScale);
  const chapters = config?.chapters || 17;

  // 如果 description 里有明确字数要求，优先用它
  const descWordCount = extractWordCountFromDescription(description);
  const totalWords = descWordCount !== null
    ? descWordCount
    : (config?.totalWords || 130000);

  return `你是一位经验丰富的图书策划编辑。请对以下书籍做全面规划。

## 书籍信息
- 书名：${title}
- 字数规模：约${totalWords}字，约${chapters}章
- 描述：${description}

## 你的职责
像职业作家出书前一样，先对整本书做完整的规划分析。

## 分析维度

### 1. 内容类型分析
判断这本书属于以下哪种类型：
- 教材/学术类：系统教学、理论知识体系完整
- 技术教程类：有操作步骤、代码、实践指南
- 小说/文学类：叙事性、故事性内容
- 商业/经管类：管理、营销、投资、创业
- 科普/大众类：普及科学知识，通俗易懂
- 其他类型

### 2. 大纲层级决策
根据内容类型和字数规模，决定使用几层大纲：
- 1层（仅章）：适合小说、短文、浅层内容。每章直接展开写，无需拆分。
- 2层（章→节）：适合商业、科普、一般教程。每章拆 2-4 节，节内直接写内容。
- 3层（章→节→小节）：适合教材、技术书、系统教学。每章拆节，节下再拆小节。

### 3. 写作方案
- 目标读者是谁？他们需要什么？
- 用什么写作风格？（通俗易懂/专业严谨/轻松幽默/故事化）
- 内容如何组织递进？（由浅入深、由理论到实践、问题驱动等）

## 输出格式

你必须严格按照以下 JSON 格式返回，不要包含任何其他文字：

{
  "genLevel": 1,
  "bookType": "教材|技术教程|小说|商业|科普",
  "bookTypeAnalysis": "对书籍类型的详细分析，2-3句话",
  "writingStyle": "推荐的写作风格",
  "structureLogic": "内容组织逻辑，如'由浅入深'、'由理论到实践'",
  "contentDepth": "内容深度评估（入门/基础/进阶/高级/专家）",
  "targetAudienceAnalysis": "目标读者分析，1-2句话",
  "reasoning": "为什么选择这个大纲层级？1-2句话"
}`;
}

/**
 * 解析 AI 返回的规划 JSON
 */
function parsePlanResponse(response: string): BookPlan | null {
  try {
    let cleaned = response.trim();

    // 移除 think 标签
    const thinkEnd = cleaned.indexOf('</think>');
    if (thinkEnd !== -1) {
      cleaned = cleaned.substring(thinkEnd + 8).trim();
    }

    // 提取 JSON
    const match = cleaned.match(/\{[\s\S]*\}/);
    if (!match) {
      console.error('[PlanNode] 未找到 JSON');
      return null;
    }

    const data = JSON.parse(match[0]);

    // 验证必要字段
    if (typeof data.genLevel !== 'number' || ![1, 2, 3].includes(data.genLevel)) {
      console.error('[PlanNode] genLevel 无效:', data.genLevel);
      return null;
    }

    return {
      genLevel: data.genLevel,
      bookTypeAnalysis: data.bookTypeAnalysis || '',
      writingStyle: data.writingStyle || '',
      structureLogic: data.structureLogic || '',
      contentDepth: data.contentDepth || '',
      targetAudienceAnalysis: data.targetAudienceAnalysis || '',
      reasoning: data.reasoning || '',
    };
  } catch (err) {
    console.error('[PlanNode] 解析失败:', err);
    return null;
  }
}

/**
 * 书籍规划节点
 * 作为 LangGraph 的第一个节点运行
 */
export async function planBookNode(state: typeof GraphState.State): Promise<Partial<typeof GraphState.State>> {
  console.log('[PlanNode] 开始规划书籍, bookId:', state.bookId);

  // 用户明确指定了 genLevel → AI 规划可以覆盖风格等，但层级必须保留
  const userDefinedLevel = state.userSpecifiedGenLevel !== undefined;

  try {
    const book = await bookStore.getById(state.bookId);
    const title = book?.title || state.topic;
    const description = book?.description || '';
    const bookScale = state.bookScale || '1000';

    const prompt = buildPlanPrompt(title, description, bookScale);

    const messages: ChatMessage[] = [
      { role: 'system', content: '你是一位资深的图书策划编辑。请严格按照要求的 JSON 格式输出规划结果，不要添加任何额外文字。' },
      { role: 'user', content: prompt },
    ];

    const response = await callLLMWithMessages(messages);
    const plan = parsePlanResponse(response);

    if (!plan) {
      console.warn('[PlanNode] AI 规划解析失败，使用默认值');
      return { progress: PROGRESS.OUTLINE_DONE };
    }

    console.log(`[PlanNode] 规划完成: genLevel=${plan.genLevel}, 类型=${(response.match(/"bookType"\s*:\s*"([^"]+)"/) || [])[1] || '未知'}, 风格=${plan.writingStyle}`);

    // 用户明确选了层级（含1/2/3）→ 保留用户选择；否则 → AI 决定
    const finalGenLevel = userDefinedLevel
      ? (state.userSpecifiedGenLevel ?? state.genLevel) // 用户主动选择，保留
      : plan.genLevel;                                    // 自动模式，AI决定

    console.log(`[PlanNode] genLevel: ${state.genLevel} → ${finalGenLevel} (userSpecified=${state.userSpecifiedGenLevel}), 风格=${plan.writingStyle}, 结构=${plan.structureLogic}`);

    // 持久化到数据库 + 通过 GraphState 传递给后续节点
    await bookStore.update(state.bookId, {
      bookAnalysis: JSON.stringify(plan),
    });

    return {
      progress: PROGRESS.OUTLINE_DONE,
      genLevel: finalGenLevel,
      bookPlan: JSON.stringify(plan),
    };
  } catch (error) {
    console.error('[PlanNode] 规划失败:', error);
    // 不阻塞流程，继续使用默认 genLevel
    await bookStore.update(state.bookId, {
      progress: PROGRESS.OUTLINE_DONE,
    });
    return { progress: PROGRESS.OUTLINE_DONE };
  }
}
