#!/usr/bin/env node
/**
 * keychain-read — синхронное чтение OS-keychain через async-only keytar.
 *
 * keytar НЕ имеет getPasswordSync (только async getPassword → Promise). Хуки роутер-наставника
 * читают ключ СИНХРОННО (resolveReceiptKey/resolveJudgeKey возвращают string|null без await).
 * Чтобы не делать асинхронными все гейты, читаем ключ в коротком node-subprocess: execFileSync
 * блокирует вызывающий поток, а внутри subprocess await'ит keytar.getPassword и печатает значение.
 *
 * keytar резолвится по АБСОЛЮТНОМУ пути (require.resolve относительно этого .mjs), чтобы subprocess
 * нашёл его независимо от своего cwd. Любая ошибка (нет keytar / spawn упал / пусто) → null
 * (fail-closed чтение: отсутствие ключа = неподписанная расписка невалидна на стороне verify).
 */
import { execFileSync } from 'node:child_process';
import { createRequire } from 'node:module';

/** Pure: node -e скрипт, который читает keytar.getPassword (async) и пишет значение в stdout. */
export function buildKeychainReadScript(keytarPath, service, account) {
  return `require(${JSON.stringify(keytarPath)}).getPassword(${JSON.stringify(service)},${JSON.stringify(account)})`
    + `.then(v=>{if(v!=null)process.stdout.write(String(v));}).catch(()=>{});`;
}

/** Pure: stdout subprocess → ключ или null (пусто/отсутствие → null). */
export function parseKeychainStdout(out) {
  const v = out == null ? '' : String(out);
  return v.length ? v : null;
}

/**
 * Синхронно прочитать запись keychain (service/account) через async keytar в subprocess.
 * requireImpl/exec инъектируются для тестов. Никогда не бросает → null на любой сбой.
 */
export function readKeychainSync(service, account, { requireImpl, exec } = {}) {
  try {
    const req = requireImpl || createRequire(import.meta.url);
    const keytarPath = req.resolve('keytar');
    const script = buildKeychainReadScript(keytarPath, service, account);
    const runner = exec || execFileSync;
    const out = runner(process.execPath, ['-e', script], {
      encoding: 'utf-8',
      stdio: ['ignore', 'pipe', 'ignore'],
      timeout: 5000,
    });
    return parseKeychainStdout(out);
  } catch {
    return null;
  }
}
