#!/usr/bin/env node
/**
 * criterion-green (Машина 5 Пакет 5, Блок 3, 5.6) — по-критерийный производитель GREEN.
 *
 * Для этой цели заменяет «весь-прогон-разом» enforce-verify-record: эмитит подписанный
 * подписантом GREEN ТОЛЬКО при настоящем зелёном прогоне конкретного критерия. Два условия
 * настоящего green (оба обязательны):
 *   - testPassed: тест изменённого шага реально прошёл;
 *   - mutationKilled (P18): сломали изменённый код — тест ОБЯЗАН покраснеть; выжил зелёным →
 *     тест ничего не проверяет → НЕ green.
 * Иначе green:false с причиной (mutation-survived / test-not-passed / no-signer-key) — fail-CLOSE.
 *
 * Подпись — над аутентифицирующей тройкой {criterion_id, code_fingerprint, occurrence} (floor-signer,
 * домен M5_GREEN). green/coverage_of_changed — поля записи; подлинность даёт подпись (Δ5),
 * свежесть — отпечаток (Δ2). Чистый модуль: факты прогона (testPassed/mutationKilled/отпечаток/
 * покрытие) и ключ подписанта инъектируются; никакого fs/исполнения тестов здесь (это — живая
 * обёртка под активацией владельца). code_fingerprint считается отдельно через codeFingerprint.
 */
import { createHash } from 'node:crypto';
import { canonicalJson } from './receipt-sign.mjs';
import { signGreen } from './floor-signer.mjs';

/**
 * Δ2: отпечаток «по делу» = sha256 канонизированной карты {путь: содержимое} изменённых
 * файлов шага + тест-файлов, которые их прогоняли. Граф импортов НЕ считаем (кросс-язык —
 * дорого/хрупко; надёжность даёт мутация P18). Правка любого из этих файлов после прогона →
 * другой отпечаток → green аннулируется fingerprintFresh. Детерминирован (canonicalJson сортит
 * ключи — порядок файлов не влияет).
 */
export function codeFingerprint(fileContents = {}) {
  return createHash('sha256').update(canonicalJson(fileContents)).digest('hex');
}

/**
 * Произвести по-критерийный GREEN. Настоящий green ⇔ testPassed && mutationKilled && есть ключ.
 * @returns настоящий green: {criterion_id, code_fingerprint, occurrence, sig, green:true, coverage_of_changed}
 *          иначе: {criterion_id, green:false, code_fingerprint, coverage_of_changed, occurrence, reason}
 */
export function produceGreen({
  criterion_id,
  occurrence,
  code_fingerprint,
  coverage_of_changed = null,
  testPassed = false,
  mutationKilled = false,
  signerKey,
} = {}) {
  if (testPassed !== true) {
    return { criterion_id, green: false, code_fingerprint, coverage_of_changed, occurrence, reason: 'test-not-passed' };
  }
  if (mutationKilled !== true) {
    // P18: тест прошёл, но мутация выжила — тест не проверяет изменённый код → не green.
    return { criterion_id, green: false, code_fingerprint, coverage_of_changed, occurrence, reason: 'mutation-survived' };
  }
  const receipt = signGreen({ criterion_id, code_fingerprint, occurrence }, signerKey);
  if (!receipt) {
    // Нет ключа подписанта → подлинный green произвести нельзя (fail-CLOSE).
    return { criterion_id, green: false, code_fingerprint, coverage_of_changed, occurrence, reason: 'no-signer-key' };
  }
  return { ...receipt, green: true, coverage_of_changed };
}
