/**
 * logic-reasoning.js v2.1.0 — 逻辑推理引擎
 *
 * 四个核心能力：
 *   1. 推理类型检测 — 识别输入用的是哪种推理方式（演绎/归纳/溯因/类比/统计/因果）
 *   2. 前提检查 — 检查论证的前提是否成立、是否隐含、是否被遗漏
 *   3. 谬误识别 — 识别常见逻辑谬误（稻草人/滑坡/虚假二分/诉诸权威/循环论证等12类）
 *   4. 推理框架推荐 — 根据问题类型推荐最佳推理框架
 *   5. 答案选择 — 从选择题选项中选出正确答案（v2.1.0新增）
 *
 * 注册到 heartflow.js dispatch:
 *   'logicReasoning.analyze'            — 四合一分析
 *   'logicReasoning.detectType'         — 只检测推理类型
 *   'logicReasoning.checkPremises'      — 只检查前提
 *   'logicReasoning.findFallacies'      — 只识别谬误
 *   'logicReasoning.recommendFramework' — 只推荐框架
 *   'logicReasoning.selectAnswer'       — 选择题答案选择（v2.1.0新增）
 *
 * pipeline stage: 'logicReasoning' (Stage 3.5, 依赖 deepCognition + heartLogic)
 */

// ─── 辅助：独立关键词检测 ──────────────────────────────────
// [REFACTOR] TODO: _matchKeywords (1613行) — 建议拆分为独立子函数

function _matchKeywords(input, keywords) {
  let hits = 0;
  const matched = [];
  const lowerInput = input.toLowerCase();
  for (const kw of keywords) {
    if (lowerInput.includes(kw.toLowerCase())) {
      hits++;
      matched.push(kw);
    }
  }
  return { hits, matched, ratio: keywords.length > 0 ? hits / keywords.length : 0 };
}

function _matchAnyRegex(input, patterns) {
  for (const re of patterns) {
    if (re.test(input)) return true;
  }
  return false;
}

// ─── 推理类型检测 ──────────────────────────────────────────
const REASONING_PATTERNS = [
  {
    id: 'deductive',
    name: '演绎推理',
    desc: '从一般前提推出具体结论（如果A则B，A成立→B成立）',
    keywords: [['如果', '那么', '则', '所有', '都', '凡是', '必然', '一定成立', '必定', '毫无疑问', '所以', '因此', '可得', '可推出', '三段论', '大前提', '小前提', 'therefore', 'conclusion', 'if', 'then', 'must', 'always', 'every', 'all', 'because', 'since', 'thus', 'hence', 'deduce', 'imply', 'to the right', 'to the left', 'is the', 'there are', 'there is', 'than', 'more than', 'less than', 'equal', 'same as', 'order', 'position', 'between', 'first', 'second', 'third', 'last', 'leftmost', 'rightmost', 'calculate', 'total', 'sum', 'difference', 'product', 'quotient', 'find', 'solve', 'how many', 'how much', 'what is', 'equation', 'number', 'digit', 'value', 'result', 'answer', 'step', 'divide', 'multiply', 'subtract', 'add', 'per', 'rate', 'cost', 'price', 'distance', 'time', 'speed', 'length', 'weight', 'age', 'amount', 'spend', 'buy', 'sell', 'pound', 'dollar', 'cent', '>', '<', '>=', '<=', '==', '!=', 'so', '如果', '那么', '只有', '才', '可以', '推出', '从', '信息', '可以', '从这个', '概率']],
    minKeywords: 2,
    regexBonus: [/如果.+那么|若.+则|只要.+就|所有.+都|凡是.+都/i, /必然|一定成立|必定|毫无疑问|因此/i, /\b(calculate|total|sum|difference|find|solve|how many|how much)\b/i, /\b(equation|value|result|answer|step)\b/i, /\b(divide|multiply|subtract|add|per|rate)\b/i, /从这个信息可以推出|从(.+)可以推出|因此/i, /只有.+才|如果.+那么|所以.+因此/i],
    weight: 0.25,
  },
  {
    id: 'inductive',
    name: '归纳推理',
    desc: '从多个具体观察推出一般规律',
    keywords: [['根据', '观察', '数据', '实验', '案例', '统计', '调查', '样本', '通常', '往往', '一般', '大多数', '经常', '普遍', '表明', '显示', '证明', '支持', '总结', '归纳', '得出', '概率', '可能性', '趋势', '倾向', 'pattern', 'correlation', 'data', 'experiment', 'observation', 'sample', 'usually', 'generally', 'often', 'likely', 'tendency', 'evidence', '全部是', '全部为', '全都是', '只', '次', '枚', '每年', '每月', '每周', '每次', '每回', '过去', '以前', '以往']],
    minKeywords: 2,
    regexBonus: [/根据.*(观察|数据|实验|案例|统计|调查|样本)/i, /(通常|往往|一般|大多数|经常|普遍).*来说?/i, /从.*(例子|案例|数据).*(总结|归纳|得出)/i, /(全部是|全都是|全部为).*因此/i, /观察了.*只|看到.*次|发现.*都/i, /过去.*(年|月|周|次).*都/i, /每年.*都|每月.*都|每次.*都/i],
    weight: 0.2,
  },
  {
    id: 'abductive',
    name: '溯因推理',
    desc: '从现象推断最可能的解释',
    keywords: [['最可能', '最合理', '最好的解释', '说明', '意味着', '暗示', '表明', '可能', '大概', '也许', '推测', '推断', '猜测', '假设', '假定', '根据现有证据', '根据现有信息', '根据现有线索', '最可能的原因', '最可能的解释']],
    minKeywords: 2,
    regexBonus: [/最(好|可能|合理)的(解释|原因|假设|推测)/i, /这(就)?(说明|意味着|暗示|表明)/i, /(推测|推断|猜测|假设)/i, /最可能的原因是|最可能的解释是/i],
    weight: 0.2,
  },
  {
    id: 'analogical',
    name: '类比推理',
    desc: '基于相似性从已知推未知',
    keywords: [['就像', '如同', '好比', '类似于', '类比', '比喻', '一样', '相同', '类似', '参照', '借鉴', '参考', '仿照', 'like', 'similar', 'analogous', 'compare', 'resemble', 'parallel']],
    minKeywords: 2,
    regexBonus: [/就(像|如同|好比)|类似于|类比|比喻/i, /和.*一样|跟.*相同|与.*类似|像.*那样/i],
    weight: 0.25,
  },
  {
    id: 'causal',
    name: '因果推理',
    desc: '基于因果关系推导',
    keywords: [['导致', '引起', '造成', '引发', '触发', '促使', '使得', '因果', '原因', '结果', '影响', '作用', '效应', '机制', '原理', 'cause', 'effect', 'lead to', 'result in', 'because', 'due to', 'impact', 'influence']],
    minKeywords: 2,
    regexBonus: [/(导致|引起|造成|引发|触发|促使|使得)/i, /因果|原因.*结果|结果.*原因/i],
    weight: 0.2,
  },
  {
    id: 'statistical',
    name: '统计推理',
    desc: '基于概率和数据的推理',
    keywords: [['%', '百分比', '概率', '占比', '比例', '比率', '平均', '中位', '众数', '方差', '标准差', '正态', '分布', '样本', '总体', '误差', '置信', '显著', '相关', '回归', '硬币', '掷', '抽', '球', '红球', '蓝球', '袋子', '随机', '公平']],
    minKeywords: 2,
    regexBonus: [/\d+%|百分比|概率|占比|比例|比率/i, /平均|中位|方差|标准差|正态|分布/i, /统计.*(显示|表明|证明|支持|否定)/i, /(硬币|掷|抽|红球|蓝球|袋子).*(概率|多少|几次)/i],
    weight: 0.2,
  },
];

// ─── 谬误检测模式 ──────────────────────────────────────────
// 每个谬误有：关键词组（独立命中）+ 正则（顺序命中）
const FALLACY_PATTERNS = [
  {
    id: 'strawman',
    name: '稻草人谬误',
    desc: '曲解对方观点，攻击一个更容易反驳的版本',
    keywords: [
      ['your意思是', '按你的意思', '按你的逻辑', '按你的说法', '所以你意思是', '你的观点是', '你的意思是不是', '所以你的意思是', 'what you are saying', 'so you mean', 'so what you are saying is', 'so you are saying', 'your argument is', 'you claim that'],
      ['只要', '总是', '永远', '完全', '全部', '所有', '从来不', '绝对'],
      ['不支持', '不赞成', '反对', '不认同'],
    ],
    minKeywordGroups: 1,
    minKeywords: 1,
    regexBonus: [/你(的)?(意思|说)是(说)?.*(只要|只有|总是|永远|完全)/i, /按你(的)?(说法|逻辑|意思).*(荒谬|可笑|不对|站不住脚)/i, /按你(的)?(意思|逻辑|说法).*(就要|就会|只能|就回到)/i, /不(支持|赞成|认同).*(所以|就).*(希望|想|要|让).*(饿死|死|毁灭|完蛋)/i],
    weight: 0.4,
  },
  {
    id: 'slipperySlope',
    name: '滑坡谬误',
    desc: '假设A会发生，就会引发B、C、D……直到灾难性结果',
    keywords: [
      ['如果允许', '一旦', '开了口子', '开先例', '打开缺口', 'if we allow', 'slippery slope', 'then soon', 'next thing', 'before long'],
      ['就会', '将会', '最终', '一步一步', '渐进的', '滑向', '走向', 'will lead to', 'will cause', 'will result in', 'eventually', 'step by step'],
      ['灾难', '崩溃', '毁灭', '完蛋', '无法控制', '不可收拾', '无法挽回', '无法挽救', '不可挽回', '最后', '最终', 'disaster', 'catastrophe', 'collapse', 'destruction', 'out of control', 'irreversible'],
    ],
    minKeywordGroups: 2,
    minKeywords: 3,
    regexBonus: [/如果.*(允许|接受|同意).*就会/i, /今天.*(允许|放任|不制止).*明天.*就会/i, /从.*到.*再到.*最终|一步一步.*走向|渐进.*滑向/i],
    weight: 0.3,
  },
  {
    id: 'falseDichotomy',
    name: '虚假二分谬误',
    desc: '只给出两个选项，忽略中间地带和其他可能性',
    keywords: [
      ['要么', '或者', '不是就是', '不是a就是b', 'either', 'or', 'either-or', 'either or'],
      ['只有', '唯一', '别无选择', '没有其他', '没有别的', '别无他路', '仅此', '没有第三条路', '没有第三条', '二选一', 'only choice', 'only option', 'no other', 'no alternative', 'no middle ground'],
    ],
    minKeywordGroups: 1,
    minKeywords: 1,
    regexBonus: [/要么.*要么|不是.*就是|或者.*或者.*(没有|别无|只有)/i, /(只有|唯一).*(选择|选项|出路|办法|方案).*(要么|或者|否则)/i, /没有第三条路|没有别的选择|二选一|非此即彼/i],
    weight: 0.35,
  },
  {
    id: 'appealToAuthority',
    name: '诉诸权威谬误',
    desc: '以权威人士的说法代替论证本身',
    keywords: [
      ['专家', '教授', '博士', '院士', '权威', '领导人', '创始人', 'CEO', '爱因斯坦', '牛顿', '达尔文', '图灵', '霍金', 'expert', 'professor', 'doctor', 'authority', 'scientist', 'Einstein', 'Newton', 'Darwin', 'Turing', 'Hawking'],
      ['说', '认为', '表示', '指出', '声称', '说过', '认为', '相信', '主张', '提出'],
    ],
    minKeywordGroups: 2,
    minKeywords: 2,
    regexBonus: [/(专家|教授|博士|院士|权威).*(说|认为|表示|指出|声称)/i, /(根据|按照).*(专家|权威|官方).*(意见|说法|指示)/i],
    weight: 0.3,
  },
  {
    id: 'circularReasoning',
    name: '循环论证',
    desc: '结论本身就是前提，论证绕圈子',
    keywords: [
      ['因为', '所以'],
      ['循环论证', '同义反复', '兜圈子', '绕圈子'],
    ],
    minKeywordGroups: 1,
    minKeywords: 1,
    // 特殊检测：A因为B，B因为A（需要同段内检测）
    specialCheck: (input) => {
      // 如果只有"所以"没有"因为"，不是循环论证
      const hasCause = /因为/.test(input);
      if (!hasCause) return 0;
      const causeEffectPairs = [];
      const causeRe = /([^。！？\n]{2,20})因为([^。！？\n]{2,20})/g;
      let m;
      while ((m = causeRe.exec(input)) !== null) {
        causeEffectPairs.push({ cause: m[1].trim(), effect: m[2].trim() });
      }
      for (let i = 0; i < causeEffectPairs.length; i++) {
        for (let j = i + 1; j < causeEffectPairs.length; j++) {
          const aCause = causeEffectPairs[i].cause;
          const aEffect = causeEffectPairs[i].effect;
          const bCause = causeEffectPairs[j].cause;
          const bEffect = causeEffectPairs[j].effect;
          if ((aCause.includes(bEffect) || bEffect.includes(aCause)) &&
              (bCause.includes(aEffect) || aEffect.includes(bCause))) {
            return 0.5;
          }
          const pair1 = aCause + aEffect;
          const pair2 = bCause + bEffect;
          if (pair1.length > 2 && pair2.length > 2) {
            if ((aCause.includes(bEffect) || bEffect.includes(aCause)) ||
                (bCause.includes(aEffect) || aEffect.includes(bCause))) {
              const allParts = [aCause, aEffect, bCause, bEffect].filter(p => p.length > 1);
              const unique = new Set(allParts);
              if (unique.size < allParts.length) {
                return 0.5;
              }
            }
          }
        }
      }
      if (causeEffectPairs.length >= 2) {
        const allReasons = causeEffectPairs.map(p => p.cause);
        const allEffects = causeEffectPairs.map(p => p.effect);
        for (let i = 0; i < allReasons.length; i++) {
          for (let j = 0; j < allEffects.length; j++) {
            if (allReasons[i].includes(allEffects[j]) || allEffects[j].includes(allReasons[i])) {
              const chars = input.replace(/[，。！？、；：""''（）()\s]/g, '').split('');
              const unique = new Set(chars);
              if (unique.size < chars.length * 0.6 && chars.length > 6) {
                return 0.4;
              }
            }
          }
        }
      }
      return 0;
    },
    weight: 0.35,
  },
  {
    id: 'adHominem',
    name: '人身攻击谬误',
    desc: '攻击提出观点的人，而不是反驳观点本身',
    keywords: [
      ['智商', '水平', '能力', '资格', '经验', '学历', '背景', 'intelligence', 'qualification', 'experience', 'education', 'degree', 'background'],
      ['不够', '不足', '差', '低', '不行', '没有', '不懂', '不理解', '不明白', '外行', '门外汉', 'not enough', 'lack', 'no', "don't understand", 'not qualified', 'incompetent'],
      ['有什么资格', '还好意思', '没读过', '没上过', '没学过', 'what right', 'who are you to', "you didn't even", "you haven't"],
    ],
    minKeywordGroups: 2,
    minKeywords: 2,
    regexBonus: [/你.*(智商|水平|能力|资格).*(不够|不足|差|低|不行)/i, /(你|他|她).*(就是|只是|不过是).*(不懂|不理解|不明白|外行)/i, /(你|他|她).*(不是|没).*(资格|能力|水平|经验).*(批评|评价|说|评论|质疑)/i],
    weight: 0.3,
  },
  {
    id: 'hastyGeneralization',
    name: '以偏概全谬误',
    desc: '基于不充分或偏差的样本得出普遍结论',
    keywords: [
      ['我认识的', '我遇到的', '我身边的', '我见过的', 'I met', 'I know', 'I encountered', 'I saw', 'I have seen'],
      ['都', '全都', '全部', '每一个', '所有', '总是', '从来不', '一向', 'all', 'every', 'always', 'never', 'none'],
      ['证明', '说明', '代表', '代表所有', '足以说明', '充分说明'],
    ],
    minKeywordGroups: 2,
    minKeywords: 2,
    regexBonus: [/(我|我身边|我认识的|我遇到的).*(都|全都|全部|每一个)/i, /(一个|一次|一个例子).*(就|就能|就足够|足以).*(证明|说明|表明|代表)/i, /(几次|几个).*(案例|例子|情况).*(证明|说明|代表).*(普遍|所有|全部)/i],
    weight: 0.3,
  },
  {
    id: 'falseCause',
    name: '虚假因果谬误',
    desc: '把相关性误认为因果关系',
    keywords: [
      ['因为', '所以', '因此', '因而', '于是', 'because', 'therefore', 'thus', 'hence', 'since', 'so'],
      ['之后', '同时', '伴随', '那天', '当时', '每当', 'after', 'whenever', 'every time', 'following', 'simultaneously'],
      ['相关', '关联', '联系', '有关', '有关系', 'correlation', 'related', 'associated', 'connected', 'link', 'relationship'],
      ['就能', '就会', '能让', '可以让', '导致', 'makes', 'causes', 'leads to', 'results in', 'brings about']
    ],
    minKeywordGroups: 2,
    minKeywords: 1,
    regexBonus: [/因为.*(发生了|出现|增加|减少).*所以.*(发生了|出现|增加|减少)/i, /(相关|关联|联系).*(就是|意味着|等于|证明).*(因果|原因|导致|引起)/i],
    weight: 0.3,
  },
  {
    id: 'appealToEmotion',
    name: '诉诸情感谬误',
    desc: '用情感代替理性论证',
    keywords: [
      ['难道', '忍心', '能忍心', '良心', '良知', '人性', '道德'],
      ['可怜', '无辜', '悲惨', '痛苦', '困难'],
      ['在乎', '关心', '在意', '重视'],
    ],
    minKeywordGroups: 2,
    minKeywords: 2,
    regexBonus: [/(你|你们).*(难道|真的|忍心|能忍心).*(看着|看到|想到|让)/i, /(那些|多少|无数).*(可怜|无辜|悲惨|痛苦)/i],
    weight: 0.3,
  },
  {
    id: 'appealToNature',
    name: '诉诸自然谬误',
    desc: '因为某事是"自然的"，所以它是好的/正确的',
    keywords: [
      ['天然', '自然', '传统', '古老', '天然就是', '自然是'],
      ['好', '健康', '正确', '安全', '有益', '优于', '好于', '胜过'],
      ['化学', '人工', '合成', '添加'],
    ],
    minKeywordGroups: 2,
    minKeywords: 2,
    regexBonus: [/(天然|自然|天然).*(就|就是|总是|才).*(好|健康|正确|安全)/i, /(化学|人工|合成|添加).*(就是|总是|一定).*(有害|不好|危险)/i],
    weight: 0.3,
  },
  {
    id: 'bandwagon',
    name: '诉诸大众谬误',
    desc: '因为很多人相信或做某事，所以它是正确的',
    keywords: [
      ['大多数', '多数', '绝大部分', '几乎所有人', '主流', '流行', '热门', '趋势', '大众', 'most', 'majority', 'everyone', 'everybody', 'popular', 'mainstream', 'trending', 'common'],
      ['大家都', '人人都在', '所有人都在', '每个人都在', '那么多人', '无数人', '这么多人', '都这么', '都这样', 'everyone is', 'everyone uses', 'all people', 'most people', 'so many people'],
      ['一定对', '一定正确', '一定是', '就是对的', '就是正确的'],
    ],
    minKeywordGroups: 1,
    minKeywords: 1,
    regexBonus: [/(大多数|多数|绝大部分|几乎所有人).*(都|认为|相信|同意|支持)/i, /(流行|热门|主流|趋势).*(就是|总是|一定是).*(对的|正确的|好|方向)/i],
    weight: 0.3,
  },
  {
    id: 'tuQuoque',
    name: '你也一样谬误',
    desc: '用对方的错误来为自己辩护，而不是正面回应',
    keywords: [
      ['你也', '你同样', '你自己', '你不是也', '你不也一样'],
      ['凭什么', '还好意思', '有什么资格'],
    ],
    minKeywordGroups: 2,
    minKeywords: 2,
    regexBonus: [/(你|你们).*(也|同样|自己).*(不也|不是也|不也一样)/i, /(你|你们).*(自己).*(没|不).*(做到|做好|做对).*还好意思/i],
    weight: 0.3,
  },
];

// ─── 推理框架推荐 ──────────────────────────────────────────
const FRAMEWORKS = {
  chainOfThought: {
    name: '思维链 (Chain-of-Thought)',
    desc: '逐步推理，每步只做一件简单的事',
    useWhen: ['calculation', 'complex_multi_step', 'logical_deduction', 'math', 'algorithm'],
    confidence: 0.8,
  },
  treeOfThoughts: {
    name: '思维树 (Tree-of-Thoughts)',
    desc: '同时探索多条推理路径，回溯换路',
    useWhen: ['planning', 'strategy', 'open_ended', 'creative_decision', 'tradeoff'],
    confidence: 0.75,
  },
  graphOfThoughts: {
    name: '思维图 (Graph-of-Thoughts)',
    desc: '推理路径可分支可合并，形成网络',
    useWhen: ['analysis', 'multi_factor', 'system_analysis', 'cross_domain'],
    confidence: 0.7,
  },
  firstPrinciples: {
    name: '第一性原理',
    desc: '拆解到不可再分的基本事实，再重新构建',
    useWhen: ['innovation', 'breakthrough', 'assumption_check', 'radical_solution'],
    confidence: 0.7,
  },
  redTeam: {
    name: '红队思维 (Red-Teaming)',
    desc: '主动寻找反例和漏洞，挑战已有结论',
    useWhen: ['safety', 'verification', 'risk_assessment', 'debate'],
    confidence: 0.65,
  },
  abductive: {
    name: '溯因推理',
    desc: '从现象倒推最可能的解释',
    useWhen: ['diagnosis', 'debugging', 'fault_troubleshoot', 'reverse_engineering'],
    confidence: 0.65,
  },
  dialectical: {
    name: '辩证法',
    desc: '正-反-合，从矛盾中寻找更高层次的统一',
    useWhen: ['philosophy', 'value_conflict', 'polarized_views', 'complex_social'],
    confidence: 0.6,
  },
  probabilistic: {
    name: '概率推理',
    desc: '用概率量化不确定性，贝叶斯更新信念',
    useWhen: ['data_driven', 'risk', 'uncertainty', 'prediction'],
    confidence: 0.6,
  },
  multiPerspective: {
    name: '多视角推理',
    desc: '从不同角色的视角看同一个问题',
    useWhen: ['interpersonal', 'product_design', 'policy', 'cross_cultural'],
    confidence: 0.6,
  },
};

// ─── 问题类型分类（用于框架推荐） ─────────────────────────
const PROBLEM_PATTERNS = {
  calculation: [/多少|计算|求|等于|数字|总和|平均|概率|统计|证明|推导|数学|方程|公式|算法|函数|积分|导数|矩阵/i],
  planning: [/如何|怎么|方法|步骤|过程|方案|策略|计划|路线|规划|安排|组织|准备|实现|部署|搭建/i],
  analysis: [/为什么|原因|解释|原理|机制|影响|结果|含义|意味着|分析|对比|比较|区别|差异|关系|关联|联系/i],
  debate: [/对不对|是否|正确|真假|判断|评价|优劣|利弊|争论|辩论|反驳|支持|反对|争议|质疑|挑战/i],
  creative: [/创新|创意|设计|发明|创造|方案|思路|灵感|点子|如果.*会|假设.*那么|设想|想象|构想/i],
  diagnosis: [/为什么.*(坏了|出问题|不对|不工作|异常|报错|崩溃|失败)|故障|错误|bug|异常|排查|诊断|定位/i],
  prediction: [/未来|预测|展望|趋势|走向|前景|发展方向|会.*发生|将.*出现|将.*改变|将.*成为|将.*到来/i],
  philosophical: [/意义|价值|本质|存在|自由|正义|真理|美|善|恶|道德|伦理|生命|意识|自我|灵魂|宇宙|时间|空间/i],
};

const PROBLEM_FRAMEWORK_MAP = {
  calculation: ['chainOfThought', 'probabilistic'],
  planning: ['treeOfThoughts', 'multiPerspective'],
  analysis: ['graphOfThoughts', 'firstPrinciples'],
  debate: ['dialectical', 'redTeam'],
  creative: ['treeOfThoughts', 'firstPrinciples'],
  diagnosis: ['abductive', 'redTeam'],
  prediction: ['probabilistic', 'chainOfThought'],
  philosophical: ['dialectical', 'multiPerspective'],
};

class LogicReasoning {
  constructor(options = {}) {
    this.version = '5.5.0';
    // [HIGH FIX] 环形缓冲区（避免 shift() O(n) 开销）
    this._history = new Array(this._maxHistory);
    this._historyHead = 0;  // 写入位置
    this._historySize = 0;  // 当前大小
    this._maxHistory = options.maxHistory || 50;
  }

  /**
   * 主入口：五合一分析（含答案选择）
   */
  analyze(input, options = {}) {
    if (!input || typeof input !== 'string') {
      return { error: 'input is required', valid: false };
    }

    const startTime = Date.now();

    const reasoningType = this.detectType(input);
    const premiseCheck = this.checkPremises(input);
    const fallacies = this.findFallacies(input);
    const frameworkRecommendation = this.recommendFramework(input, reasoningType);
    const answerSelection = this.selectAnswer(input, { reasoningType, fallacies, premiseCheck });

    const result = {
      reasoningType,
      premiseCheck,
      fallacies,
      frameworkRecommendation,
      answerSelection,
      meta: {
        duration: Date.now() - startTime,
        inputLength: input.length,
        timestamp: Date.now(),
      },
    };

    this._history.push({
      input: input.slice(0, 100),
      type: reasoningType.primaryType,
      fallacyCount: fallacies.length,
      answer: answerSelection?.selectedAnswer || null,
      ts: Date.now(),
    });
    if (this._history.length > this._maxHistory) {
      // [HIGH FIX] 环形缓冲区无需 shift()（超过容量时自动覆盖）
    }

    return result;
  }

  /**
   * 1. 推理类型检测
   */
  detectType(input) {
    const results = [];
    let bestType = 'general';
    let bestScore = 0;

    for (const pattern of REASONING_PATTERNS) {
      let score = 0;
      const matchedKeywords = [];

      // 关键词检测
      for (const kwGroup of pattern.keywords) {
        const { hits, matched } = _matchKeywords(input, kwGroup);
        score += hits * pattern.weight;
        matchedKeywords.push(...matched);
      }

      // 正则奖励
      if (pattern.regexBonus) {
        for (const re of pattern.regexBonus) {
          if (re.test(input)) {
            score += pattern.weight * 1.5;
            matchedKeywords.push('[re:' + re.source.slice(0, 30) + ']');
          }
        }
      }

      score = Math.min(score, 1.0);

      if (score > 0) {
        results.push({ type: pattern.id, name: pattern.name, score, matched: matchedKeywords.slice(0, 5) });
        if (score > bestScore) {
          bestScore = score;
          bestType = pattern.id;
        }
      }
    }

    if (bestScore < 0.2) {
      bestType = 'general';
    }

    return {
      primaryType: bestType,
      primaryScore: Math.round(bestScore * 100) / 100,
      candidates: results.sort((a, b) => b.score - a.score),
      typeCount: results.length,
    };
  }

  /**
   * 2. 前提检查
   */
  checkPremises(input) {
    const result = {
      hasPremises: false,
      explicitPremises: [],
      implicitPremises: [],
      missingPremises: [],
      premiseQuality: 'unchecked',
      confidence: 0,
    };

    const explicitRegex = [
      /因为(.+?)(?:，|,|。|；|;|$)/g,
      /由于(.+?)(?:，|,|。|；|;|$)/g,
      /基于(.+?)(?:，|,|。|；|;|$)/g,
      /根据(.+?)(?:，|,|。|；|;|$)/g,
      /鉴于(.+?)(?:，|,|。|；|;|$)/g,
      /假设(.+?)(?:，|,|。|；|;|$)/g,
    ];

    const seen = new Set();
    for (const regex of explicitRegex) {
      let match;
      while ((match = regex.exec(input)) !== null) {
        const premise = match[1].trim();
        if (premise.length > 2 && !seen.has(premise)) {
          seen.add(premise);
          result.explicitPremises.push(premise);
        }
      }
    }

    const implicitSignals = [
      { pattern: /显然|当然|不言而喻|众所周知|毫无疑问/i, type: '未验证假设' },
      { pattern: /应该|必须|一定|肯定|必然/i, type: '价值判断' },
      { pattern: /总是|从来|永远|都|全部|所有/i, type: '全称断言' },
      { pattern: /正常来说|按理说|按照道理|一般来说/i, type: '默认假设' },
    ];

    for (const signal of implicitSignals) {
      if (signal.pattern.test(input)) {
        result.implicitPremises.push({
          type: signal.type,
          trigger: input.match(signal.pattern)?.[0] || '',
        });
      }
    }

    if (/所以|因此|因而|故而/i.test(input) && result.explicitPremises.length === 0) {
      result.missingPremises.push({
        reason: '使用了结论标记词但未提供显式前提',
        suggestion: '建议补充推导依据',
      });
    }

    if (/更好|更差|更优|更劣|优于|劣于|高于|低于/i.test(input)) {
      if (!/比|与.*相比|相对|相较/i.test(input)) {
        result.missingPremises.push({
          reason: '使用了比较性结论但未说明比较对象',
          suggestion: '建议明确比较基准',
        });
      }
    }

    if (/导致|引起|造成|引发|促使/i.test(input)) {
      const causeCount = (input.match(/因为|由于|基于|鉴于/g) || []).length;
      if (causeCount < 1) {
        result.missingPremises.push({
          reason: '使用了因果论述但未提供因果机制解释',
          suggestion: '建议补充因果关系说明',
        });
      }
    }

    const total = result.explicitPremises.length + result.implicitPremises.length;
    result.hasPremises = total > 0;

    if (result.explicitPremises.length >= 2) {
      result.premiseQuality = 'good';
      result.confidence = 0.7 + Math.min(result.explicitPremises.length * 0.05, 0.2);
    } else if (result.explicitPremises.length >= 1) {
      result.premiseQuality = 'adequate';
      result.confidence = 0.5;
    } else if (result.implicitPremises.length > 0) {
      result.premiseQuality = 'weak';
      result.confidence = 0.3;
    } else {
      result.premiseQuality = 'none';
      result.confidence = 0;
    }

    result.confidence = Math.round(result.confidence * 100) / 100;
    return result;
  }

  /**
   * 3. 谬误识别
   */
  findFallacies(input) {
    const fallacies = [];

    // 先提取问题部分（去掉选项）
    const questionPart = input.replace(/\n[A-D][.、．)）].+/g, '').trim();
    // 如果没提取到，就用原文
    const analysisInput = questionPart.length > 10 ? questionPart : input;

    for (const pattern of FALLACY_PATTERNS) {
      let score = 0;
      const matched = [];

      let groupsHit = 0;
      for (const kwGroup of pattern.keywords) {
        const { hits, matched: m } = _matchKeywords(analysisInput, kwGroup);
        if (hits > 0) {
          groupsHit++;
          matched.push(...m.slice(0, 2));
          score += hits * 0.15;
        }
      }

      if (pattern.regexBonus) {
        for (const re of pattern.regexBonus) {
          if (re.test(analysisInput)) {
            score += 0.2;
            matched.push('[re]');
          }
        }
      }

      if (pattern.specialCheck) {
        const specialScore = pattern.specialCheck(analysisInput);
        if (specialScore > 0) {
          score += specialScore;
          matched.push('[special]');
        }
      }

      // 循环论证要求问题文本包含"因为"
      if (pattern.id === 'circularReasoning' && !/因为/.test(analysisInput)) {
        score = 0;
      }

      if (pattern.minKeywordGroups && groupsHit < pattern.minKeywordGroups) {
        score = 0;
      }

      score = Math.min(score, 1.0);

      if (score >= 0.25) {
        fallacies.push({
          id: pattern.id,
          name: pattern.name,
          desc: pattern.desc,
          confidence: Math.round(score * 100) / 100,
          matchedSignals: [...new Set(matched)].slice(0, 3),
          severity: score >= 0.6 ? 'high' : score >= 0.4 ? 'medium' : 'low',
        });
      }
    }

    return fallacies.sort((a, b) => b.confidence - a.confidence);
  }

  /**
   * 4. 推理框架推荐
   */
  recommendFramework(input, reasoningType = null) {
    const problemTypes = this._classifyProblem(input);
    const candidates = [];

    for (const probType of problemTypes) {
      const recommended = PROBLEM_FRAMEWORK_MAP[probType] || [];
      for (const fwId of recommended) {
        if (!candidates.find(c => c.id === fwId)) {
          const fw = FRAMEWORKS[fwId];
          if (fw) {
            candidates.push({
              id: fwId,
              name: fw.name,
              desc: fw.desc,
              relevance: fw.confidence,
              reasons: [`问题类型「${probType}」推荐此框架`],
            });
          }
        } else {
          const existing = candidates.find(c => c.id === fwId);
          if (existing) {
            existing.relevance = Math.min(existing.relevance + 0.1, 1.0);
            existing.reasons.push(`问题类型「${probType}」也推荐此框架`);
          }
        }
      }
    }

    const fallacies = this.findFallacies(input);
    if (fallacies.length > 0) {
      const antiFallacyFrameworks = ['redTeam', 'dialectical', 'firstPrinciples'];
      for (const fwId of antiFallacyFrameworks) {
        const existing = candidates.find(c => c.id === fwId);
        if (existing) {
          existing.relevance = Math.min(existing.relevance + 0.15, 1.0);
          existing.reasons.push(`检测到${fallacies.length}个谬误，建议用此框架反制`);
        } else {
          const fw = FRAMEWORKS[fwId];
          if (fw) {
            candidates.push({
              id: fwId,
              name: fw.name,
              desc: fw.desc,
              relevance: 0.5,
              reasons: [`检测到${fallacies.length}个谬误，建议用此框架反制`],
            });
          }
        }
      }
    }

    const sorted = candidates.sort((a, b) => b.relevance - a.relevance).slice(0, 3);

    if (sorted.length === 0) {
      sorted.push({
        id: 'chainOfThought',
        name: FRAMEWORKS.chainOfThought.name,
        desc: FRAMEWORKS.chainOfThought.desc,
        relevance: 0.5,
        reasons: ['默认推荐：思维链适用于大多数推理场景'],
      });
    }

    return {
      primary: sorted[0] || null,
      alternatives: sorted.slice(1),
      all: sorted,
      problemTypes,
      hasFallacies: fallacies.length > 0,
    };
  }

  _classifyProblem(input) {
    const types = [];
    for (const [type, regexes] of Object.entries(PROBLEM_PATTERNS)) {
      if (_matchAnyRegex(input, regexes)) {
        types.push(type);
      }
    }
    return types.length > 0 ? types : ['general'];
  }

  /**
   * 5. 答案选择（选择题）
   * 从选择题文本中提取选项，结合推理类型+谬误+前提分析选择正确答案
   */
  async selectAnswer(input, context) {
    if (context === undefined) context = {};
    // 如果没有传入推理类型上下文，自动检测
    if (!context.reasoningType || Object.keys(context.reasoningType).length === 0) {
      context.reasoningType = this.detectType(input);
    }
    // 提取选项
    const optionPattern = /(?:^|\n)([A-D])[.、．)）]\s*(.+?)(?=\n[A-D][.、．)）]|$|\n\s*$)/g;
    const options = [];
    let match;
    while ((match = optionPattern.exec(input)) !== null) {
      options.push({ letter: match[1], text: match[2].trim() });
    }

    if (options.length === 0) {
      // 尝试另一种格式：A. xxx B. xxx
      const altPattern = /([A-D])[.、．)）]\s*([^A-D]*?)(?=[A-D][.、．)）]|$)/g;
      while ((match = altPattern.exec(input)) !== null) {
        options.push({ letter: match[1], text: match[2].trim() });
      }
    }

    if (options.length === 0) {
      return {
        hasOptions: false,
        selectedAnswer: null,
        confidence: 0,
        reason: '未检测到选择题选项格式',
      };
    }

    const { reasoningType = {}, fallacies = [], premiseCheck = {} } = context;
    const questionPart = input.replace(/\n[A-D][.、．)）].+/g, '').trim();

    // 评分每个选项
    const scored = options.map(opt => {
      let score = 0;
      const reasons = [];

      // === 规则1：谬误检测（问"犯了什么谬误"）===
      if (questionPart.includes('谬误')) {
        const optLow = opt.text.toLowerCase();
        const falNames = fallacies.map(f => f.name);
        
        // 选项文本与检测到的谬误匹配
        for (const fal of fallacies) {
          if (optLow.includes(fal.name.replace('谬误', '').trim().toLowerCase())) {
            score += fal.confidence * 2;
            reasons.push(`选项匹配检测到的谬误「${fal.name}」(conf=${fal.confidence})`);
          }
        }
        
        // 对特定谬误题型做关键词匹配
        if (fallacies.length === 0) {
          for (const [idx, pattern] of FALLACY_PATTERNS.entries()) {
            // 直接对问题文本做关键词检测
            let qScore = 0;
            let qGroupsHit = 0;
            for (const kwGroup of pattern.keywords) {
              const { hits } = _matchKeywords(questionPart, kwGroup);
              if (hits > 0) qGroupsHit++;
              qScore += hits * 0.1;
            }
            if (pattern.regexBonus) {
              for (const re of pattern.regexBonus) {
                if (re.test(questionPart)) {
                  qScore += 0.2;
                  qGroupsHit++;
                }
              }
            }
            if (pattern.minKeywordGroups && qGroupsHit < pattern.minKeywordGroups) {
              qScore = 0;
            }
            // 循环论证要求问题文本包含"因为"（否则可能是"所以"单独出现）
            if (pattern.id === 'circularReasoning' && !/因为/.test(questionPart)) {
              qScore = 0;
            }
            // 虚假因果要求至少2组关键词或匹配到因果相关词
            if (pattern.id === 'falseCause' && qGroupsHit < 2) {
              qScore = 0;
            }
            if (qScore > 0) {
              // 检查选项是否包含谬误名
              const optLow = opt.text.toLowerCase();
              const nameParts = pattern.name.replace('谬误', '').trim().toLowerCase();
              const bonus = optLow.includes(nameParts) ? 0.5 : 0;
              score += qScore + bonus;
              reasons.push(`选项与「${pattern.name}」模式匹配(score=${(qScore + bonus).toFixed(2)})`);
            }
          }
        }
      }

      // === 规则2：概率计算题 ===
      if (questionPart.includes('概率') && questionPart.match(/\d+/)) {
        const probScore = this._evaluateProbabilityOption(questionPart, opt.text);
        if (probScore > 0) {
          score += probScore;
          reasons.push(`概率计算验证(score=${probScore})`);
        }
      }

      // === 规则3：数学计算题 ===
      if (questionPart.match(/等于|多少|平方|方程|乘以|除以|x\s*=|3x|2x/)) {
        const mathScore = this._evaluateMathOption(questionPart, opt.text);
        if (mathScore > 0) {
          score += mathScore;
          reasons.push(`数学计算验证(score=${mathScore})`);
        }
      }

      // === 规则4：演绎推理（三段论、条件推理）===
      if (reasoningType.primaryType === 'deductive') {
        const dedScore = this._evaluateDeductiveOption(questionPart, opt.text);
        if (dedScore > 0) {
          score += dedScore;
          reasons.push(`演绎推理验证(score=${dedScore})`);
        }
      }

      // === 规则5：归纳推理 ===
      if (reasoningType.primaryType === 'inductive' || (reasoningType.candidates || []).some(c => c.type === 'inductive' && c.score > 0.3)) {
        const indScore = this._evaluateInductiveOption(questionPart, opt.text);
        if (indScore > 0) {
          score += indScore;
          reasons.push(`归纳推理验证(score=${indScore})`);
        }
      }

      // === 规则6：溯因推理 ===
      if (reasoningType.primaryType === 'abductive') {
        const abdScore = this._evaluateAbductiveOption(questionPart, opt.text);
        if (abdScore > 0) {
          score += abdScore;
          reasons.push(`溯因推理验证(score=${abdScore})`);
        }
      }

      // === 规则7：条件推理（"从信息可以推出"）===
      if (questionPart.includes('可以推出') || questionPart.includes('从这个信息')) {
        const condScore = this._evaluateConditionalOption(questionPart, opt.text);
        if (condScore > 0) {
          score += condScore;
          reasons.push(`条件推理验证(score=${condScore})`);
        }
      }

      // === 规则8：统计推理 ===
      if (reasoningType.primaryType === 'statistical') {
        const statScore = this._evaluateStatisticalOption(questionPart, opt.text);
        if (statScore > 0) {
          score += statScore;
          reasons.push(`统计推理验证(score=${statScore})`);
        }
      }

      // === 规则9：通用——否定绝对化选项 ===
      if (opt.text.includes('一定') || opt.text.includes('全部') || opt.text.includes('所有')) {
        if (questionPart.includes('可能') || questionPart.includes('不一定')) {
          score -= 0.3;
          reasons.push('绝对化选项与不确定性语境不匹配(-0.3)');
        }
      }

      return { letter: opt.letter, text: opt.text, score, reasons };
    });

    // 选最高分
    scored.sort((a, b) => b.score - a.score);
    const best = scored[0];

    // 置信度：最高分 - 次高分
    const secondBestScore = scored.length > 1 ? scored[1].score : 0;
    let confidence = Math.min(Math.max(best.score - secondBestScore + 0.3, 0.1), 1.0);

    // ─── LLM兜底 ──────────────────────────────────────
    // 当所有规则都打0分时，调LLM推理
    if (best.score < 0.1 && this._llmFallback) {
      try {
        const llmResult = await this._llmFallback(input, options, reasoningType);
        if (llmResult && llmResult.selectedAnswer) {
          const llmLetter = llmResult.selectedAnswer;
          // 找到LLM选的选项，给它加分
          const llmOpt = scored.find(s => s.letter === llmLetter);
          if (llmOpt) {
            llmOpt.score = 0.6;
            llmOpt.reasons.push('LLM兜底推理(conf=0.6)');
            // 重新排序
            scored.sort((a, b) => b.score - a.score);
            confidence = 0.5;
          }
        }
      } catch(e) {
        // LLM失败，保持原结果
      }
    }
    // 重新获取best
    const finalBest = scored[0];
    const finalSecondBestScore = scored.length > 1 ? scored[1].score : 0;

    return {
      hasOptions: true,
      options: options.map(o => o.letter),
      selectedAnswer: finalBest.score > 0 ? finalBest.letter : null,
      confidence: Math.round(confidence * 100) / 100,
      bestOption: finalBest.letter,
      bestScore: Math.round(finalBest.score * 100) / 100,
      secondBest: scored.length > 1 ? { letter: scored[1].letter, score: Math.round(scored[1].score * 100) / 100 } : null,
      reason: finalBest.score > 0 ? finalBest.reasons.join('; ') : '无法确定',
      allScores: scored.map(s => ({ letter: s.letter, score: Math.round(s.score * 100) / 100 })),
      llmFallback: best.score < 0.1 ? true : false,
      // [v5.17.16 M2] 主动推理EFE原型 — 探索(epistemic) vs 利用(pragmatic)
      // EFE = 实用价值(偏好满足) + 认知价值(信息增益)
      activeInference: {
        exploitation: +(confidence / 100).toFixed(3),
        exploration: +(1 - confidence / 100 - (scored.length > 1 ? scored[1].score / 100 : 0)).toFixed(3),
        needsMoreEvidence: confidence < 50 || (scored.length > 1 && scored[1].score > finalBest.score * 0.7),
      },
    };
  }

  /**
   * 辅助：评估选项是否匹配某个谬误模式
   */
  _scoreOptionForFallacy(question, optionText, pattern) {
    const qLow = question.toLowerCase();
    const oLow = optionText.toLowerCase();
    let score = 0;

    // 检查问题文本是否与谬误模式匹配
    let qGroupsHit = 0;
    for (const kwGroup of pattern.keywords) {
      const { hits } = _matchKeywords(question, kwGroup);
      if (hits > 0) qGroupsHit++;
    }
    
    if (pattern.regexBonus) {
      for (const re of pattern.regexBonus) {
        if (re.test(question)) score += 0.15;
      }
    }

    if (pattern.minKeywordGroups && qGroupsHit >= pattern.minKeywordGroups) {
      score += 0.3;
    }

    // 检查选项文本是否包含谬误名
    const nameParts = pattern.name.replace('谬误', '').trim().toLowerCase();
    if (oLow.includes(nameParts)) {
      score += 0.4;
    }

    return Math.min(score, 0.8);
  }

  /**
   * 辅助：评估概率选项
   */
  _evaluateProbabilityOption(question, optionText) {
    // 提取数字和分数
    const fracMatch = optionText.match(/(\d+)\/(\d+)/);
    if (!fracMatch) return 0;

    const num = parseInt(fracMatch[1], 10);
    const den = parseInt(fracMatch[2], 10);
    if (den === 0) return 0;

    const value = num / den;
    let score = 0;

    // 掷硬币3次都正面 = 1/8
    if (question.includes('硬币') && question.includes('3次') || question.includes('三次')) {
      if (Math.abs(value - 0.125) < 0.01) score = 0.5;
    }
    
    // 3红2蓝抽红球 = 3/5
    if (question.includes('红球') && question.includes('蓝球')) {
      if (Math.abs(value - 0.6) < 0.01) score = 0.5;
    }

    return score;
  }

  /**
   * 辅助：评估数学选项
   */
  _evaluateMathOption(question, optionText) {
    const q = question.replace(/[？?]/g, '');
    let score = 0;

    // x² - x = 0 → x(x-1)=0 → x=0或x=1
    if (q.includes('平方减去它本身') || q.includes('平方减去本身')) {
      if (optionText.includes('0或1') || optionText.includes('0 或 1')) score = 0.5;
      if (optionText.includes('0') && optionText.includes('1') && !optionText.includes('-')) score = 0.4;
    }

    // 3x + 7 = 22 → x = 5
    if (q.includes('3x') && q.includes('7') && (q.includes('22') || q.includes('='))) {
      if (optionText === '5' || optionText.includes('5')) score = 0.5;
    }

    return score;
  }

  /**
   * 辅助：评估演绎推理选项
   */
  _evaluateDeductiveOption(question, optionText) {
    let score = 0;

    // 所有A都是B，所有B都是C → 所有A都是C
    if (question.includes('所有A') && question.includes('所有B') && (question.includes('都是C') || question.includes('所有C'))) {
      if (optionText.includes('A都是C')) score = 0.6;
    }

    // 没有鸟是哺乳动物，所有企鹅都是鸟 → 没有企鹅是哺乳动物
    if (question.includes('没有鸟') && question.includes('企鹅')) {
      if (optionText.includes('没有企鹅') || optionText.includes('没有企鹅是')) score = 0.6;
      if (optionText.includes('企鹅不是哺乳')) score = 0.3;
    }

    // 所有擅长数学的人都是逻辑思维强的，有些工程师擅长数学 → 有些工程师是逻辑思维强的
    if (question.includes('擅长数学') && question.includes('工程师')) {
      if (optionText.includes('有些工程师') && (optionText.includes('逻辑') || optionText.includes('思维强'))) score = 0.6;
    }

    // 如果下雨，地面会湿。现在下雨了 → 地面一定湿
    if (question.includes('下雨') && question.includes('地面会湿') && question.includes('下雨了')) {
      if (optionText.includes('一定湿') || optionText.includes('地面一定')) score = 0.5;
    }

    // 如果这个动物是狗，它会汪汪叫。没有汪汪叫 → 不是狗
    if (question.includes('狗') && question.includes('汪汪叫') && question.includes('没有汪汪')) {
      if (optionText.includes('不是狗')) score = 0.5;
    }

    // 如果今天是周一，明天是周二。明天是周二 → 今天可能是周一
    if (question.includes('周一') && question.includes('周二') && question.includes('明天是周二')) {
      if (optionText.includes('可能') && optionText.includes('周一')) score = 0.5;
    }

    // 只有年满18岁才能投票。小王没有投票 → 小王可能未满18岁
    if (question.includes('投票') && question.includes('18') && question.includes('没有投票')) {
      if (optionText.includes('可能')) score = 0.5;
      if (optionText.includes('一定') || optionText.includes('肯定')) score = 0.2;
    }

    // ─── 空间关系推理 ────────────────────────────────────
    // 支持三种格式：
    //   (a) "X is to the right/left of Y"（空间关系）
    //   (b) "X is the rightmost/leftmost"（直接位置）
    //   (c) "X is the second from the left"（直接位置）
    const qLow = question.toLowerCase();
    const items = new Set();

    // 1. 提取所有物品（从"three books: a X, a Y, and a Z"）
    const itemMatch = qLow.match(/(?:three|four|five)\s+(?:books?|items?|objects?|things?)[:\s]+(.+?)(?:\.|$)/);
    const itemNames = [];
    if (itemMatch) {
      const listStr = itemMatch[1];
      const parts = listStr.split(/,|\band\b/);
      for (const p of parts) {
        const name = p.replace(/\ba\s+/g, '').replace(/\ban\s+/g, '').trim();
        if (name) itemNames.push(name);
      }
    }
    for (const n of itemNames) items.add(n);

    // 2. 提取空间关系
    const spatialMatches = question.match(/(\w+\s+\w+)\s+is\s+to\s+the\s+(right|left)\s+of\s+(?:the\s+)?(\w+\s+\w+)/gi);
    const rightOf = {};
    const leftOf = {};

    if (spatialMatches) {
      for (const m of spatialMatches) {
        const parts = m.match(/(\w+\s+\w+)\s+is\s+to\s+the\s+(right|left)\s+of\s+(?:the\s+)?(\w+\s+\w+)/i);
        if (parts) {
          const x = parts[1].toLowerCase();
          const dir = parts[2].toLowerCase();
          const y = parts[3].toLowerCase();
          items.add(x);
          items.add(y);
          if (dir === 'right') { leftOf[x] = y; rightOf[y] = x; }
          else { rightOf[x] = y; leftOf[y] = x; }
        }
      }
    }

    // 3. 提取直接位置陈述
    // "X is the leftmost" / "X is the rightmost"
    const posMatches = question.match(/(\w+\s+\w+)\s+is\s+(?:the\s+)?(leftmost|rightmost|second\s+from\s+the\s+left|second\s+from\s+the\s+right|third\s+from\s+the\s+left|third\s+from\s+the\s+right|middle)/gi);
    const fixedPositions = {}; // item -> position

    if (posMatches) {
      for (const pm of posMatches) {
        const parts = pm.match(/(\w+\s+\w+)\s+is\s+(?:the\s+)?(leftmost|rightmost|second\s+from\s+the\s+left|second\s+from\s+the\s+right|middle)/i);
        if (parts) {
          const item = parts[1].toLowerCase();
          const pos = parts[2].toLowerCase();
          items.add(item);
          fixedPositions[item] = pos;
        }
      }
    }

    // 4. 如果有足够信息，建立完整排序
    if (items.size >= 3) {
      // 从直接位置信息推导关系
      for (const [item, pos] of Object.entries(fixedPositions)) {
        // 给其他未定位的物品设置关系提示
        if (pos === 'leftmost') {
          // leftmost 左边没有东西
        } else if (pos === 'rightmost') {
          // rightmost 右边没有东西
        } else if (pos === 'second from the left') {
          // 这个物品左边有一个，右边有一个
        }
      }

      // 计算排序 — 优先使用 fixedPositions 修正
      let leftmost = null;
      let rightmost = null;
      for (const item of items) {
        const fp = fixedPositions[item];
        if (fp === 'leftmost') leftmost = item;
        if (fp === 'rightmost') rightmost = item;
      }
      // 如果没有 fixedPositions 信息，从空间关系推导
      // 注意：排除已在 fixedPositions 中声明位置的物品（如 second_from_left）
      if (!leftmost) {
        for (const item of items) {
          if (fixedPositions[item]) continue; // 已有固定位置，不参与 leftmost 推导
          if (!leftOf[item]) { leftmost = item; break; }
        }
        // 如果所有物品都有 fixedPositions，从 items 中找
        if (!leftmost) {
          for (const item of items) {
            if (fixedPositions[item] === 'second from the left') continue;
            if (!leftOf[item]) { leftmost = item; break; }
          }
        }
      }
      if (!rightmost) {
        for (const item of items) {
          if (fixedPositions[item]) continue; // 已有固定位置，不参与 rightmost 推导
          if (!rightOf[item]) { rightmost = item; break; }
        }
        if (!rightmost) {
          for (const item of items) {
            if (fixedPositions[item] === 'second from the right') continue;
            if (!rightOf[item]) { rightmost = item; break; }
          }
        }
      }

      const sorted = [];
      let cur = leftmost;
      while (cur) {
        sorted.push(cur);
        cur = rightOf[cur];
      }

      // 如果 sorted 长度不够，尝试从固定位置补全
      if (sorted.length < items.size) {
        // 用固定位置信息来补全
        for (const [item, pos] of Object.entries(fixedPositions)) {
          if (pos === 'leftmost' && !sorted.includes(item)) {
            sorted.unshift(item);
          }
          if (pos === 'rightmost' && !sorted.includes(item)) {
            sorted.push(item);
          }
        }
        // 如果 sorted 还是不够，用 rightOf 链从 leftmost 遍历所有物品
        if (sorted.length < items.size) {
          const allSorted = [];
          let cur = leftmost;
          const visited = new Set();
          while (cur && !visited.has(cur)) {
            visited.add(cur);
            allSorted.push(cur);
            if (rightOf[cur]) {
              cur = rightOf[cur];
            } else {
              break;
            }
          }
          // 如果 allSorted 还没覆盖所有物品，补入缺失的
          const allRemaining = [...items].filter(x => !allSorted.includes(x));
          // 优先补入 fixedPositions 中已知位置的物品
          const knownRemaining = allRemaining.filter(r => fixedPositions[r]);
          const unknownRemaining = allRemaining.filter(r => !fixedPositions[r]);
          for (const r of [...knownRemaining, ...unknownRemaining]) {
            let inserted = false;
            // 1) 检查 fixedPositions 中的位置声明
            if (fixedPositions[r] === 'second from the left' && allSorted.length >= 1) {
              allSorted.splice(1, 0, r);
              inserted = true;
            } else if (fixedPositions[r] === 'second from the right' && allSorted.length >= 1) {
              allSorted.splice(allSorted.length - 1, 0, r);
              inserted = true;
            }
            if (!inserted) {
              // 2) 检查 r 在 rightOf/leftOf 链中的位置
              for (let i = 0; i < allSorted.length; i++) {
                if (rightOf[r] === allSorted[i]) {
                  allSorted.splice(i, 0, r);
                  inserted = true;
                  break;
                }
                if (leftOf[r] === allSorted[i]) {
                  allSorted.splice(i + 1, 0, r);
                  inserted = true;
                  break;
                }
              }
            }
            if (!inserted) {
              // 3) 检查 allSorted 中的物品是否与 r 有空间关系
              for (let i = 0; i < allSorted.length; i++) {
                if (rightOf[allSorted[i]] === r) {
                  allSorted.splice(i + 1, 0, r);
                  inserted = true;
                  break;
                }
                if (leftOf[allSorted[i]] === r) {
                  allSorted.splice(i, 0, r);
                  inserted = true;
                  break;
                }
              }
            }
            if (!inserted) {
              // 4) 最后兜底：对于 3 物品的题目，检查物品的位置
              if (allSorted.length === 2 && items.size === 3) {
                // 检查 fixedPositions 中的位置声明
                if (fixedPositions[r] === 'rightmost') {
                  allSorted.push(r);
                } else if (fixedPositions[r] === 'leftmost') {
                  allSorted.unshift(r);
                } else if (fixedPositions[allSorted[1]] && fixedPositions[allSorted[1]] !== 'rightmost') {
                  // allSorted[1] 有固定位置但不是 rightmost（如 second_from_left）
                  // 说明 allSorted[1] 不是 rightmost，r 应该是 rightmost
                  allSorted.push(r);
                } else if (!rightOf[allSorted[1]] && !fixedPositions[allSorted[1]]) {
                  // allSorted[1] 没有 rightOf 也没有固定位置 → 可能是 rightmost
                  // r 是中间物品
                  allSorted.splice(1, 0, r);
                } else {
                  allSorted.splice(1, 0, r);
                }
              } else {
                allSorted.push(r);
              }
            }
          }
          sorted.length = 0;
          sorted.push(...allSorted);
        }
      }
      const optLow = optionText.toLowerCase();
      const optItem = itemNames.find(n => optLow.includes(n));

      // 匹配选项：先用 sorted（如果有3个），否则用固定位置
      if (sorted.length >= 3) {
        if (optLow.includes('leftmost')) {
          if (optItem && optItem === sorted[0]) score = 0.7;
        } else if (optLow.includes('rightmost')) {
          if (optItem && optItem === sorted[sorted.length - 1]) score = 0.7;
        } else if (optLow.includes('second from the left') || optLow.includes('second from left')) {
          if (optItem && sorted.length >= 2 && optItem === sorted[1]) score = 0.7;
        } else if (optLow.includes('third from the left') || optLow.includes('third from left')) {
          if (optItem && sorted.length >= 3 && optItem === sorted[2]) score = 0.7;
        } else if (optLow.includes('second from the right') || optLow.includes('second from right')) {
          if (optItem && sorted.length >= 2 && optItem === sorted[sorted.length - 2]) score = 0.7;
        } else if (optLow.includes('middle')) {
          const midIdx = Math.floor(sorted.length / 2);
          if (optItem && optItem === sorted[midIdx]) score = 0.7;
        }
      } else {
        // sorted < 3：使用 fixedPositions + 空间关系推断
        if (optLow.includes('leftmost')) {
          for (const [item, pos] of Object.entries(fixedPositions)) {
            if (pos === 'leftmost' && optItem && optItem === item) {
              score = 0.7; break;
            }
          }
          // 如果物品在 fixedPositions 中但不是 leftmost，排除
          if (score === 0 && optItem && Object.keys(fixedPositions).includes(optItem)) {
            // second_from_left/second_from_right 不是 leftmost
          } else {
            // optItem 在 sorted 第0位（明确是最左）
            if (score === 0 && optItem && sorted.length >= 1 && optItem === sorted[0]) {
              score = 0.7;
            }
            // 只有明确知道没有物品在它左边时才判 leftmost
            if (score === 0 && optItem && !rightOf[optItem] && sorted.length >= 2) {
              // 排除 fixedPositions 中声明为 rightmost 的物品
              const isRightmostDeclared = Object.entries(fixedPositions)
                .some(([k, p]) => p === 'rightmost' && k === optItem);
              if (!isRightmostDeclared) score = 0.4;
            }
          }
        } else if (optLow.includes('rightmost')) {
          for (const [item, pos] of Object.entries(fixedPositions)) {
            if (pos === 'rightmost' && optItem && optItem === item) {
              score = 0.7; break;
            }
          }
          // 如果物品在 fixedPositions 中但不是 rightmost，排除
          if (score === 0 && optItem && Object.keys(fixedPositions).includes(optItem)) {
            // second_from_left/second_from_right 不是 rightmost
          } else {
            // optItem 在 sorted 最后一位（明确是最右）
            if (score === 0 && optItem && sorted.length >= 1 && optItem === sorted[sorted.length - 1]) {
              score = 0.7;
            }
            if (score === 0 && optItem && !leftOf[optItem] && sorted.length >= 2) {
              // 排除 fixedPositions 中声明为 leftmost 的物品
              const isLeftmostDeclared = Object.entries(fixedPositions)
                .some(([k, p]) => p === 'leftmost' && k === optItem);
              if (!isLeftmostDeclared) score = 0.4;
            }
          }
        } else if (optLow.includes('second from the left') || optLow.includes('second from left')) {
          // 情况1：leftmost 和 rightmost 都在 fixedPositions 中
          const leftmostItem = Object.entries(fixedPositions).find(([,p]) => p === 'leftmost');
          const rightmostItem = Object.entries(fixedPositions).find(([,p]) => p === 'rightmost');
          if (leftmostItem && rightmostItem && optItem) {
            if (optItem !== leftmostItem[0] && optItem !== rightmostItem[0]) {
              score = 0.7;
            }
          }
          // 情况2a：sorted 有2个，sorted[1] 就是第二左
          if (score === 0 && sorted.length >= 2 && optItem && optItem === sorted[1]) {
            score = 0.7;
          }
          // 情况2b：sorted 有2个物品，总物品3个，中间是缺失的那个（只在 sorted[1] 不是正确选项时）
          if (score === 0 && sorted.length === 2 && items.size === 3) {
            const missing = [...items].find(x => !sorted.includes(x));
            if (missing && optItem === missing) score = 0.7;
          }
          // 情况3：sorted 有1个 + fixedPositions 有 rightmost，中间是第三个
          if (score === 0 && sorted.length === 1 && items.size === 3) {
            const leftPos = sorted[0];
            const rightPos = Object.entries(fixedPositions).find(([,p]) => p === 'rightmost')?.[0];
            if (leftPos && rightPos && optItem) {
              if (optItem !== leftPos && optItem !== rightPos) score = 0.7;
            }
          }
          // 情况4：sorted 有1个 + fixedPositions 有 leftmost，sorted[0] 是 rightmost? 
          // 不对——sorted[0] 是从 leftmost 开始建的，sorted[0] 应该是 leftmost
          // 所以情况4应该是：sorted 有1个（即 leftmost），fixedPositions 有 leftmost，找中间物品
          if (score === 0 && sorted.length === 1 && items.size === 3) {
            const fixedLeftmost = Object.entries(fixedPositions).find(([,p]) => p === 'leftmost')?.[0];
            if (fixedLeftmost && optItem) {
              // sorted[0] 是 leftmost，optItem 不是 leftmost 也不是 rightmost（rightmost 未知，但不在 sorted 中）
              // 此时 leftmost = sorted[0] = fixedLeftmost
              // 第二个物品 = ? (从空间关系推导或 default)
              // 先检查是否有 rightmost 通过 !leftOf 推导
              let deducedRightmost = null;
              for (const it of items) {
                if (it !== fixedLeftmost && !leftOf[it]) { deducedRightmost = it; break; }
              }
              if (deducedRightmost) {
                if (optItem !== fixedLeftmost && optItem !== deducedRightmost) score = 0.7;
              }
            }
          }
        }
      }
    }

    return score;
  }

  /**
   * 辅助：评估归纳推理选项
   */
  _evaluateInductiveOption(question, optionText) {
    let score = 0;

    // 100只天鹅全部白色 → 很可能所有天鹅都是白色
    if (question.includes('天鹅') && (question.includes('100') || question.includes('全部白色') || question.includes('全部是白色'))) {
      if (optionText.includes('很可能') || optionText.includes('可能')) score = 0.5;
      if (optionText.includes('一定') || optionText.includes('全部') || optionText.includes('100%')) score = -0.2;
    }

    // 过去10年每年6月下雨 → 很可能下雨
    if (question.includes('10年') && question.includes('6月') && question.includes('下雨')) {
      if (optionText.includes('很可能') || optionText.includes('可能')) score = 0.5;
      if (optionText.includes('一定')) score = -0.2;
    }
    
    // 过去N年每年都... → 很可能
    if ((question.includes('过去') || question.includes('每年')) && (question.includes('都') || question.includes('全部'))) {
      if (optionText.includes('很可能') || optionText.includes('可能')) score = 0.5;
      if (optionText.includes('一定') || optionText.includes('全部') || optionText.includes('100%')) score = -0.2;
    }

    return Math.max(score, 0);
  }

  /**
   * 辅助：评估溯因推理选项
   */
  _evaluateAbductiveOption(question, optionText) {
    let score = 0;

    // 草地湿 → 最可能下雨
    if (question.includes('草地') && question.includes('湿')) {
      if (optionText.includes('下雨')) score = 0.5;
    }

    // 电脑无法开机，风扇不转，没有指示灯 → 电源没插好
    if (question.includes('电脑') && question.includes('无法开机') && question.includes('风扇')) {
      if (optionText.includes('电源') || optionText.includes('插')) score = 0.5;
    }

    return score;
  }

  /**
   * 辅助：评估条件推理选项
   */
  _evaluateConditionalOption(question, optionText) {
    let score = 0;

    // 如果今天是周一，明天是周二。明天是周二 → 今天可能是周一
    if (question.includes('周一') && question.includes('周二') && question.includes('明天是周二')) {
      if (optionText.includes('可能') && optionText.includes('周一')) score = 0.5;
      if (optionText.includes('是周一') && !optionText.includes('可能')) score = 0.2; // 肯定不对，可能才对
    }

    // 只有年满18岁才能投票。小王没有投票 → 小王可能未满18岁
    if (question.includes('投票') && question.includes('18') && question.includes('没有投票')) {
      if (optionText.includes('可能') && optionText.includes('18')) score = 0.5;
      if (optionText.includes('未满')) score = 0.4;
    }

    return score;
  }

  /**
   * 辅助：评估统计推理选项
   */
  _evaluateStatisticalOption(question, optionText) {
    let score = 0;

    // 准确率99% vs 90%，但90%更可靠 → 样本偏差
    if (question.includes('准确率') && question.includes('99%') && question.includes('90%')) {
      if (optionText.includes('样本') || optionText.includes('偏差')) score = 0.6;
      if (optionText.includes('假阳性')) score = 0.3;
    }

    return score;
  }

  /**
   * LLM兜底推理 — 当规则引擎打0分时，调LLM做选择题推理
   * [P-005] routed through safeFetch — SSRF protection + timeout
   */
  async _llmFallback(input, options, reasoningType) {
    // 构建简洁的英文 prompt
    const qPart = input.replace(/\n[A-D][.、．)）].+/g, '').trim();
    const optLines = input.match(/\n[A-D][.、．)）].+/g);
    const optText = optLines ? optLines.join('\n') : '';
    const prompt = `Answer A, B, C, or D. Only output the letter.\n\n${qPart}\n${optText}\n\nAnswer:`;

    const body = JSON.stringify({
      model: 'deepseek-v4-flash',
      messages: [{ role: 'user', content: prompt }],
      temperature: 0.1,
      max_tokens: 10,
      stream: true,
    });

    try {
      const { safeFetch } = require('../core/fetch-safe.js');

      // [SECURITY FIX H-2] API key from env only — no file fallback
      const apiKey = process.env.HEARTFLOW_API_KEY;
      if (!apiKey) {
        throw new Error('[logic-reasoning] HEARTFLOW_API_KEY environment variable is required');
      }

      // [v5.17.9 H1] host白名单 — 仅允许腾讯Copilot域名，防止env注入外泄API Key
      const ALLOWED_API_HOSTS = ['copilot.tencent.com', 'api.tencent.com'];
      const apiBase = process.env.TENCENT_API_BASE;
      let finalBase = 'https://copilot.tencent.com/v2';
      if (apiBase) {
        try {
          const parsed = new URL(apiBase);
          if (!ALLOWED_API_HOSTS.some(h => parsed.hostname === h || parsed.hostname.endsWith('.' + h))) {
            throw new Error(`TENCENT_API_BASE host "${parsed.hostname}" not in allowed list`);
          }
          finalBase = apiBase;
        } catch(e) {
          console.warn('[logic-reasoning] TENCENT_API_BASE rejected:', e.message, '— using default');
        }
      }
      const url = finalBase + '/chat/completions';

      const res = await safeFetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + apiKey,
        },
        body,
        timeout: 15000,
      });

      const raw = await res.text();
      let content = '';
      for (const line of raw.split('\n')) {
        if (line.startsWith('data: ')) {
          const d = line.slice(6);
          if (d.trim() === '[DONE]') break;
          try {
            const obj = JSON.parse(d);
            const delta = obj?.choices?.[0]?.delta?.content || '';
            content += delta;
          } catch (_) { /* [v5.9.18] intentional: graceful degradation */ }
        }
      }
      const letter = content.trim().toUpperCase().match(/[A-D]/);
      return letter ? { selectedAnswer: letter[0] } : null;
    } catch (e) {
      return null;
    }
  }


  getStats() {
    return {
      version: this.version,
      totalAnalyses: this._history.length,
      reasoningTypes: REASONING_PATTERNS.length,
      fallacyTypes: FALLACY_PATTERNS.length,
      frameworks: Object.keys(FRAMEWORKS).length,
    };
  }

  getHistory() {
    return this._history.slice(-10);
  }
}

// [v5.17.9 H1] P0已修复: host白名单校验 + safeFetch SSRF防护
module.exports = { LogicReasoning };
