/**
 * atomic-write.ts — tmp→rename atomic file write with ENOENT recovery
 * Pattern from abtop (MIT)
 * Deps: fs/promises, path (Node built-ins)
 */

import { writeFile, rename, unlink, mkdir } from "fs/promises"
import { join, extname, dirname } from "path"

function isEnoent(e: unknown): e is { code: "ENOENT" } {
  return typeof e === "object" && e !== null && "code" in e && (e as { code: string }).code === "ENOENT"
}

export async function atomicWrite(filePath: string, content: string | Buffer, mode?: number): Promise<void> {
  const tmp = `${filePath}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`
  const doWrite = async () => {
    if (mode) {
      await writeFile(tmp, content, { mode })
    } else {
      await writeFile(tmp, content)
    }
    await rename(tmp, filePath)
  }
  try {
    await doWrite()
  } catch (e) {
    if (isEnoent(e)) {
      await mkdir(dirname(filePath), { recursive: true })
      await doWrite()
      return
    }
    try { await unlink(tmp) } catch { /* ignore */ }
    throw e
  }
}
