#!/usr/bin/env node
/**
 * plan-steps-parse (C2, §5) — план markdown → [{op,object,ref}]. fail-CLOSE на любой брак.
 * Reject op:'Task' (VA-4). Канон object: файлы → repo-relative POSIX (SE-5). ref обязателен (SE-1).
 */
const BLOCK_RE = /```steps-json\s*\n([\s\S]*?)\n```/;
const FILE_OPS = new Set(['Edit', 'Write', 'MultiEdit', 'NotebookEdit']);

/** Достать содержимое блока ```steps-json``` или null. */
export function extractStepsBlock(md) {
  const m = String(md == null ? '' : md).match(BLOCK_RE);
  return m ? m[1] : null;
}

/**
 * Канон формы object для файловых op — repo-relative POSIX (SE-5): только backslash→slash,
 * БЕЗ абсолютизации. Стабильная, cwd-независимая форма для хранения в печати; реальная
 * нормализация для матча — pathNormalize в actionMatchesStep (plan-lock.mjs) на ОБЕ стороны
 * (шаг И событие) во время матча. Класть сюда pathNormalize нельзя: он cwd-зависим/абсолютен,
 * печать ставится в cwd хука-судьи, а матч — в cwd стены (рассинхрон).
 */
function canonObject(op, object) {
  const s = String(object ?? '');
  return FILE_OPS.has(op) ? s.replace(/\\/g, '/') : s;
}

/** Распарсить + провалидировать шаги. Бросает на брак (fail-CLOSE). */
export function parsePlanSteps(md) {
  const raw = extractStepsBlock(md);
  if (raw == null) throw new Error('plan-steps-parse: нет блока ```steps-json``` (fail-CLOSE)');
  let arr;
  try { arr = JSON.parse(raw); } catch { throw new Error('plan-steps-parse: блок не валидный JSON (fail-CLOSE)'); }
  if (!Array.isArray(arr) || arr.length === 0) throw new Error('plan-steps-parse: ожидался непустой массив');
  return arr.map((s, i) => {
    if (!s || typeof s !== 'object') throw new Error(`plan-steps-parse: шаг ${i} не объект`);
    const op = String(s.op || '');
    const object = String(s.object ?? '');
    const ref = String(s.ref ?? '');
    if (!op) throw new Error(`plan-steps-parse: шаг ${i} без op`);
    if (op === 'Task') throw new Error('plan-steps-parse: op:Task запрещён (субагенты, VA-4)');
    if (!object.trim()) throw new Error(`plan-steps-parse: шаг ${i} без object`);
    if (!ref.trim()) throw new Error(`plan-steps-parse: шаг ${i} без ref (closed-door, SE-1)`);
    return { op, object: canonObject(op, object), ref };
  });
}
