// tools/self-debrief-detector.mjs
/**
 * Self-debrief detector — router-gate v4.1 spec §3.12 (NEW). Закрывает F18.
 * Pure: ловит retrospective/self-analysis content в response без recent
 * self-retrospect / brain-retro Skill invocation.
 */

// NOTE: \b does not fire before Cyrillic characters in Node.js (ASCII word boundary).
// Cyrillic-leading patterns use Unicode lookbehind (?<![\p{L}\p{N}_]) to avoid
// mid-word false positives (e.g. судь*я*, семь*я*).  All such patterns carry the
// `u` flag which is required for \p{…} to work (Node 20+).
// Pattern 7 uses generali[sz] to cover both US (generalizable) and British (generalisable)
// spellings — required by the plan's own Step 1 test ("Generalisable lesson").
export const SELF_DEBRIEF_PATTERNS = [
  /(?<![\p{L}\p{N}_])я\s+проанализировал\s+(?:свои|собственные)\s+(?:паттерны|поведенческие|обходные)/iu,
  /\b(?:retrospect|self-evaluation|self-analysis|self-debrief)\b/i,
  /(?<![\p{L}\p{N}_])обобщ(?:аю|ил)\s+(?:опыт|выводы)/iu,
  /(?<![\p{L}\p{N}_])я\s+(?:заметил|обнаружил|увидел)\s+(?:паттерн|тенденцию|behavioral)/iu,
  /\bself-retro/i,
  /(?<![\p{L}\p{N}_])брэйн-ретро/iu,
  /\bgenerali[sz](?:able|ed)\s+lesson\b/i,
  /(?:\blesson|(?<![\p{L}\p{N}_])урок)\s+v?\d+\.\d+\s*[:—-]/iu,
];

function normSkill(name) {
  return String(name || '').trim().toLowerCase();
}

function skillMatchesAny(name, list) {
  const n = normSkill(name);
  return list.some((s) => {
    const t = normSkill(s);
    return n === t || n.endsWith(':' + t) || t.endsWith(':' + n);
  });
}

export function extractSkillCallsLastNTurns(transcript, n) {
  const recs = transcript || [];
  let scoped = recs;
  const withTurn = recs.filter((r) => typeof r.turn === 'number');
  if (withTurn.length > 0) {
    const maxTurn = Math.max(...withTurn.map((r) => r.turn));
    scoped = recs.filter((r) => typeof r.turn !== 'number' || r.turn > maxTurn - n);
  }
  const out = [];
  for (const rec of scoped) {
    if (rec.type === 'tool_use' && rec.name === 'Skill') {
      out.push({ skill_name: normSkill(rec.input?.skill ?? rec.input?.command ?? '') });
    }
  }
  return out;
}

export function detectSelfDebrief(controllerResponseText, transcript, opts = {}) {
  const { recentTurns = 30, selfRetroSkills = ['self-retrospect', 'brain-retro'] } = opts;
  const text = String(controllerResponseText || '');
  const matched = SELF_DEBRIEF_PATTERNS.some((re) => re.test(text));
  if (!matched) return { action: 'allow' };

  const recent = extractSkillCallsLastNTurns(transcript, recentTurns);
  const selfRetroInvoked = recent.some((c) => skillMatchesAny(c.skill_name, selfRetroSkills));
  if (selfRetroInvoked) return { action: 'allow' };

  return {
    action: 'hard_block_next_mutating',
    reason: 'v4.1 self-debrief hard-block: response содержит retrospective/self-analysis content без recent self-retrospect или brain-retro Skill invocation. Invoke matching Skill для honest captured retrospect, не inline narrative analysis.',
  };
}
