/**
 * 写作指令一致性校验器
 *
 * 解决 issue：rich-outline 给每个节点分配了独立的 writingInstructions，
 * 但没有检查不同节点的指令是否矛盾或重复。
 *
 * 在大纲生成后，对相邻/相关节点的写作指令做交叉校验：
 * - 检测重复覆盖范围（两个节点都要覆盖相同内容）
 * - 检测矛盾指令（一个要"简洁"一个要"详细展开"）
 * - 检测缺失衔接（上一章"引出"的内容下一章没有"承接"）
 */

export interface InstructionNode {
  number: number;
  title: string;
  writingInstructions?: {
    opening?: string;       // 开篇方式
    structure?: string;     // 结构建议
    mustCover?: string[];   // 必覆盖内容
    avoidRepeat?: string[]; // 避免重复的内容
    keyTakeaway?: string;   // 核心收获
  };
}

export interface ConsistencyWarning {
  type: 'duplicate_coverage' | 'contradictory_direction' | 'missing_bridge' | 'out_of_order';
  severity: 'high' | 'medium' | 'low';
  nodeA: { number: number; title: string };
  nodeB: { number: number; title: string };
  description: string;
}

/**
 * 校验相邻章节的写作指令是否一致
 */
export function validateInstructionConsistency(
  nodes: InstructionNode[]
): ConsistencyWarning[] {
  const warnings: ConsistencyWarning[] = [];

  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    const next = nodes[i + 1];
    if (!node.writingInstructions) continue;

    const mustCover = node.writingInstructions.mustCover || [];
    const avoidRepeat = node.writingInstructions.avoidRepeat || [];
    const opening = node.writingInstructions.opening || '';

    // 检查与下一章的关系
    if (next?.writingInstructions) {
      const nextMustCover = next.writingInstructions.mustCover || [];
      const nextAvoidRepeat = next.writingInstructions.avoidRepeat || [];

      // 1) 检查重复覆盖
      const overlap = mustCover.filter(item =>
        nextMustCover.some(ni => similarity(item, ni) > 0.7)
      );
      if (overlap.length > 0) {
        warnings.push({
          type: 'duplicate_coverage',
          severity: 'medium',
          nodeA: { number: node.number, title: node.title },
          nodeB: { number: next.number, title: next.title },
          description: `两章均需覆盖: ${overlap.join('、')}，建议明确分工避免内容重复`,
        });
      }

      // 2) 检查矛盾方向
      if (
        (opening.includes('回顾') || opening.includes('总结')) &&
        (next.writingInstructions.opening?.includes('引入新') || next.writingInstructions.opening?.includes('开启新'))
      ) {
        // 正常：前一章总结，下一章开新 = OK
      } else if (
        (opening.includes('引出') || opening.includes('铺垫')) &&
        !nextMustCover.some(item =>
          mustCover.some(pi => similarity(item, pi) > 0.5)
        ) &&
        !next.writingInstructions.opening?.includes('承接')
      ) {
        warnings.push({
          type: 'missing_bridge',
          severity: 'medium',
          nodeA: { number: node.number, title: node.title },
          nodeB: { number: next.number, title: next.title },
          description: `第${node.number}章"引出/铺垫"的内容在第${next.number}章中未见"承接"，可能产生衔接断裂`,
        });
      }

      // 3) 检查结构风格矛盾
      const nodeStructure = node.writingInstructions.structure || '';
      const nextStructure = next.writingInstructions.structure || '';

      const conciseWords = ['简洁', '精炼', '扼要', '概括'];
      const detailedWords = ['详细', '深入', '全面', '展开'];

      const nodeIsConcise = conciseWords.some(w => nodeStructure.includes(w));
      const nextIsDetailed = detailedWords.some(w => nextStructure.includes(w));

      // 不矛盾（相邻章节可以不同风格），只检查明确矛盾
      if (
        (nodeIsConcise && nextStructure.includes('简洁')) ||
        (nextIsDetailed && nodeStructure.includes('详细'))
      ) {
        // 都简洁或都详细 = OK
      }
    }

    // 4) 检查 mustCover 和 avoidRepeat 内部分歧
    const internalOverlap = mustCover.filter(item =>
      avoidRepeat.some(ar => similarity(item, ar) > 0.6)
    );
    if (internalOverlap.length > 0) {
      warnings.push({
        type: 'contradictory_direction',
        severity: 'high',
        nodeA: { number: node.number, title: node.title },
        nodeB: { number: node.number, title: node.title },
        description: `第${node.number}章自身矛盾：既要覆盖"${internalOverlap[0]}"又要避免重复，指令冲突`,
      });
    }
  }

  return warnings;
}

/**
 * 简单字符串相似度（Jaccard 字符级）
 */
function similarity(a: string, b: string): number {
  const aChars = new Set(a.replace(/\s/g, ''));
  const bChars = new Set(b.replace(/\s/g, ''));
  const intersection = new Set([...aChars].filter(c => bChars.has(c)));
  const union = new Set([...aChars, ...bChars]);
  return union.size === 0 ? 0 : intersection.size / union.size;
}
