/**
 * 光湖视频AI系统 · HLDP→Seedance提示词转换器
 * D130 · 铸渊 ICE-GL-ZY001
 *
 * 核心原则（来自冰朔思维模型 MP-004）:
 *   "先有理解，再有能力"
 *   铸渊理解的台词 → 画面 ≠ 通用AI机械翻译的台词 → 画面
 *   差异在于: 铸渊理解了这场戏的"为什么"——角色情绪来源、场景氛围基调、叙事意图
 *
 * 这不是一个格式转换器。这是铸渊的大脑在理解剧本。
 */

/**
 * 为单个场景生成 Seedance 视频提示词列表
 *
 * 铸渊读一场戏，不只是读「谁说了什么话」。
 * 铸渊理解的是：
 *   - 这场戏在整集里的位置（开场/冲突/转折/收尾）
 *   - 每个角色的情绪从哪来、往哪去
 *   - 这场戏最需要被观众"看到"的是什么
 *
 * @param {object} scene - script-parser 输出的结构化场景
 * @param {object} context - 上下文信息
 * @param {object} context.episodeInfo - 剧集信息 { number, title }
 * @param {object} context.characters - 全局角色信息 { name, description, firstAppearance }
 * @param {number} context.sceneIndex - 当前场次在本集的位置（0-based）
 * @param {number} context.totalScenes - 本集总场次
 * @returns {object} { shots: [{ shotId, prompt, thinking, duration }], mood, visualNotes }
 */
function sceneToPrompts(scene, context) {
  const { episodeInfo, characters, sceneIndex, totalScenes } = context;

  // === 铸渊: 先理解这场戏 ===
  const reading = understandScene(scene, context);

  // === 铸渊: 基于理解，逐镜写提示词 ===
  const shots = [];

  // 镜1: 建立镜头 — 场景全景
  shots.push({
    shotId: `${scene.id}-ESTABLISHING`,
    type: 'establishing',
    thinking: reading.establishingThink,
    prompt: reading.establishingPrompt,
    duration: '5',
  });

  // 镜2-N: 动作/对话镜头
  for (let i = 0; i < scene.descriptions.length; i++) {
    const desc = scene.descriptions[i];
    const thinkKey = `desc${i}Think`;
    const promptKey = `desc${i}Prompt`;

    if (reading[thinkKey] && reading[promptKey]) {
      shots.push({
        shotId: `${scene.id}-ACTION-${i + 1}`,
        type: 'action',
        thinking: reading[thinkKey],
        prompt: reading[promptKey],
        duration: '5',
      });
    }
  }

  // 镜: 关键情绪镜头（每个有台词的时刻）
  const keyDialogues = scene.dialogues.filter(d =>
    d.type === 'dialogue' &&
    (d.emotion.includes('愤怒') || d.emotion.includes('震惊') ||
     d.emotion.includes('泪') || d.emotion.includes('笑') ||
     d.emotion.includes('坚定') || d.emotion.includes('绝望') ||
     sceneIndex === 0 || sceneIndex === totalScenes - 1)
  );

  for (const d of keyDialogues.slice(0, 3)) { // 最多3个情绪镜头
    const emotionShot = reading.emotionShots?.find(e => e.dialogueLine === d.line);
    if (emotionShot) {
      shots.push({
        shotId: `${scene.id}-EMOTION-${d.character}`,
        type: 'emotion',
        thinking: emotionShot.thinking,
        prompt: emotionShot.prompt,
        duration: '5',
      });
    }
  }

  // 镜: 转场/特效镜头
  for (const e of scene.effects) {
    if (e.type === 'closeup') {
      shots.push({
        shotId: `${scene.id}-CLOSEUP`,
        type: 'closeup',
        thinking: `特写镜头: ${e.description}`,
        prompt: buildCloseupPrompt(e.description, reading.mood),
        duration: '5',
      });
    }
  }

  return {
    sceneId: scene.id,
    sceneName: scene.name,
    episodeInfo,
    mood: reading.mood,
    zhuYuanReading: reading.coreUnderstanding,
    visualNotes: reading.visualNotes,
    characterStates: reading.characterStates,
    shots,
  };
}

/**
 * 铸渊理解一场戏 — 这是「台词→提示词」的核心，
 * 不是格式转换，是铸渊的脑子在运转
 */
function understandScene(scene, context) {
  const { episodeInfo, sceneIndex, totalScenes } = context;
  const isOpeningScene = sceneIndex === 0;
  const isClosingScene = sceneIndex === totalScenes - 1;

  // 提取本场所有角色的情绪
  const characterEmotions = {};
  for (const d of scene.dialogues) {
    if (!characterEmotions[d.character]) {
      characterEmotions[d.character] = [];
    }
    characterEmotions[d.character].push({
      emotion: d.emotion,
      line: d.line,
      type: d.type,
    });
  }

  // 判断场景基调
  let mood = 'neutral';
  const allEmotions = Object.values(characterEmotions).flat().map(e => e.emotion);
  if (allEmotions.some(e => e.includes('愤怒') || e.includes('恨') || e.includes('杀'))) {
    mood = 'dark_tension';
  } else if (allEmotions.some(e => e.includes('泪') || e.includes('悲') || e.includes('绝望'))) {
    mood = 'sad';
  } else if (allEmotions.some(e => e.includes('笑') || e.includes('高兴') || e.includes('得意'))) {
    mood = 'bright';
  } else if (isOpeningScene) {
    mood = 'grand_opening';
  } else if (allEmotions.some(e => e.includes('讽刺') || e.includes('质疑'))) {
    mood = 'tense_conflict';
  }

  // 场景视觉基调
  const visualMoodMap = {
    grand_opening: '宏大的修仙世界场景，云海仙气，金色光芒，大气磅礴',
    dark_tension: '阴沉压抑的氛围，低饱和度色彩，冷色调，暗影重重',
    sad: '柔和的光线，略带灰调，安静而忧伤',
    bright: '明亮温暖的色调，柔和自然光，清新画面',
    tense_conflict: '强烈对比的光影，锐利的光线，紧张的氛围',
    neutral: '自然光线，标准修仙世界场景风格',
  };

  const visualMood = visualMoodMap[mood] || visualMoodMap.neutral;

  // === 建立镜头思考 ===
  const locationDesc = scene.name || '修仙世界场景';
  const timeStr = scene.time === '日' ? '白天，阳光明媚' :
                  scene.time === '夜' ? '夜晚，月光或灯火' :
                  scene.time === '晨' ? '清晨，晨雾和朝霞' :
                  scene.time === '暮' ? '黄昏，金色余晖' : '';

  const establishingThink = `这是第${episodeInfo.number}集的${isOpeningScene ? '开场' : isClosingScene ? '收尾' : `第${sceneIndex + 1}`}场戏。` +
    `场景: ${locationDesc}。${timeStr}。` +
    `出场角色: ${scene.characters.map(c => c.name).join('、')}。` +
    `本场基调: ${mood}。` +
    `${isOpeningScene ? '需要用建立镜头确立世界观和本集开场氛围。' : ''}`;

  const establishingPrompt = `修仙世界，${locationDesc}，${timeStr}，${visualMood}，` +
    (scene.descriptions[0] || `远景全景，场景宏大`).substring(0, 200);

  // === 动作镜头思考 ===
  const reading = {
    coreUnderstanding: '',
    establishingThink,
    establishingPrompt,
    mood,
    visualMood,
    visualNotes: [],
    characterStates: {},
    emotionShots: [],
  };

  // 为每个场景描述生成思考+提示词
  for (let i = 0; i < scene.descriptions.length; i++) {
    const desc = scene.descriptions[i];
    reading[`desc${i}Think`] = `动作描述: ${desc}。需要展现此动作的完整过程和氛围。`;
    reading[`desc${i}Prompt`] = `修仙世界风格，${desc}，${visualMood}，Seedance动态漫风格，电影级光影`;
  }

  // 为关键台词生成情绪镜头
  for (const d of scene.dialogues.filter(d => d.type === 'dialogue')) {
    const charInfo = scene.characters.find(c => c.name === d.character);
    const charDesc = charInfo?.description || d.character;

    const emotionThink = `${d.character}（${charDesc}）此时的心情：${d.emotion}。` +
      `TA说的是"${d.line}"。这不是一句台词——这是TA在这个瞬间的真实情绪。`;

    const emotionPrompt = `修仙世界，${charDesc}，${d.emotion}，${d.character}，${visualMood}，` +
      `半身镜头，表情特写，Seedance动态漫风格`;

    reading.emotionShots.push({
      character: d.character,
      dialogueLine: d.line,
      emotion: d.emotion,
      thinking: emotionThink,
      prompt: emotionPrompt.substring(0, 500),
    });
  }

  // 核心理解——铸渊读完这场戏后的整体感受
  const firstDialogue = scene.dialogues[0];
  const lastDialogue = scene.dialogues[scene.dialogues.length - 1];
  const mainCharacter = scene.characters[0];

  reading.coreUnderstanding =
    `铸渊读完第${episodeInfo.number}集第${scene.id}场。` +
    `${mainCharacter ? `主角${mainCharacter.name}（${mainCharacter.description || ''}），` : ''}` +
    `从「${firstDialogue?.line?.substring(0, 30) || '开场'}」到「${lastDialogue?.line?.substring(0, 30) || '收尾'}」` +
    `——这场戏的情绪弧线是: 从${firstDialogue?.emotion || '开始'}到${lastDialogue?.emotion || '结束'}。` +
    `视觉核心: ${visualMood}。`;

  // 角色状态
  for (const c of scene.characters) {
    const emotions = characterEmotions[c.name] || [];
    reading.characterStates[c.name] = {
      description: c.description,
      emotions: emotions.map(e => e.emotion),
      keyLine: emotions[0]?.line || '',
    };
  }

  return reading;
}

/**
 * 特写镜头提示词生成
 */
function buildCloseupPrompt(description, mood) {
  // 从特写描述中提取关键视觉元素
  const objMatch = description.match(/【(.+?)】/);
  const subject = objMatch ? objMatch[1] : description;

  return `修仙世界，${subject}，特写镜头，${mood}，电影级细节，Seedance动态漫风格`;
}

/**
 * 为整集生成完整的分镜脚本（供铸渊审阅）
 */
function episodeToStoryboard(episode, characters) {
  const lines = [];
  lines.push(`# 第${episode.episodeNumber}集 分镜脚本`);
  lines.push('');
  lines.push(`## 本集概要`);
  lines.push(`- 场次: ${episode.scenes.length}场`);
  lines.push(`- 角色: ${extractEpisodeChars(episode).join('、')}`);
  lines.push('');

  for (let i = 0; i < episode.scenes.length; i++) {
    const scene = episode.scenes[i];
    const ctx = {
      episodeInfo: { number: episode.episodeNumber, title: episode.title },
      characters,
      sceneIndex: i,
      totalScenes: episode.scenes.length,
    };
    const result = sceneToPrompts(scene, ctx);

    lines.push(`## 第${scene.id}场 · ${scene.name}`);
    lines.push('');
    lines.push(`**氛围**: ${result.mood}`);
    lines.push(`**铸渊理解**: ${result.zhuYuanReading}`);
    lines.push('');
    lines.push(`| 镜号 | 类型 | 提示词 | 时长 |`);
    lines.push(`|------|------|--------|------|`);
    for (const shot of result.shots) {
      lines.push(`| ${shot.shotId} | ${shot.type} | ${shot.prompt.substring(0, 120)}... | ${shot.duration}s |`);
    }
    lines.push('');
  }

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

/**
 * 提取本集出场角色
 */
function extractEpisodeChars(episode) {
  const chars = new Set();
  for (const sc of episode.scenes) {
    for (const c of sc.characters) {
      chars.add(c.name);
    }
  }
  return Array.from(chars);
}

// ==================== HLDP编号协议 · 展开引擎 ====================
// D132 · 铸渊 ICE-GL-ZY001 · 冰朔见证
//
// 触发: D131秦山号翻车 → 冰朔提出"沙子本体+衣服层+外面脑子"三层架构
// 涌现: 编号展开引擎 = 衣服层。chars.hdlp = 布料。hldp-prompt.js = 缝纫机。
//       铸渊在外面缝好衣服(展开CHAR+ENV+动作) → 塞进Seedance(沙子) → 视频出来
//       同一件衣服(同一CHAR编号展开) = 同一个人物视觉。不在"A衣服"和"B衣服"之间漂移。
// lock: ⊢ 每镜提示词 = expandPrompt(CHAR-ID, ENV-ID, 动作, 风格)
//       ⊢ 展开后校验: 数据库原文必须在展开结果中，否则强制切回
//       ⊢ 不是"可能一致"，是"不可能不一致"
// why: 秦山号镜1林昊≠镜8林昊 → 根因是每镜独立手写人物描述，没有统一的编号展开。
//      这个引擎就是冰朔说的那件衣服——穿上了，10个镜头的苏白都是同一个苏白。

const fs = require('fs');
const path = require('path');

// 运行时数据库
const DB = { characters: {}, environments: {} };

/**
 * 解析HLDP格式数据库，提取CHAR/ENV编号对应的完整提示词片段。
 *
 * D132修复: 原D131版本用split()导致CHAR编号在分割时被丢弃，解析始终失败。
 * 改为matchAll直接捕获编号+提示词片段，一步完成。
 *
 * @why 这是秦山号翻车的直接技术原因。引擎写了但数据库解析失败 → 每次expand返回空 → 退化为手写。
 */
function loadDatabase() {
  const dataDir = path.resolve(__dirname, '../data');

  // --- 人物库 ---
  try {
    const charContent = fs.readFileSync(path.join(dataDir, 'characters.hdlp'), 'utf-8');
    const charRegex = /## (CHAR-\d+)[\s\S]*?提示词片段:\s*([^\n]+)/g;
    let match;
    while ((match = charRegex.exec(charContent)) !== null) {
      DB.characters[match[1]] = match[2].trim();
    }
  } catch (e) { /* 数据库文件不存在 */ }

  // --- 环境库 ---
  try {
    const envContent = fs.readFileSync(path.join(dataDir, 'environments.hdlp'), 'utf-8');
    const envRegex = /## (ENV-\d+)[\s\S]*?提示词片段:\s*([^\n]+)/g;
    let match;
    while ((match = envRegex.exec(envContent)) !== null) {
      DB.environments[match[1]] = match[2].trim();
    }
  } catch (e) { /* 数据库文件不存在 */ }
}

loadDatabase();

/**
 * 展开编号模板 → 完整Seedance提示词。
 *
 * 用法: expandPrompt('CHAR-003', 'ENV-002', '对比镜头，苏白叹气，风吹落叶', '真人写实')
 * 返回: "18岁亚洲少年，五官俊秀…白色长衫…修仙世界开阔广场，云海环绕…对比镜头…真人写实风格，电影级光影"
 *
 * @param {string} charId  - 人物编号, 如 'CHAR-003'
 * @param {string} envId   - 环境编号, 如 'ENV-002'
 * @param {string} action  - 本镜动作描述
 * @param {string} style   - 风格后缀, '真人写实' 或 '动态漫'（默认真人写实）
 * @returns {object} { prompt: 完整提示词, warnings: [] }
 */
function expandPrompt(charId, envId, action, style) {
  const warnings = [];
  const charDesc = DB.characters[charId];
  const envDesc = DB.environments[envId];
  const styleSuffix = (style || '真人写实') === '真人写实'
    ? '真人写实风格，电影级光影，亚洲面孔'
    : 'Seedance动态漫风格，电影级光影';

  if (!charDesc) warnings.push(`未知人物编号: ${charId}`);
  if (!envDesc)  warnings.push(`未知环境编号: ${envId}`);

  const prompt = [
    charDesc || `[${charId}]`,
    envDesc ? `，${envDesc}` : '',
    action ? `，${action}` : '',
    `，${styleSuffix}`,
  ].join('');

  return { prompt, warnings };
}

/**
 * 展开 [CHAR-NNN] 和 [ENV-NNN] 标记为完整提示词片段
 */
function expand(text) {
  const warnings = [];
  let result = text;
  result = result.replace(/\[CHAR-(\d+)\]/g, (m, num) => {
    const id = `CHAR-${num}`;
    return DB.characters[id] || (warnings.push(`未知: ${id}`), m);
  });
  result = result.replace(/\[ENV-(\d+)\]/g, (m, num) => {
    const id = `ENV-${num}`;
    return DB.environments[id] || (warnings.push(`未知: ${id}`), m);
  });
  return { expanded: result, warnings };
}

function validate(original, expanded) {
  const diffs = [];
  for (const ref of [...original.matchAll(/\[CHAR-(\d+)\]/g)]) {
    const id = `CHAR-${ref[1]}`;
    if (DB.characters[id] && !expanded.includes(DB.characters[id]))
      diffs.push({ id, issue: '展开结果中缺失数据库原文' });
  }
  for (const ref of [...original.matchAll(/\[ENV-(\d+)\]/g)]) {
    const id = `ENV-${ref[1]}`;
    if (DB.environments[id] && !expanded.includes(DB.environments[id]))
      diffs.push({ id, issue: '展开结果中缺失数据库原文' });
  }
  return { valid: diffs.length === 0, diffs };
}

function expandAndValidate(text) {
  const { expanded, warnings } = expand(text);
  let final = expanded;
  for (const ref of [...text.matchAll(/\[(CHAR|ENV)-(\d+)\]/g)]) {
    const id = `${ref[1]}-${ref[2]}`;
    const dbText = ref[1] === 'CHAR' ? DB.characters[id] : DB.environments[id];
    if (dbText && !final.includes(dbText)) {
      final += ' ' + dbText;
      warnings.push(`⚠️ ${id}强制切回数据库原文`);
    }
  }
  return final;
}

function getDBStatus() {
  return {
    characters: Object.keys(DB.characters).length,
    environments: Object.keys(DB.environments).length,
    loadedChars: DB.characters,
    loadedEnvs: DB.environments,
  };
}

// ==================== 导出 ====================

module.exports = {
  sceneToPrompts,
  understandScene,
  episodeToStoryboard,
  expandPrompt,
  expand,
  validate,
  expandAndValidate,
  getDBStatus,
};
