// tools/decomposition-detector.mjs
/**
 * Decomposition detector — router-gate v4 spec §3.8 + v4.1 (Direction 3).
 * Pure: ловит feature, разбитую на 3+ мелких prompts с overlapping keywords без plan skill.
 * v4.1: hard-block mutating at 3+ overlapping (was 5+ soft). LLM-judge verdict инъектируется.
 */
import { keywordOverlapCount, isResetMarker } from './safe-baseline-metering.mjs';

export { isResetMarker };

export const V4_1_DECOMP_THRESHOLD = Object.freeze({
  min_overlapping_prompts: 3,
  min_keyword_intersection: 3,
  window_size_prompts: 10,
  hard_block_mutating: true,
});

export function keywordIntersection(a, b) {
  return keywordOverlapCount(a, b);
}

export function appendHistory(history, entry) {
  return [...(history || []), entry];
}

export function detectDecompositionCandidate(history, currentEntry, threshold = V4_1_DECOMP_THRESHOLD) {
  const window = (history || []).slice(-threshold.window_size_prompts);
  const curKws = currentEntry.primary_keywords || [];

  const overlapping = window.filter(
    (e) => keywordOverlapCount(e.primary_keywords || [], curKws) >= threshold.min_keyword_intersection,
  );

  const anySkill = [...overlapping, currentEntry].some((e) => e.skill_invoked_this_prompt === true);

  if (overlapping.length >= threshold.min_overlapping_prompts && !anySkill) {
    // overlappingKeywords: curKws present in EVERY overlapping prompt
    const overlappingKeywords = curKws.filter((k) =>
      overlapping.every(
        (e) => (e.primary_keywords || []).map((x) => String(x).toLowerCase()).includes(String(k).toLowerCase()),
      ),
    );
    return {
      candidate: true,
      overlappingPrompts: overlapping.map((e) => e.prompt_idx),
      overlappingKeywords,
      reason: `${overlapping.length + 1} prompts overlapping keywords [${overlappingKeywords.join(', ')}] без writing-plans/brainstorming skill.`,
    };
  }
  return { candidate: false, overlappingPrompts: [], overlappingKeywords: [] };
}

export function decideDecomposition(candidate, llmVerdict, threshold = V4_1_DECOMP_THRESHOLD) {
  if (!candidate || !candidate.candidate) return { action: 'allow' };
  const verdict = typeof llmVerdict === 'string' ? llmVerdict : llmVerdict?.verdict;
  if (verdict === 'YES') {
    return {
      action: threshold.hard_block_mutating ? 'hard_block_mutating' : 'soft_flag',
      reason: `v4.1 decomp hard-block: ${candidate.reason} LLM-judge confirmed decomposition. Invoke writing-plans skill сейчас.`,
    };
  }
  // candidate but LLM says legit-distinct → soft surface only
  return { action: 'soft_flag', reason: candidate.reason };
}
