import { randomBytes, scryptSync, timingSafeEqual } from "node:crypto"

const SCRYPT_KEY_LENGTH = 64
const SCRYPT_COST = 16384
const SCRYPT_BLOCK_SIZE = 8
const SCRYPT_PARALLELIZATION = 1
const HASH_PREFIX = "scrypt"

const ensureSafePassword = (password: string) => {
  if (password.length < 6) {
    throw new Error("La contrasena debe tener al menos 6 caracteres.")
  }

  if (!/[^A-Za-z0-9]/.test(password)) {
    throw new Error("La contrasena debe incluir al menos un simbolo.")
  }
}

export const hashPassword = (password: string) => {
  ensureSafePassword(password)

  const salt = randomBytes(16)
  const derivedKey = scryptSync(password, salt, SCRYPT_KEY_LENGTH, {
    cost: SCRYPT_COST,
    blockSize: SCRYPT_BLOCK_SIZE,
    parallelization: SCRYPT_PARALLELIZATION
  })

  return [HASH_PREFIX, salt.toString("base64url"), Buffer.from(derivedKey).toString("base64url")].join("$")
}

export const verifyPassword = (password: string, hashedPassword: string) => {
  const [prefix, saltEncoded, keyEncoded] = hashedPassword.split("$")

  if (prefix !== HASH_PREFIX || !saltEncoded || !keyEncoded) {
    return false
  }

  const salt = Buffer.from(saltEncoded, "base64url")
  const expectedKey = Buffer.from(keyEncoded, "base64url")
  const derivedKey = scryptSync(password, salt, expectedKey.length, {
    cost: SCRYPT_COST,
    blockSize: SCRYPT_BLOCK_SIZE,
    parallelization: SCRYPT_PARALLELIZATION
  })

  if (derivedKey.length !== expectedKey.length) {
    return false
  }

  return timingSafeEqual(Buffer.from(derivedKey), expectedKey)
}
