/**
 * 音频打轴文本匹配工具
 * =====================
 *
 * 将火山引擎打轴返回的 utterances 按 segment voiceover 进行分组匹配，
 * 计算每个 segment 在完整配音音频中的时间范围。
 */

interface SegmentAudioRange {
  segmentId: number;
  startTime: number; // 毫秒
  endTime: number;   // 毫秒
  actualDuration: number; // 秒
  matchedText: string;
  matchedUtteranceCount: number;
}

interface Utterance {
  text: string;
  startTime: number; // 毫秒
  endTime: number;   // 毫秒
}

// 中文数字映射（只处理单个数字，不处理组合如"十五"）
const CN_NUMBERS: Record<string, string> = {
  '零': '0', '〇': '0', '一': '1', '二': '2', '两': '2', '三': '3',
  '四': '4', '五': '5', '六': '6', '七': '7', '八': '8', '九': '9',
};

// 语气词列表
const PARTICLES = new Set([
  '啊', '呢', '吧', '嗯', '哦', '哈', '啦', '嘛', '呗', '咧', '哟',
]);

/**
 * 文本归一化：去标点、去空格、中文数字转阿拉伯数字、去语气词
 */
function normalizeText(text: string): string {
  return text
    // 去标点（保留中文、英文、数字）
    .replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '')
    // 中文数字转阿拉伯数字
    .replace(/[零〇一二两三四五六七八九十]/g, (match) => CN_NUMBERS[match] || match)
    // 去语气词
    .split('')
    .filter((c) => !PARTICLES.has(c))
    .join('')
    .toLowerCase();
}

/**
 * 将 segment voiceover 列表与打轴 utterances 进行顺序匹配
 *
 * @param segments 分镜列表，每个含 id 和 voiceover
 * @param utterances 打轴返回的字幕时间轴
 * @returns 每个 segment 对应的时间范围，匹配失败的 segment 不包含在结果中
 */
export function matchSegmentsToUtterances(
  segments: Array<{ id: number; voiceover: string }>,
  utterances: Utterance[]
): SegmentAudioRange[] {
  const results: SegmentAudioRange[] = [];
  let uIdx = 0; // 当前 utterance 索引

  for (const seg of segments) {
    const targetNorm = normalizeText(seg.voiceover);
    if (!targetNorm) {continue;}

    let accumulatedNorm = '';
    const matchedUtterances: Utterance[] = [];

    // 贪婪累加 utterances，直到匹配或耗尽
    while (uIdx < utterances.length) {
      const u = utterances[uIdx];
      const uNorm = normalizeText(u.text);
      accumulatedNorm += uNorm;
      matchedUtterances.push(u);
      uIdx++;

      // 检查是否包含目标文本
      if (accumulatedNorm.includes(targetNorm)) {
        break;
      }
    }

    // 判断是否匹配成功
    // 条件1：accumulated 文本包含 target 文本（最可靠）
    // 条件2：长度差异 ≤ 10% 且 accumulated 以 target 开头（避免完全不相关的文本被错误匹配）
    const isMatch = accumulatedNorm.includes(targetNorm);
    const lengthDiff = Math.abs(accumulatedNorm.length - targetNorm.length);
    const lengthTolerance = Math.max(1, Math.floor(targetNorm.length * 0.1));
    const isApproxMatch =
      lengthDiff <= lengthTolerance &&
      (accumulatedNorm.startsWith(targetNorm.slice(0, Math.min(3, targetNorm.length))) ||
       targetNorm.startsWith(accumulatedNorm.slice(0, Math.min(3, accumulatedNorm.length))));

    if (isMatch || isApproxMatch) {
      const firstU = matchedUtterances[0];
      const lastU = matchedUtterances[matchedUtterances.length - 1];
      results.push({
        segmentId: seg.id,
        startTime: firstU.startTime,
        endTime: lastU.endTime,
        actualDuration: (lastU.endTime - firstU.startTime) / 1000,
        matchedText: matchedUtterances.map((u) => u.text).join(''),
        matchedUtteranceCount: matchedUtterances.length,
      });
    } else {
      // 匹配失败：已消费的 utterances 确实属于这个失败的 segment，不回退 uIdx
    }
  }

  return results;
}
