/**
 * رمز عبور پنل مدیریت با scrypt (کندکننده عمدی در برابر حدس زدن رمز) و نمک تصادفی.
 * قالب ذخیره: scrypt$N$r$p$salt$hash (base64url)
 */
import "server-only";
import { randomBytes, scrypt as scryptCb, timingSafeEqual, type ScryptOptions } from "node:crypto";

const scrypt = (pw: string, salt: Buffer, len: number, opts: ScryptOptions) =>
  new Promise<Buffer>((res, rej) => scryptCb(pw, salt, len, opts, (err, key) => (err ? rej(err) : res(key))));
const N = 16384;
const R = 8;
const P = 1;

export const PASSWORD_RULE = "رمز باید حداقل ۸ کاراکتر و شامل حرف و عدد باشد";
export const isStrongPassword = (pw: string) => pw.length >= 8 && pw.length <= 128 && /\d/.test(pw) && /[^\d\s]/.test(pw);

export async function hashPassword(pw: string) {
  const salt = randomBytes(16);
  const hash = await scrypt(pw.normalize("NFKC"), salt, 64, { N, r: R, p: P, maxmem: 64 * 1024 * 1024 });
  return ["scrypt", N, R, P, salt.toString("base64url"), hash.toString("base64url")].join("$");
}

export async function verifyPassword(pw: string, stored: string | null | undefined) {
  // حتی بدون رمز ذخیره‌شده هم محاسبه انجام می‌شود تا زمان پاسخ، وجود حساب را لو ندهد
  const parts = (stored ?? "").split("$");
  const ok = parts.length === 6 && parts[0] === "scrypt";
  const [n, r, p] = ok ? parts.slice(1, 4).map(Number) : [N, R, P];
  const salt = ok ? Buffer.from(parts[4], "base64url") : randomBytes(16);
  const expected = ok ? Buffer.from(parts[5], "base64url") : randomBytes(64);
  const actual = await scrypt(pw.normalize("NFKC"), salt, expected.length, { N: n, r, p, maxmem: 64 * 1024 * 1024 });
  return ok && timingSafeEqual(actual, expected);
}
