import { MIN_PASSWORD_LENGTH, PASSWORD_REQUIREMENTS } from "./password-policy-rules.js";
import { APIError } from "better-auth/api";
import { randomInt } from "node:crypto";

//#region src/auth/password-policy.ts
function validatePasswordPolicy(password, context) {
	const violations = [];
	for (const req of PASSWORD_REQUIREMENTS) if (!req.test(password)) violations.push({
		rule: req.rule,
		message: req.label
	});
	const lower = password.toLowerCase();
	if (context?.userEmail) {
		const localPart = context.userEmail.split("@")[0]?.toLowerCase();
		if (localPart && localPart.length >= 3 && lower.includes(localPart)) violations.push({
			rule: "email",
			message: "Password must not contain your email address"
		});
	}
	if (context?.userName) {
		const name = context.userName.toLowerCase();
		if (name.length >= 3 && lower.includes(name)) violations.push({
			rule: "name",
			message: "Password must not contain your name"
		});
	}
	if (lower.includes("inkeep")) violations.push({
		rule: "company",
		message: "Password must not contain the company name"
	});
	return violations;
}
function enforcePasswordPolicy(password, context) {
	const violations = validatePasswordPolicy(password, context);
	if (violations.length > 0) throw new APIError("BAD_REQUEST", { message: violations.map((v) => v.message).join("; ") });
}
const PASSWORD_POLICY_PATHS = new Set([
	"/sign-up/email",
	"/reset-password",
	"/change-password"
]);
function isPlainObject(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
function readString(body, key) {
	const value = body[key];
	return typeof value === "string" ? value : void 0;
}
async function checkPasswordPolicy(ctx) {
	if (!PASSWORD_POLICY_PATHS.has(ctx.path)) return;
	if (!isPlainObject(ctx.body)) return;
	const pw = readString(ctx.body, "newPassword") ?? readString(ctx.body, "password");
	if (!pw) return;
	enforcePasswordPolicy(pw, {
		userEmail: readString(ctx.body, "email"),
		userName: readString(ctx.body, "name")
	});
}
/** @deprecated Renamed to `checkPasswordPolicy`. Will be removed in a future major version. */
const passwordPolicyHook = checkPasswordPolicy;
const LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
const UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const DIGITS = "0123456789";
const SPECIALS = "!@#%^&*()_+-[];:,.<>/?~";
const ALL_CHARS = LOWERCASE + UPPERCASE + DIGITS + SPECIALS;
function pick(alphabet) {
	return alphabet[randomInt(alphabet.length)];
}
function generateCompliantPassword(length = MIN_PASSWORD_LENGTH + 4) {
	if (length < MIN_PASSWORD_LENGTH) throw new Error(`Password length must be at least ${MIN_PASSWORD_LENGTH}`);
	for (let attempt = 0; attempt < 10; attempt++) {
		const chars = [
			pick(LOWERCASE),
			pick(UPPERCASE),
			pick(DIGITS),
			pick(SPECIALS)
		];
		while (chars.length < length) chars.push(pick(ALL_CHARS));
		for (let i = chars.length - 1; i > 0; i--) {
			const j = randomInt(i + 1);
			[chars[i], chars[j]] = [chars[j], chars[i]];
		}
		const password = chars.join("");
		if (validatePasswordPolicy(password).length === 0) return password;
	}
	throw new Error("Failed to generate a policy-compliant password");
}

//#endregion
export { MIN_PASSWORD_LENGTH, PASSWORD_REQUIREMENTS, checkPasswordPolicy, enforcePasswordPolicy, generateCompliantPassword, passwordPolicyHook, validatePasswordPolicy };