#!/usr/bin/env node
/**
 * router-emerge — лёгкий узкий per-ответ проход «всплывшая нужда» (spec 2026-06-05
 * §3): после КАЖДОГО ответа роутер смотрит ТОЛЬКО последний ответ + план и ищет
 * озвученную нужду, которой в плане нет (не полный пересчёт — дёшево). При батче
 * ответов мелочь тонет; отдельный взгляд на каждый ответ даёт чистый шанс поймать.
 * РАССУЖДЕНИЕ — у модели (llmCall мокается); проверки вокруг — механические.
 */
import { parseRouterResponse } from './router-engine.mjs';

function isNonEmptyString(v) { return typeof v === 'string' && v.trim().length > 0; }

/** Построитель промпта лёгкого прохода (чистый, {system,user}). Узкий: только последний ответ + план. */
export function buildEmergePrompt({ lastAnswer = '', plan = null } = {}) {
  const planText = plan == null ? '(плана пока нет)' : (typeof plan === 'string' ? plan : JSON.stringify(plan));
  const system = [
    'Ты — РОУТЕР-НАСТАВНИК, ЛЁГКИЙ УЗКИЙ ПРОХОД (всплывшая нужда). Смотри ТОЛЬКО последний ответ владельца ниже.',
    'Вопрос один: есть ли в ЭТОМ последнем ответе озвученная нужда/просьба (напр. «сделай отступ»), которой НЕТ в плане? НЕ пересчитывай весь план — только этот ответ.',
    'Ничего нового нет → emerged пустой. Это нормально.',
    'ВЫВОД — строго JSON: {"emerged":[{"item":"…","detail":"…"}]}. item — непустой.',
  ].join('\n');
  const user = [
    '--- ПОСЛЕДНИЙ ОТВЕТ ВЛАДЕЛЬЦА ---',
    lastAnswer,
    '--- ТЕКУЩИЙ ПЛАН ---',
    planText,
  ].join('\n');
  return { system, user };
}

/** Механический валидатор: emerged — массив (может быть пуст); пункт {item непустой, detail?}. */
export function validateEmergeTrace(trace) {
  const badItems = [];
  if (!trace || typeof trace !== 'object' || !Array.isArray(trace.emerged))
    return { ok: false, badItems, reason: 'нет массива emerged' };
  trace.emerged.forEach((it, i) => {
    if (!it || typeof it !== 'object' || !isNonEmptyString(it.item)) badItems.push(`emerged[${i}]`);
  });
  return { ok: badItems.length === 0, badItems };
}

/**
 * Оркестратор лёгкого прохода: построить → llmCall (инъекция) → распарсить →
 * провалидировать форму → вернуть emerged. hasEmerged считает КОД из массива.
 */
export async function runEmergeCheck({ lastAnswer = '', plan = null, llmCall }) {
  let trace;
  try { trace = await llmCall({ buildPrompt: () => buildEmergePrompt({ lastAnswer, plan }) }); }
  catch { return { ok: false, reason: 'сбой вызова модели', trace: null }; }
  if (typeof trace === 'string') trace = parseRouterResponse(trace);
  if (!trace) return { ok: false, reason: 'пустой/неразборный ответ', trace: null };
  const v = validateEmergeTrace(trace);
  if (!v.ok) return { ok: false, reason: `невалидный emerge: ${v.reason || ''} пункты [${v.badItems.join(', ')}]`, trace };
  return { ok: true, emerged: trace.emerged, hasEmerged: trace.emerged.length > 0, trace };
}
