// atomic-write.mjs — Crash-safe writes via tmp+rename.
// Zero dependencies. Defends against half-written files; readers either see
// the previous version or the new one, never a partial.

import { writeFileSync, renameSync, unlinkSync } from 'node:fs';
import { randomInt } from 'node:crypto';

/**
 * Atomically write content to filePath via a tmp file + rename.
 * @param {string} filePath — target absolute path
 * @param {string|Buffer} content — bytes to write
 */
export function atomicWriteSync(filePath, content) {
  if (!filePath || typeof filePath !== 'string') {
    throw new Error('atomicWriteSync: filePath required');
  }
  const tmp = `${filePath}.tmp.${process.pid}.${randomInt(0, 2 ** 32)}`;
  try {
    writeFileSync(tmp, content);
    try {
      renameSync(tmp, filePath);
    } catch (err) {
      // Windows ERROR_ALREADY_EXISTS / EPERM defensive fallback.
      if (err && (err.code === 'EEXIST' || err.code === 'EPERM')) {
        try {
          unlinkSync(filePath);
        } catch {
          // best-effort
        }
        renameSync(tmp, filePath);
      } else {
        throw err;
      }
    }
  } catch (err) {
    // Best-effort cleanup of the tmp file on failure.
    try {
      unlinkSync(tmp);
    } catch {
      // best-effort
    }
    throw err;
  }
}

/**
 * Atomically write a JSON-serialized object with 2-space indent.
 * @param {string} filePath
 * @param {unknown} obj
 */
export function atomicWriteJson(filePath, obj) {
  atomicWriteSync(filePath, JSON.stringify(obj, null, 2) + '\n');
}
