/**
 * 逐章处理节点（perChapterNode）
 * 每个章节作为一个独立单元：结构生成 + 内容生成一次完成
 * 章节之间可并行执行
 *
 * 适用于策略3（per-chapter）
 *
 * 工作方式：
 *   1. 先生成全书章大纲
 *   2. 然后为每章（并行）生成该章内部结构（节/小节）+ 所有内容
 */

import { GraphState } from '../graph';
import { bookStore } from '../book-generator.store';
import { callLLMWithMessages, ChatMessage } from '../../../services/llm';
import { callLLMWithRetry, executeNodeWithTimeout, FAULT_TOLERANCE_CONFIG } from '../fault-tolerance';
import { PROGRESS } from '../utils';
import { parseOutline } from '../parsers/outline.parser';
import { buildOutlineMessages } from '../prompts/builder';
import { prisma } from '../../../models';
import { withGlobalLLMConcurrency } from '../utils/llm-concurrency';

/** 最大并行数（可通过环境变量 LLM_BOOK_CONCURRENCY 配置） */
const MAX_CONCURRENCY = (() => {
  const env = process.env['LLM_BOOK_CONCURRENCY'];
  if (env) { const p = parseInt(env, 10); if (!isNaN(p) && p > 0) return p; }
  return 5;
})();

/**
 * 滚动摘要窗口大小：保留最近 N 章的完整摘要，更早的章压缩为一行概要。
 * 避免到第30章时提示词包含前29章全量标题，Token 爆炸。
 */
const ROLLING_WINDOW_SIZE = 3;

/**
 * 构建滚动摘要上下文
 *
 * 最近 ROLLING_WINDOW_SIZE 章：完整列出标题 + 摘要
 * 更早的章：压缩为一行概要（只列标题）
 *
 * 这样第30章时上下文只包含最近3章详情 + 前27章概览，大幅节省 Token。
 */
function buildRollingContext(
  previousChapters: Array<string | { number: number; title: string; summary?: string }>
): string {
  if (!previousChapters || previousChapters.length === 0) return '';

  // 规范化输入（兼容旧调用传 string[]）
  const normalized = previousChapters.map((ch, i) => {
    if (typeof ch === 'string') {
      return { number: i + 1, title: ch, summary: undefined as string | undefined };
    }
    return ch;
  });

  if (normalized.length <= ROLLING_WINDOW_SIZE) {
    // 还不够窗口大小，全部列出
    const lines = normalized.map(c =>
      `- 第${c.number}章: ${c.title}${c.summary ? `（${c.summary.substring(0, 80)}）` : ''}`
    );
    return `\n## 前面已完成的章节\n${lines.join('\n')}`;
  }

  // 拆分：远章（压缩）+ 近章（完整）
  const distant = normalized.slice(0, normalized.length - ROLLING_WINDOW_SIZE);
  const recent = normalized.slice(-ROLLING_WINDOW_SIZE);

  const parts: string[] = [];

  // 远章压缩为一行概览
  const distantTitles = distant.map(c => `第${c.number}章「${c.title}」`).join(' → ');
  parts.push(`\n## 前面已完成的章节（第1-${distant[distant.length - 1].number}章概要）`);
  parts.push(`${distantTitles}`);

  // 近章完整列出
  parts.push(`\n## 最近章节（保持连贯性）`);
  parts.push(recent.map(c =>
    `- 第${c.number}章: ${c.title}${c.summary ? `\n  摘要：${c.summary}` : ''}`
  ).join('\n'));

  return parts.join('\n');
}

/**
 * 构建单章生成提示词（包括节/小节结构 + 正文内容）
 *
 * @param previousChapters 前面章节列表，每条 { number, title, summary? }
 *                         如果传入 string[]（旧签名兼容），自动转为 {number, title} 格式
 */
function buildChapterPrompt(
  chapterTitle: string,
  chapterSummary: string,
  bookTitle: string,
  bookDescription: string,
  genLevel: number,
  previousChapters: Array<string | { number: number; title: string; summary?: string }>,
  bookPlan?: any
): ChatMessage[] {
  const levelInstruction = genLevel >= 3
    ? '请在正文中先列出节标题，每节下列出小节标题，然后为每小节写出完整正文。'
    : genLevel === 2
      ? '请在正文中先列出节标题，然后为每节写出完整正文。'
      : '直接写出本章完整正文，无需分节。';

  // 构建滚动摘要上下文
  const prevContext = buildRollingContext(previousChapters);

  const planContext = bookPlan
    ? `\n## 全书规划\n写作风格：${bookPlan.writingStyle || '未指定'}\n结构逻辑：${bookPlan.structureLogic || '未指定'}\n内容深度：${bookPlan.contentDepth || '未指定'}`
    : '';

  const systemPrompt = `你是一位专业的书籍作者。请为指定章节生成完整内容。

## 全书信息
- 书名：${bookTitle}
- 内容：${bookDescription}

## 当前章节
章标题：${chapterTitle}
章摘要：${chapterSummary}

## 写作要求
${levelInstruction}

## 结构要求
- genLevel >= 2：正文需包含 2-4 个节，每个节 500-1000 字
- genLevel >= 3：每节下包含 2-3 个小节，每个小节 300-500 字
- genLevel = 1：直接写 1500-2500 字的完整正文，不分节

## 格式要求
- 使用 Markdown 格式
- 节标题用 ###，小节标题用 ####
- 正文要详细、有深度，不能只是要点列表
- 字数尽量达到预估要求${prevContext}${planContext}

## 输出格式
必须返回纯 JSON，不要任何额外文字：
{
  "content": "本章完整正文（Markdown格式，包含标题层级）",
  "wordCount": 1500
}`;

  return [
    { role: 'system', content: systemPrompt },
    { role: 'user', content: `请为「${chapterTitle}」生成完整内容。` },
  ];
}

/**
 * 逐章处理节点
 * 先获全书章大纲，然后为每章独立生成内部结构+内容
 */
export async function perChapterNode(state: typeof GraphState.State): Promise<Partial<typeof GraphState.State>> {
  console.log('[PerChapter] 逐章处理, bookId:', state.bookId, 'genLevel:', state.genLevel);

  const bookIdNum = parseInt(state.bookId);

  try {
    const book = await bookStore.getById(state.bookId);
    if (!book || !book.outline || !book.outline.chapters || book.outline.chapters.length === 0) {
      console.error('[PerChapter] 无大纲');
      return { finished: true, error: '无大纲' };
    }

    const chapters = book.outline.chapters;
    const totalChapters = chapters.length;
    const completedChapters: number[] = [];
    const failedChapters: number[] = [];

    // 读取规划结果
    let bookPlan: any = null;
    if (state.bookPlan) {
      try { bookPlan = JSON.parse(state.bookPlan); } catch { /* ok */ }
    }

    // 逐章串行处理（避免并行竞争导致的写入丢失）
    for (const chapter of chapters) {
      const chapterIdx = chapter.number - 1;

      // 收集前几章的标题+摘要用于滚动上下文（含摘要以减少 Token 浪费）
      const prevTitles = chapters
        .slice(0, chapterIdx)
        .filter(c => completedChapters.includes(c.number))
        .map(c => ({ number: c.number, title: c.title, summary: c.summary }));

      try {
        console.log(`[PerChapter] 开始处理第${chapter.number}章「${chapter.title}」`);

        const messages = buildChapterPrompt(
          chapter.title,
          chapter.summary || '',
          book.title,
          book.description || '',
          state.genLevel,
          prevTitles,
          bookPlan
        );

        const response = await withGlobalLLMConcurrency(() =>
          callLLMWithRetry(messages, undefined, {
            bookId: state.bookId,
            nodeId: `chapter_${chapter.number}`,
            attempt: 0,
            maxAttempts: FAULT_TOLERANCE_CONFIG.aiRetry.maxRetries,
          })
        );

        console.log(`[PerChapter] 第${chapter.number}章 AI 响应长度: ${response.length}`);

        // 解析返回的 JSON 内容
        const parsed = parseChapterContent(response);
        if (!parsed || !parsed.content) {
          throw new Error(`章节内容解析失败，response=${response.substring(0, 200)}`);
        }

        console.log(`[PerChapter] 第${chapter.number}章内容解析成功，字数: ${parsed.wordCount || parsed.content.length}`);

        // 如果 genLevel >= 2，需要先生成章节内部结构（level=2/3的节点）
        if (state.genLevel >= 2) {
          const chapterRecord = await prisma.bookChapter.findFirst({
            where: { bookId: bookIdNum, number: chapter.number, level: 1, parentId: 0 },
          });

          if (chapterRecord) {
            console.log(`[PerChapter] 第${chapter.number}章找到记录 id=${chapterRecord.id}，创建内部结构`);
            await createInternalStructure(bookIdNum, chapterRecord.id, parsed.content, state.genLevel, chapter.title);
          } else {
            console.warn(`[PerChapter] 第${chapter.number}章未找到数据库记录`);
          }
        }

        // 保存内容到章节（内容始终存到 level=1 的章记录）
        const targetChapter = await prisma.bookChapter.findFirst({
          where: { bookId: bookIdNum, number: chapter.number, level: 1, parentId: 0 },
        });

        if (targetChapter) {
          const contentLen = parsed.content.length;
          await bookStore.updateChapterById(targetChapter.id, {
            content: parsed.content,
            wordCount: parsed.wordCount || contentLen,
            genStage: 'content_completed',
          });

          // 验证内容是否真的写入了数据库
          const verify = await prisma.bookChapter.findUnique({
            where: { id: targetChapter.id },
            select: { id: true, content: true, genStage: true },
          });
          const savedLen = (verify?.content || '').length;
          if (savedLen > 0) {
            console.log(`[PerChapter] 第${chapter.number}章内容保存验证 ✅: 写入${contentLen}字, 读取${savedLen}字`);
          } else {
            console.warn(`[PerChapter] 第${chapter.number}章内容保存验证 ⚠️: 写入${contentLen}字但读取为空，重试...`);
            // 重试一次
            await bookStore.updateChapterById(targetChapter.id, { content: parsed.content });
            const retryVerify = await prisma.bookChapter.findUnique({
              where: { id: targetChapter.id }, select: { content: true },
            });
            console.log(`[PerChapter] 第${chapter.number}章重试后: ${(retryVerify?.content || '').length}字`);
          }
        } else {
          console.error(`[PerChapter] 第${chapter.number}章未找到 target 记录`);
        }

        completedChapters.push(chapter.number);
        console.log(`[PerChapter] 第${chapter.number}章「${chapter.title}」完成`);

      } catch (err) {
        console.error(`[PerChapter] 第${chapter.number}章失败:`, err);
        failedChapters.push(chapter.number);
      }
    }

    if (failedChapters.length === 0) {
      await bookStore.update(state.bookId, { progress: PROGRESS.CONTENT_END });
      console.log(`[PerChapter] 全部完成，共${completedChapters.length}章`);
    } else {
      await bookStore.update(state.bookId, {
        genStage: 'failed',
        failedStage: 'content_generating',
      });
      console.warn(`[PerChapter] ${completedChapters.length}章成功，${failedChapters.length}章失败`);
    }

    return { progress: PROGRESS.CONTENT_END, finished: failedChapters.length === 0 };
  } catch (error) {
    console.error('[PerChapter] 处理失败:', error);
    await bookStore.update(state.bookId, {
      genStage: 'failed',
      failedStage: 'content_generating',
      errorMsg: error instanceof Error ? error.message : '逐章处理失败',
    });
    return { error: error instanceof Error ? error.message : '失败', finished: true };
  }
}

/**
 * 解析单章 AI 返回
 */
function parseChapterContent(response: string): { content: string; wordCount: number } | null {
  try {
    let cleaned = response.trim();
    const thinkEnd = cleaned.indexOf('</think>');
    if (thinkEnd !== -1) cleaned = cleaned.substring(thinkEnd + 8).trim();
    const match = cleaned.match(/\{[\s\S]*\}/);
    if (!match) {
      // 如果不是 JSON，可能是纯文本，直接当内容
      return { content: cleaned, wordCount: cleaned.length };
    }
    const data = JSON.parse(match[0]);
    return {
      content: data.content || cleaned,
      wordCount: data.wordCount || 0,
    };
  } catch {
    // fallback: 把整个响应当内容
    return { content: response, wordCount: response.length };
  }
}

/**
 * 根据正文中的标题层级创建节/小节记录（仅索引，不存储内容）
 */
async function createInternalStructure(
  bookIdNum: number,
  chapterId: number,
  content: string,
  genLevel: number,
  chapterTitle: string
): Promise<void> {
  // 用正则提取 ### 和 #### 标题
  const sectionRegex = /###\s+(\d+\.?\d*)\s+(.+)/g;
  const subsectionRegex = /####\s+(\d+\.?\d*)\s+(.+)/g;

  let match: RegExpExecArray | null;
  let sectionNumber = 0;

  while ((match = sectionRegex.exec(content)) !== null) {
    sectionNumber++;
    const sectionTitle = match[2].trim();

    if (genLevel >= 3) {
      // 创建节记录，再提取该节下的小节
      const sectionId = await bookStore.createChapterItem(bookIdNum, {
        number: sectionNumber,
        title: sectionTitle,
        summary: '',
        keyPoints: [],
        estimatedWords: 500,
      }, chapterId, 2);

      // 在该节范围内找 #### 标题
      const sectionStart = match.index;
      const nextSectionMatch = sectionRegex.exec(content);
      const sectionEnd = nextSectionMatch ? nextSectionMatch.index : content.length;
      // 重置 regex 位置
      sectionRegex.lastIndex = match.index + match[0].length;

      const sectionContent = content.substring(sectionStart, sectionEnd);
      let subMatch: RegExpExecArray | null;
      let subNumber = 0;
      const subRegex = /####\s+(\d+\.?\d*)\s+(.+)/g;

      while ((subMatch = subRegex.exec(sectionContent)) !== null) {
        subNumber++;
        await bookStore.createChapterItem(bookIdNum, {
          number: subNumber,
          title: subMatch[2].trim(),
          summary: '',
          keyPoints: [],
          estimatedWords: 300,
        }, sectionId, 3);
      }
    } else {
      // genLevel=2，直接创建节，不拆小节
      await bookStore.createChapterItem(bookIdNum, {
        number: sectionNumber,
        title: sectionTitle,
        summary: '',
        keyPoints: [],
        estimatedWords: 800,
      }, chapterId, 2);
    }
  }
}
