import { createHash } from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";

export function sha256(content: string): string {
  // 개행 정규화 — Windows 체크아웃(autocrlf)에서 해시가 흔들리는 것을 막는다
  return createHash("sha256").update(content.replace(/\r\n/g, "\n"), "utf8").digest("hex");
}

export async function exists(p: string): Promise<boolean> {
  try {
    await fs.access(p);
    return true;
  } catch {
    return false;
  }
}

export async function readIfExists(p: string): Promise<string | null> {
  try {
    return await fs.readFile(p, "utf8");
  } catch {
    return null;
  }
}

/** 임시 파일에 쓴 뒤 rename — 중간에 죽어도 반쯤 쓰인 파일이 남지 않는다 */
export async function writeAtomic(p: string, content: string): Promise<void> {
  await fs.mkdir(path.dirname(p), { recursive: true });
  const tmp = `${p}.${process.pid}.tmp`;
  await fs.writeFile(tmp, content, "utf8");
  await fs.rename(tmp, p);
}

/** 사용자가 고친 파일을 덮기 전에 원본을 남겨둔다 */
export async function backup(p: string): Promise<string | null> {
  const cur = await readIfExists(p);
  if (cur === null) return null;
  const dest = `${p}.orig`;
  await fs.writeFile(dest, cur, "utf8");
  return dest;
}

/** 파일을 지우고, 비게 된 상위 디렉터리를 stopAt 까지 정리한다 */
export async function removeFileAndPrune(p: string, stopAt: string): Promise<void> {
  await fs.rm(p, { force: true });
  let dir = path.dirname(p);
  const stop = path.resolve(stopAt);
  while (path.resolve(dir).startsWith(stop) && path.resolve(dir) !== stop) {
    try {
      const rest = await fs.readdir(dir);
      if (rest.length > 0) break;
      await fs.rmdir(dir);
    } catch {
      break;
    }
    dir = path.dirname(dir);
  }
  // stopAt 자체도 비었으면 함께 정리한다
  try {
    if ((await fs.readdir(stop)).length === 0) await fs.rmdir(stop);
  } catch {
    /* 남아있으면 그대로 둔다 */
  }
}
